feat: rename originAdapter and originUrl to origin

pull/715/head
Travis Fischer 2025-06-25 01:55:39 -05:00
rodzic 99d0edb6b6
commit 34d73481d7
47 zmienionych plików z 407 dodań i 293 usunięć

Wyświetl plik

@ -84,11 +84,8 @@ export const deployments = pgTable(
// Tool configs customize the behavior of tools for different pricing plans
toolConfigs: jsonb().$type<ToolConfig[]>().default([]).notNull(),
// Origin API URL
originUrl: text().notNull(),
// Origin API adapter config (openapi, mcp, raw, hosting considerations, etc)
originAdapter: jsonb().$type<OriginAdapter>().notNull(),
// Origin API adapter config (url, openapi/mcp/raw, internal/external hosting, etc)
origin: jsonb().$type<OriginAdapter>().notNull(),
// Array<PricingPlan>
pricingPlans: jsonb().$type<PricingPlanList>().notNull(),
@ -166,7 +163,7 @@ export const deploymentSelectSchema = createSelectSchema(deployments, {
readme: resolvedAgenticProjectConfigSchema.shape.readme,
iconUrl: resolvedAgenticProjectConfigSchema.shape.iconUrl,
sourceUrl: resolvedAgenticProjectConfigSchema.shape.sourceUrl,
originAdapter: resolvedAgenticProjectConfigSchema.shape.originAdapter,
origin: resolvedAgenticProjectConfigSchema.shape.origin,
pricingPlans: resolvedAgenticProjectConfigSchema.shape.pricingPlans,
pricingIntervals: resolvedAgenticProjectConfigSchema.shape.pricingIntervals,
tools: resolvedAgenticProjectConfigSchema.shape.tools,
@ -174,7 +171,7 @@ export const deploymentSelectSchema = createSelectSchema(deployments, {
defaultRateLimit: resolvedAgenticProjectConfigSchema.shape.defaultRateLimit
})
.omit({
originUrl: true
origin: true
})
.extend({
// user: z
@ -221,7 +218,7 @@ Deployments are private to a developer or team until they are published, at whic
export const deploymentAdminSelectSchema = deploymentSelectSchema
.extend({
originUrl: resolvedAgenticProjectConfigSchema.shape.originUrl,
origin: resolvedAgenticProjectConfigSchema.shape.origin,
_secret: z.string().nonempty()
})
.openapi('AdminDeployment')

Wyświetl plik

@ -22,9 +22,9 @@ export async function createHttpRequestForOpenAPIOperation({
}): Promise<Request> {
assert(toolCallArgs, 500, 'Tool args are required')
assert(
deployment.originAdapter.type === 'openapi',
deployment.origin.type === 'openapi',
500,
`Internal logic error for origin adapter type "${deployment.originAdapter.type}"`
`Internal logic error for origin adapter type "${deployment.origin.type}"`
)
const { method } = operation
@ -154,7 +154,7 @@ export async function createHttpRequestForOpenAPIOperation({
}
const queryString = query.toString()
const originRequestUrl = `${deployment.originUrl}${path}${
const originRequestUrl = `${deployment.origin.url}${path}${
queryString ? `?${queryString}` : ''
}`

Wyświetl plik

@ -17,9 +17,9 @@ export async function createHttpResponseFromMcpToolCallResponse(
}
): Promise<Response> {
assert(
deployment.originAdapter.type === 'mcp',
deployment.origin.type === 'mcp',
500,
`Internal logic error for origin adapter type "${deployment.originAdapter.type}"`
`Internal logic error for origin adapter type "${deployment.origin.type}"`
)
assert(
!toolCallResponse.isError,

Wyświetl plik

@ -16,9 +16,9 @@ export async function getToolArgsFromRequest(
): Promise<Record<string, any>> {
const request = ctx.req.raw
assert(
deployment.originAdapter.type !== 'raw',
deployment.origin.type !== 'raw',
500,
`Internal logic error for origin adapter type "${deployment.originAdapter.type}"`
`Internal logic error for origin adapter type "${deployment.origin.type}"`
)
if (request.method === 'GET') {

Wyświetl plik

@ -1,4 +1,4 @@
import type { Deployment, Tool } from '@agentic/platform-types'
import type { AdminDeployment, Tool } from '@agentic/platform-types'
import { assert } from '@agentic/platform-core'
export function getTool({
@ -8,7 +8,7 @@ export function getTool({
strict = false
}: {
toolName: string
deployment: Deployment
deployment: AdminDeployment
method?: string
strict?: boolean
}): Tool {
@ -17,7 +17,7 @@ export function getTool({
let tool = deployment.tools.find((tool) => tool.name === toolName)
if (!tool && !strict) {
if (deployment.originAdapter.type === 'openapi') {
if (deployment.origin.type === 'openapi') {
// Check if the tool name is an operation ID since it's easy to forget
// and mistake the two (`getPost` vs `get_post`).
// TODO: In the future, we should be consistent about how we handle tool
@ -25,7 +25,7 @@ export function getTool({
// alternates for operationIds? We should also make sure alternates are
// uniquely defined.
const operationToolName = Object.entries(
deployment.originAdapter.toolToOperationMap
deployment.origin.toolToOperationMap
).find(([_, operation]) => {
if (operation.operationId === toolName) {
return true
@ -52,8 +52,8 @@ export function getTool({
`Tool not found "${toolName}" for deployment "${deployment.identifier}"`
)
if (deployment.originAdapter.type === 'openapi') {
const operation = deployment.originAdapter.toolToOperationMap[tool.name]
if (deployment.origin.type === 'openapi') {
const operation = deployment.origin.toolToOperationMap[tool.name]
assert(
operation,
404,

Wyświetl plik

@ -57,7 +57,7 @@ export async function resolveOriginToolCall({
// it's not specific to tool calls. eg, other MCP requests may still need to
// be rate-limited / cached / tracked / etc.
const { originAdapter } = deployment
const { origin } = deployment
// TODO: make this configurable via `ToolConfig.cost`
const numRequestsCost = 1
let rateLimit = deployment.defaultRateLimit
@ -180,7 +180,7 @@ export async function resolveOriginToolCall({
}
}
if (originAdapter.type === 'raw') {
if (origin.type === 'raw') {
// TODO
assert(false, 500, 'Raw origin adapter not implemented')
} else {
@ -195,8 +195,8 @@ export async function resolveOriginToolCall({
const originStartTimeMs = Date.now()
if (originAdapter.type === 'openapi') {
const operation = originAdapter.toolToOperationMap[tool.name]
if (origin.type === 'openapi') {
const operation = origin.toolToOperationMap[tool.name]
assert(operation, 404, `Tool "${tool.name}" not found in OpenAPI spec`)
assert(toolCallArgs, 500)
@ -246,7 +246,7 @@ export async function resolveOriginToolCall({
numRequestsCost,
toolConfig
}
} else if (originAdapter.type === 'mcp') {
} else if (origin.type === 'mcp') {
const { projectIdentifier } = parseDeploymentIdentifier(
deployment.identifier,
{ errorStatusCode: 500 }
@ -258,9 +258,9 @@ export async function resolveOriginToolCall({
) as DurableObjectStub<DurableMcpClientBase>
await originMcpClient.init({
url: deployment.originUrl,
name: originAdapter.serverInfo.name,
version: originAdapter.serverInfo.version
url: deployment.origin.url,
name: origin.serverInfo.name,
version: origin.serverInfo.version
})
const originMcpRequestMetadata = {
@ -286,7 +286,7 @@ export async function resolveOriginToolCall({
let cacheKey: Request | undefined
if (cacheControl && isCacheControlPubliclyCacheable(cacheControl)) {
const fakeOriginRequest = new Request(deployment.originUrl, {
const fakeOriginRequest = new Request(deployment.origin.url, {
method: 'POST',
headers: {
'content-type': 'application/json',
@ -353,7 +353,7 @@ export async function resolveOriginToolCall({
assert(
false,
500,
`Internal error: origin adapter type "${(originAdapter as any).type}"`
`Internal error: origin adapter type "${(origin as any).type}"`
)
}
}

Wyświetl plik

@ -121,9 +121,9 @@ import { defineConfig } from '@agentic/platform'
export default defineConfig({
name: 'mcp-example',
// Your origin MCP server URL supporting the streamable HTTP transport
originUrl: '<YOUR_MCP_SERVER_URL>',
originAdapter: {
origin: {
// Your origin MCP server URL supporting the streamable HTTP transport
url: '<YOUR_MCP_SERVER_URL>',
type: 'mcp'
}
})`.trim(),
@ -136,9 +136,11 @@ import { defineConfig } from '@agentic/platform'
export default defineConfig({
name: 'openapi-example',
originUrl: '<YOUR_OPENAPI_SERVER_BASE_URL>',
originAdapter: {
origin: {
type: 'openapi',
// Your origin OpenAPI server base URL
url: '<YOUR_OPENAPI_SERVER_BASE_URL>',
// Your origin OpenAPI spec path or URL
spec: '<YOUR_OPENAPI_SPEC_PATH_OR_URL>'
}
})`.trim(),
@ -151,9 +153,9 @@ export default defineConfig({
code: `
{
"name": "mcp-example",
"originUrl": "<YOUR_MCP_SERVER_URL>",
"originAdapter": {
"type": "mcp"
"origin": {
"type": "mcp",
"url": "<YOUR_MCP_SERVER_URL>"
}
}`.trim(),
lang: 'json'
@ -163,9 +165,9 @@ export default defineConfig({
code: `
{
"name": "openapi-example",
"originUrl": "<YOUR_OPENAPI_SERVER_BASE_URL>",
"originAdapter": {
"origin": {
"type": "openapi",
"url": "<YOUR_OPENAPI_SERVER_BASE_URL>",
"spec": "<YOUR_OPENAPI_SPEC_PATH_OR_URL>"
}
}`.trim(),

Wyświetl plik

@ -4,9 +4,9 @@ import { defineConfig } from '@agentic/platform'
export default defineConfig({
name: 'search',
originUrl: process.env.MCP_ORIGIN_URL!,
originAdapter: {
type: 'mcp'
origin: {
type: 'mcp',
url: process.env.MCP_ORIGIN_URL!
},
toolConfigs: [
{

Wyświetl plik

@ -11,10 +11,10 @@ export function pruneDeployment(
d.readme = '<omitted>'
}
if (d.originAdapter?.type === 'openapi') {
d.originAdapter.spec = '<omitted>'
d.originAdapter.toolToOperationMap = '<omitted>' as any
}
// if (d.origin?.type === 'openapi') {
// d.origin.spec = '<omitted>'
// d.origin.toolToOperationMap = '<omitted>' as any
// }
return d
}

Wyświetl plik

@ -2,5 +2,8 @@ import { defineConfig } from '@agentic/platform'
export default defineConfig({
name: 'Test Invalid Name 0',
originUrl: 'https://jsonplaceholder.typicode.com'
origin: {
type: 'raw',
url: 'https://jsonplaceholder.typicode.com'
}
})

Wyświetl plik

@ -2,5 +2,8 @@ import { defineConfig } from '@agentic/platform'
export default defineConfig({
name: 'Test-Invalid-Name-1',
originUrl: 'https://jsonplaceholder.typicode.com'
origin: {
type: 'raw',
url: 'https://jsonplaceholder.typicode.com'
}
})

Wyświetl plik

@ -2,5 +2,8 @@ import { defineConfig } from '@agentic/platform'
export default defineConfig({
name: 'test_invalid_name_2',
originUrl: 'https://jsonplaceholder.typicode.com'
origin: {
type: 'raw',
url: 'https://jsonplaceholder.typicode.com'
}
})

Wyświetl plik

@ -1,5 +1,8 @@
import { defineConfig } from '@agentic/platform'
export default defineConfig({
originUrl: 'https://jsonplaceholder.typicode.com'
origin: {
type: 'raw',
url: 'https://jsonplaceholder.typicode.com'
}
} as any) // invalid; missing name

Wyświetl plik

@ -2,5 +2,8 @@ import { defineConfig } from '@agentic/platform'
export default defineConfig({
name: '@foo/bar', // invalid; name contains invalid characters
originUrl: 'https://jsonplaceholder.typicode.com'
origin: {
type: 'raw',
url: 'https://jsonplaceholder.typicode.com'
}
})

Wyświetl plik

@ -2,5 +2,8 @@ import { defineConfig } from '@agentic/platform'
export default defineConfig({
name: 'test-invalid-origin-url-0',
originUrl: 'http://jsonplaceholder.typicode.com' // invalid http url (missing https)
origin: {
type: 'raw',
url: 'http://jsonplaceholder.typicode.com' // invalid http url (missing https)
}
})

Wyświetl plik

@ -2,5 +2,8 @@ import { defineConfig } from '@agentic/platform'
export default defineConfig({
name: 'test-invalid-origin-url-1',
originUrl: 'https://' // invalid https url
origin: {
type: 'raw',
url: 'https://' // invalid https url
}
})

Wyświetl plik

@ -2,5 +2,8 @@ import { defineConfig } from '@agentic/platform'
export default defineConfig({
name: 'test-invalid-origin-url-3',
originUrl: '' // invalid https url
origin: {
type: 'raw',
url: '' // invalid https url
}
})

Wyświetl plik

@ -2,7 +2,10 @@ import { defineConfig } from '@agentic/platform'
export default defineConfig({
name: 'test-pricing-base-inconsistent',
originUrl: 'https://httpbin.org',
origin: {
type: 'raw',
url: 'https://httpbin.org'
},
pricingPlans: [
{
name: 'Free',

Wyświetl plik

@ -2,7 +2,10 @@ import { defineConfig } from '@agentic/platform'
export default defineConfig({
name: 'test-pricing-custom-inconsistent',
originUrl: 'https://httpbin.org',
origin: {
type: 'raw',
url: 'https://httpbin.org'
},
pricingPlans: [
{
name: 'Free',

Wyświetl plik

@ -2,7 +2,10 @@ import { defineConfig } from '@agentic/platform'
export default defineConfig({
name: 'test-pricing-duplicate-0',
originUrl: 'https://httpbin.org',
origin: {
type: 'raw',
url: 'https://httpbin.org'
},
pricingIntervals: ['month', 'year'],
pricingPlans: [
{

Wyświetl plik

@ -2,7 +2,10 @@ import { defineConfig } from '@agentic/platform'
export default defineConfig({
name: 'test-pricing-duplicate-1',
originUrl: 'https://httpbin.org',
origin: {
type: 'raw',
url: 'https://httpbin.org'
},
pricingPlans: [
{
name: 'Basic',

Wyświetl plik

@ -2,7 +2,10 @@ import { defineConfig } from '@agentic/platform'
export default defineConfig({
name: 'test-pricing-empty-0',
originUrl: 'https://httpbin.org',
origin: {
type: 'raw',
url: 'https://httpbin.org'
},
pricingIntervals: [] as any, // this is invalid
pricingPlans: [
{

Wyświetl plik

@ -2,7 +2,10 @@ import { defineConfig } from '@agentic/platform'
export default defineConfig({
name: 'test-pricing-empty-1',
originUrl: 'https://httpbin.org',
origin: {
type: 'raw',
url: 'https://httpbin.org'
},
pricingPlans: [
{
name: 'Basic',

Wyświetl plik

@ -2,6 +2,9 @@ import { defineConfig } from '@agentic/platform'
export default defineConfig({
name: 'test-pricing-empty-2',
originUrl: 'https://httpbin.org',
origin: {
type: 'raw',
url: 'https://httpbin.org'
},
pricingPlans: [] as any // this is invalid
})

Wyświetl plik

@ -2,9 +2,8 @@ import { defineConfig } from '@agentic/platform'
export default defineConfig({
name: 'test-basic-mcp',
// originUrl: 'http://localhost:8080/stream',
originUrl: 'https://agentic-basic-mcp-test.onrender.com/mcp',
originAdapter: {
type: 'mcp'
origin: {
type: 'mcp',
url: 'https://agentic-basic-mcp-test.onrender.com/mcp'
}
})

Wyświetl plik

@ -2,9 +2,9 @@ import { defineConfig } from '@agentic/platform'
export default defineConfig({
name: 'test-basic-openapi',
originUrl: 'https://jsonplaceholder.typicode.com',
originAdapter: {
origin: {
type: 'openapi',
url: 'https://jsonplaceholder.typicode.com',
spec: './jsonplaceholder.json'
},
toolConfigs: [

Wyświetl plik

@ -1,4 +1,7 @@
{
"name": "test-basic-raw-free-json",
"originUrl": "https://jsonplaceholder.typicode.com"
"origin": {
"type": "raw",
"url": "https://jsonplaceholder.typicode.com"
}
}

Wyświetl plik

@ -2,5 +2,8 @@ import { defineConfig } from '@agentic/platform'
export default defineConfig({
name: 'test-basic-raw-free-ts',
originUrl: 'https://jsonplaceholder.typicode.com'
origin: {
type: 'raw',
url: 'https://jsonplaceholder.typicode.com'
}
})

Wyświetl plik

@ -2,9 +2,9 @@ import { defineConfig } from '@agentic/platform'
export default defineConfig({
name: 'test-everything-openapi',
originUrl: 'https://agentic-platform-fixtures-everything.onrender.com',
originAdapter: {
origin: {
type: 'openapi',
url: 'https://agentic-platform-fixtures-everything.onrender.com',
spec: 'https://agentic-platform-fixtures-everything.onrender.com/docs'
},
toolConfigs: [

Wyświetl plik

@ -2,7 +2,10 @@ import { defineConfig } from '@agentic/platform'
export default defineConfig({
name: 'test-pricing-3-plans',
originUrl: 'https://httpbin.org',
origin: {
type: 'raw',
url: 'https://httpbin.org'
},
pricingPlans: [
{
name: 'Free',

Wyświetl plik

@ -2,7 +2,10 @@ import { defineConfig } from '@agentic/platform'
export default defineConfig({
name: 'test-pricing-custom-0',
originUrl: 'https://httpbin.org',
origin: {
type: 'raw',
url: 'https://httpbin.org'
},
pricingIntervals: ['month', 'year'],
pricingPlans: [
{

Wyświetl plik

@ -3,7 +3,10 @@ import { defaultFreePricingPlan, defineConfig } from '@agentic/platform'
export default defineConfig({
// TODO: resolve name / slug conflicts
name: 'test-pricing-freemium',
originUrl: 'https://httpbin.org',
origin: {
type: 'raw',
url: 'https://httpbin.org'
},
pricingPlans: [
defaultFreePricingPlan,
{

Wyświetl plik

@ -2,7 +2,10 @@ import { defineConfig } from '@agentic/platform'
export default defineConfig({
name: 'test-pricing-monthly-annual',
originUrl: 'https://httpbin.org',
origin: {
type: 'raw',
url: 'https://httpbin.org'
},
pricingIntervals: ['month', 'year'],
pricingPlans: [
{

Wyświetl plik

@ -2,7 +2,10 @@ import { defineConfig } from '@agentic/platform'
export default defineConfig({
name: 'test-pricing-pay-as-you-go',
originUrl: 'https://httpbin.org',
origin: {
type: 'raw',
url: 'https://httpbin.org'
},
pricingPlans: [
{
name: 'Free',

File diff suppressed because one or more lines are too long

Wyświetl plik

@ -10,25 +10,23 @@ import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/
export async function resolveMCPOriginAdapter({
name,
version,
originUrl,
originAdapter,
origin,
label
}: {
name: string
originUrl: string
originAdapter: MCPOriginAdapterConfig
origin: MCPOriginAdapterConfig
label: string
version: string
}): Promise<{
originAdapter: MCPOriginAdapter
origin: MCPOriginAdapter
tools?: Tool[]
}> {
assert(
originAdapter.type === 'mcp',
origin.type === 'mcp',
400,
`Invalid origin adapter type "${originAdapter.type}" for ${label}`
`Invalid origin adapter type "${origin.type}" for ${label}`
)
const transport = new StreamableHTTPClientTransport(new URL(originUrl))
const transport = new StreamableHTTPClientTransport(new URL(origin.url))
const client = new McpClient({ name, version })
await client.connect(transport)
@ -47,8 +45,8 @@ export async function resolveMCPOriginAdapter({
return {
tools,
originAdapter: {
...originAdapter,
origin: {
...origin,
serverInfo
}
}

Wyświetl plik

@ -10,38 +10,38 @@ import {
} from '@agentic/platform-openapi-utils'
export async function resolveOpenAPIOriginAdapter({
originAdapter,
origin,
label,
cwd,
logger
}: {
originAdapter: OpenAPIOriginAdapterConfig
origin: OpenAPIOriginAdapterConfig
label: string
cwd?: string
logger?: Logger
}): Promise<{
originAdapter: OpenAPIOriginAdapter
origin: OpenAPIOriginAdapter
tools?: Tool[]
}> {
assert(
originAdapter.type === 'openapi',
origin.type === 'openapi',
400,
`Invalid origin adapter type "${originAdapter.type}" for ${label}`
`Invalid origin adapter type "${origin.type}" for ${label}`
)
assert(
originAdapter.spec,
origin.spec,
400,
`OpenAPI spec is required for ${label} with origin adapter type set to "openapi"`
)
// Validate and normalize the OpenAPI spec
const openapiSpec = await validateOpenAPISpec(originAdapter.spec, {
const openapiSpec = await validateOpenAPISpec(origin.spec, {
cwd,
logger
})
// Remove origin servers from the OpenAPI spec.
// TODO: Ensure that `originUrl` matches any origin servers in the openapi spec?
// TODO: Ensure that `origin.url` matches any origin servers in the openapi spec?
delete openapiSpec.servers
// TODO: Additional, agentic-specific validation of the OpenAPI spec's
@ -52,13 +52,10 @@ export async function resolveOpenAPIOriginAdapter({
// TODO: Extract tool definitions from OpenAPI operationIds
const dereferencedOpenAPISpec = await validateOpenAPISpec(
originAdapter.spec,
{
cwd,
dereference: true
}
)
const dereferencedOpenAPISpec = await validateOpenAPISpec(origin.spec, {
cwd,
dereference: true
})
const { tools, toolToOperationMap } = await getToolsFromOpenAPISpec(
dereferencedOpenAPISpec
@ -66,8 +63,8 @@ export async function resolveOpenAPIOriginAdapter({
return {
tools,
originAdapter: {
...originAdapter,
origin: {
...origin,
// Update the openapi spec with the normalized version
spec: JSON.stringify(openapiSpec),
toolToOperationMap

Wyświetl plik

@ -23,27 +23,26 @@ export async function resolveAgenticProjectConfig(
const { name, version } = resolveMetadata(config)
validatePricing(config)
const { originAdapter, tools } = await resolveOriginAdapter({
const { origin, tools } = await resolveOriginAdapter({
name,
version,
label: `project "${name}"`,
...opts,
originUrl: config.originUrl,
originAdapter: config.originAdapter
origin: config.origin
})
const resolvedConfig = parseResolvedAgenticProjectConfig({
...config,
name,
version,
originAdapter,
origin,
tools
})
validateTools({
label: `project "${name}"`,
...opts,
originAdapter: resolvedConfig.originAdapter,
origin: resolvedConfig.origin,
tools: resolvedConfig.tools,
toolConfigs: resolvedConfig.toolConfigs
})

Wyświetl plik

@ -15,49 +15,46 @@ import { validateOriginUrl } from './validate-origin-url'
export async function resolveOriginAdapter({
name,
version = '0.0.0',
originUrl,
originAdapter,
origin,
label,
cwd,
logger
}: {
name: string
originUrl: string
originAdapter: OriginAdapterConfig
origin: OriginAdapterConfig
label: string
version?: string
cwd?: string
logger?: Logger
}): Promise<{
originAdapter: OriginAdapter
origin: OriginAdapter
tools?: Tool[]
}> {
validateOriginUrl({ originUrl, label })
validateOriginUrl({ originUrl: origin.url, label })
if (originAdapter.type === 'openapi') {
if (origin.type === 'openapi') {
return resolveOpenAPIOriginAdapter({
originAdapter,
origin,
label,
cwd,
logger
})
} else if (originAdapter.type === 'mcp') {
} else if (origin.type === 'mcp') {
return resolveMCPOriginAdapter({
name,
version,
originUrl,
originAdapter,
origin,
label
})
} else {
assert(
originAdapter.type === 'raw',
origin.type === 'raw',
400,
`Invalid origin adapter type "${originAdapter.type}" for ${label}`
`Invalid origin adapter type "${origin.type}" for ${label}`
)
return {
originAdapter
origin
}
}
}

Wyświetl plik

@ -21,13 +21,12 @@ export async function validateAgenticProjectConfig(
const { name, version } = resolveMetadata(config)
validatePricing(config)
const originAdapter = await validateOriginAdapter({
const origin = await validateOriginAdapter({
name,
version,
label: `project "${name}"`,
...opts,
originUrl: config.originUrl,
originAdapter: config.originAdapter
origin: config.origin
})
return parseAgenticProjectConfig(
@ -35,7 +34,7 @@ export async function validateAgenticProjectConfig(
...config,
name,
version,
originAdapter
origin
},
{ strip, strict: !strip }
)

Wyświetl plik

@ -11,58 +11,56 @@ import { validateOriginUrl } from './validate-origin-url'
export async function validateOriginAdapter({
name,
version = '0.0.0',
originUrl,
originAdapter,
origin,
label,
cwd,
logger
}: {
name: string
originUrl: string
originAdapter: Readonly<OriginAdapterConfig>
origin: Readonly<OriginAdapterConfig>
label: string
version?: string
cwd?: string
logger?: Logger
}): Promise<OriginAdapterConfig> {
validateOriginUrl({ originUrl, label })
validateOriginUrl({ originUrl: origin.url, label })
if (originAdapter.type === 'openapi') {
if (origin.type === 'openapi') {
// We intentionally ignore the resolved tools here because the server will
// need to re-validate the OpenAPI spec and tools anyway. We do, however,
// override the `spec` field with the parsed, normalized version because
// that may have been pointing to a local file or remote URL.
const { originAdapter: resolvedOriginAdapter } =
await resolveOpenAPIOriginAdapter({
originAdapter,
const { origin: resolvedOriginAdapter } = await resolveOpenAPIOriginAdapter(
{
origin,
label,
cwd,
logger
})
}
)
return {
...originAdapter,
...origin,
spec: resolvedOriginAdapter.spec
}
} else if (originAdapter.type === 'mcp') {
} else if (origin.type === 'mcp') {
// We intentionally ignore the resolved version and tools here because the
// server will need to re-validate the MCP server info and tools anyway.
await resolveMCPOriginAdapter({
name,
version,
originUrl,
originAdapter,
origin,
label
})
return originAdapter
return origin
} else {
assert(
originAdapter.type === 'raw',
origin.type === 'raw',
400,
`Invalid origin adapter type "${originAdapter.type}" for ${label}`
`Invalid origin adapter type "${origin.type}" for ${label}`
)
return originAdapter
return origin
}
}

Wyświetl plik

@ -5,20 +5,20 @@ import { assert } from '@agentic/platform-core'
* Validates the origin server's tools for a project.
*/
export function validateTools({
originAdapter,
origin,
tools,
toolConfigs,
label
}: {
originAdapter: OriginAdapter
origin: OriginAdapter
tools: Tool[]
toolConfigs: ToolConfig[]
label: string
}) {
if (!tools.length) {
assert(
originAdapter.type === 'raw',
`No tools defined for ${label} with origin adapter type "${originAdapter.type}"`
origin.type === 'raw',
`No tools defined for ${label} with origin adapter type "${origin.type}"`
)
}

Wyświetl plik

@ -5,12 +5,20 @@ import type { AgenticProjectConfigInput } from './agentic-project-config'
test('AgenticProjectConfig input types', () => {
expectTypeOf<{
name: 'test'
originUrl: 'https://httpbin.org'
origin: {
type: 'openapi'
url: 'https://httpbin.org'
spec: './openapi.json'
}
}>().toExtend<AgenticProjectConfigInput>()
expectTypeOf<{
name: 'test'
originUrl: 'https://httpbin.org'
origin: {
type: 'openapi'
url: 'https://httpbin.org'
spec: './openapi.json'
}
pricingPlans: [
{
name: 'Free'
@ -28,7 +36,11 @@ test('AgenticProjectConfig input types', () => {
expectTypeOf<{
name: 'test'
originUrl: 'https://httpbin.org'
origin: {
type: 'openapi'
url: 'https://httpbin.org'
spec: './openapi.json'
}
pricingPlans: [
{
name: 'Basic Monthly'
@ -52,7 +64,11 @@ test('AgenticProjectConfig input types', () => {
expectTypeOf<{
name: 'test'
originUrl: 'https://httpbin.org'
origin: {
type: 'openapi'
url: 'https://httpbin.org'
spec: './openapi.json'
}
pricingPlans: [
{
name: 'Basic Monthly'
@ -76,14 +92,22 @@ test('AgenticProjectConfig input types', () => {
expectTypeOf<{
name: 'test'
originUrl: 'https://httpbin.org'
origin: {
type: 'openapi'
url: 'https://httpbin.org'
spec: './openapi.json'
}
// Invalid because `pricingPlans` must be non-empty if defined
pricingPlans: []
}>().not.toExtend<AgenticProjectConfigInput>()
expectTypeOf<{
name: 'test'
originUrl: 'https://httpbin.org'
origin: {
type: 'openapi'
url: 'https://httpbin.org'
spec: './openapi.json'
}
pricingPlans: [
{
name: 'Basic Monthly'

Wyświetl plik

@ -89,24 +89,21 @@ export const agenticProjectConfigSchema = z
)
.optional(),
/** Required origin API HTTPS base URL */
originUrl: z.string().url()
.describe(`Required base URL of the externally hosted origin API server. Must be a valid \`https\` URL.
NOTE: Agentic currently only supports \`external\` API servers. If you'd like to host your API or MCP server on Agentic's infrastructure, please reach out to support@agentic.so.`),
/**
* Optional origin API adapter used to configure the origin API server
* downstream from Agentic's API gateway. It specifies whether the origin
* API server denoted by \`originUrl\` is hosted externally or deployed
* internally to Agentic's infrastructure. It also specifies the format
* for how origin tools / services are defined: either as an OpenAPI spec,
* an MCP server, or as a raw HTTP REST API.
* Origin API adapter used to configure the origin API server downstream
* from Agentic's API gateway. It specifies whether the origin API server's
* is hosted externally or deployed internally to Agentic's infrastructure.
* If hosted externally, the origin `url` must be a valid \`https\` URL
* pointing to the remote origin server.
*
* It also specifies the format for how origin tools are defined: either as
* an OpenAPI spec or an MCP server.
*
* @note Currently, only external origin servers are supported. If you'd like
* to host your API or MCP server on Agentic's infrastructure, please reach
* out to support@agentic.so.
*/
originAdapter: originAdapterConfigSchema.optional().default({
location: 'external',
type: 'raw'
}),
origin: originAdapterConfigSchema,
/** Optional subscription pricing config for this project. */
pricingPlans: pricingPlanListSchema
@ -206,7 +203,7 @@ export type AgenticProjectConfig = Simplify<
export const resolvedAgenticProjectConfigSchema =
agenticProjectConfigSchema.extend({
originAdapter: originAdapterSchema,
origin: originAdapterSchema,
tools: z.array(toolSchema).default([])
})
export type ResolvedAgenticProjectConfig = Simplify<

Wyświetl plik

@ -571,9 +571,10 @@ export interface components {
username: string;
/** @enum {string} */
role: "user" | "admin";
name?: string;
email: string;
isEmailVerified: boolean;
name?: string;
bio?: string;
image?: string;
stripeCustomerId?: string;
};
@ -682,70 +683,6 @@ export interface components {
[key: string]: components["schemas"]["PricingPlanToolOverride"];
};
};
/** @description Origin adapter is used to configure the origin API server downstream from Agentic's API gateway. It specifies whether the origin API server denoted by `originUrl` is hosted externally or deployed internally to Agentic's infrastructure. It also specifies the format for how origin tools are defined: either an OpenAPI spec, an MCP server, or a raw HTTP REST API. */
OriginAdapter: {
/**
* @default external
* @enum {string}
*/
location: "external";
/** @enum {string} */
type: "openapi";
/** @description JSON stringified OpenAPI spec describing the origin API server. */
spec: string;
/** @description Mapping from tool name to OpenAPI Operation info. This is used by the Agentic API gateway to route tools to the correct origin API operation, along with the HTTP method, path, params, etc. */
toolToOperationMap: {
[key: string]: {
/** @description OpenAPI operationId for the tool */
operationId: string;
/** @description HTTP method */
method: "get" | "put" | "post" | "delete" | "patch" | "trace";
/** @description HTTP path template */
path: string;
/** @description Mapping from parameter name to HTTP source (query, path, JSON body, etc). */
parameterSources: {
[key: string]: "query" | "header" | "path" | "cookie" | "body" | "formData";
};
tags?: string[];
};
};
} | {
/**
* @default external
* @enum {string}
*/
location: "external";
/** @enum {string} */
type: "mcp";
serverInfo: {
name: string;
version: string;
capabilities?: {
experimental?: Record<string, never>;
logging?: Record<string, never>;
completions?: Record<string, never>;
prompts?: {
listChanged?: boolean;
};
resources?: {
subscribe?: boolean;
listChanged?: boolean;
};
tools?: {
listChanged?: boolean;
};
};
instructions?: string;
};
} | {
/**
* @default external
* @enum {string}
*/
location: "external";
/** @enum {string} */
type: "raw";
};
/**
* @description Human-readable name for the pricing plan (eg, "Free", "Starter Monthly", "Pro Annual", etc)
* @example Starter Monthly
@ -838,7 +775,6 @@ export interface components {
tools: components["schemas"]["Tool"][];
/** @default [] */
toolConfigs: components["schemas"]["ToolConfig"][];
originAdapter: components["schemas"]["OriginAdapter"];
/**
* @description List of PricingPlans configuring which Stripe subscriptions should be available for the project. Defaults to a single free plan which is useful for developing and testing your project.
* @default [
@ -917,21 +853,22 @@ export interface components {
project?: components["schemas"]["Project"];
deployment?: unknown;
};
/**
* @description Origin adapter is used to configure the origin API server downstream from Agentic's API gateway. It specifies whether the origin API server denoted by `originUrl` is hosted externally or deployed internally to Agentic's infrastructure. It also specifies the format for how origin tools are defined: either an OpenAPI spec, an MCP server, or a raw HTTP REST API.
/** @description Origin adapter is used to configure the origin API server downstream from Agentic's API gateway. It specifies whether the origin API server denoted by `url` is hosted externally or deployed internally to Agentic's infrastructure. It also specifies the format for how origin tools are defined: either an OpenAPI spec or an MCP server.
*
* NOTE: Agentic currently only supports `external` API servers. If you'd like to host your API or MCP server on Agentic's infrastructure, please reach out to support@agentic.so.
* @default {
* "location": "external",
* "type": "raw"
* }
*/
* NOTE: Agentic currently only supports `external` API servers. If you'd like to host your API or MCP server on Agentic's infrastructure, please reach out to support@agentic.so. */
OriginAdapterConfig: {
/**
* @default external
* @enum {string}
*/
location: "external";
/**
* Format: uri
* @description Required base URL of the externally hosted origin API server. Must be a valid `https` URL.
*
* NOTE: Agentic currently only supports `external` API servers. If you'd like to host your API or MCP server on Agentic's infrastructure, please reach out to support@agentic.so.
*/
url: string;
/** @enum {string} */
type: "openapi";
/** @description Local file path, URL, or JSON stringified OpenAPI spec describing the origin API server. */
@ -942,6 +879,13 @@ export interface components {
* @enum {string}
*/
location: "external";
/**
* Format: uri
* @description Required base URL of the externally hosted origin API server. Must be a valid `https` URL.
*
* NOTE: Agentic currently only supports `external` API servers. If you'd like to host your API or MCP server on Agentic's infrastructure, please reach out to support@agentic.so.
*/
url: string;
/** @enum {string} */
type: "mcp";
} | {
@ -950,6 +894,13 @@ export interface components {
* @enum {string}
*/
location: "external";
/**
* Format: uri
* @description Required base URL of the externally hosted origin API server. Must be a valid `https` URL.
*
* NOTE: Agentic currently only supports `external` API servers. If you'd like to host your API or MCP server on Agentic's infrastructure, please reach out to support@agentic.so.
*/
url: string;
/** @enum {string} */
type: "raw";
};
@ -963,17 +914,96 @@ export interface components {
AdminConsumer: components["schemas"]["Consumer"] & {
_stripeCustomerId: string;
};
/** @description A Deployment is a single, immutable instance of a Project. Each deployment contains pricing plans, origin server config (OpenAPI or MCP server), tool definitions, and metadata.
*
* Deployments are private to a developer or team until they are published, at which point they are accessible to any customers with access to the parent Project. */
AdminDeployment: components["schemas"]["Deployment"] & {
/** @description Origin adapter is used to configure the origin API server downstream from Agentic's API gateway. It specifies whether the origin API server denoted by `url` is hosted externally or deployed internally to Agentic's infrastructure. It also specifies the format for how origin tools are defined: either an OpenAPI spec, an MCP server, or a raw HTTP REST API. */
OriginAdapter: {
/**
* @default external
* @enum {string}
*/
location: "external";
/**
* Format: uri
* @description Required base URL of the externally hosted origin API server. Must be a valid `https` URL.
*
* NOTE: Agentic currently only supports `external` API servers. If you'd like to host your API or MCP server on Agentic's infrastructure, please reach out to support@agentic.so.
*/
originUrl: string;
url: string;
/** @enum {string} */
type: "openapi";
/** @description JSON stringified OpenAPI spec describing the origin API server. */
spec: string;
/** @description Mapping from tool name to OpenAPI Operation info. This is used by the Agentic API gateway to route tools to the correct origin API operation, along with the HTTP method, path, params, etc. */
toolToOperationMap: {
[key: string]: {
/** @description OpenAPI operationId for the tool */
operationId: string;
/** @description HTTP method */
method: "get" | "put" | "post" | "delete" | "patch" | "trace";
/** @description HTTP path template */
path: string;
/** @description Mapping from parameter name to HTTP source (query, path, JSON body, etc). */
parameterSources: {
[key: string]: "query" | "header" | "path" | "cookie" | "body" | "formData";
};
tags?: string[];
};
};
} | {
/**
* @default external
* @enum {string}
*/
location: "external";
/**
* Format: uri
* @description Required base URL of the externally hosted origin API server. Must be a valid `https` URL.
*
* NOTE: Agentic currently only supports `external` API servers. If you'd like to host your API or MCP server on Agentic's infrastructure, please reach out to support@agentic.so.
*/
url: string;
/** @enum {string} */
type: "mcp";
serverInfo: {
name: string;
version: string;
capabilities?: {
experimental?: Record<string, never>;
logging?: Record<string, never>;
completions?: Record<string, never>;
prompts?: {
listChanged?: boolean;
};
resources?: {
subscribe?: boolean;
listChanged?: boolean;
};
tools?: {
listChanged?: boolean;
};
};
instructions?: string;
};
} | {
/**
* @default external
* @enum {string}
*/
location: "external";
/**
* Format: uri
* @description Required base URL of the externally hosted origin API server. Must be a valid `https` URL.
*
* NOTE: Agentic currently only supports `external` API servers. If you'd like to host your API or MCP server on Agentic's infrastructure, please reach out to support@agentic.so.
*/
url: string;
/** @enum {string} */
type: "raw";
};
/** @description A Deployment is a single, immutable instance of a Project. Each deployment contains pricing plans, origin server config (OpenAPI or MCP server), tool definitions, and metadata.
*
* Deployments are private to a developer or team until they are published, at which point they are accessible to any customers with access to the parent Project. */
AdminDeployment: components["schemas"]["Deployment"] & {
origin: components["schemas"]["OriginAdapter"];
_secret: string;
};
};
@ -1356,6 +1386,7 @@ export interface operations {
content: {
"application/json": {
name?: string;
bio?: string;
image?: string;
};
};
@ -2263,7 +2294,7 @@ export interface operations {
createDeployment: {
parameters: {
query?: {
publish?: boolean;
publish?: "true" | "false";
};
header?: never;
path?: never;
@ -2290,14 +2321,7 @@ export interface operations {
* @description Optional URL to the source code of the project (eg, GitHub repo).
*/
sourceUrl?: string;
/**
* Format: uri
* @description Required base URL of the externally hosted origin API server. Must be a valid `https` URL.
*
* NOTE: Agentic currently only supports `external` API servers. If you'd like to host your API or MCP server on Agentic's infrastructure, please reach out to support@agentic.so.
*/
originUrl: string;
originAdapter?: components["schemas"]["OriginAdapterConfig"];
origin: components["schemas"]["OriginAdapterConfig"];
/**
* @description List of PricingPlans configuring which Stripe subscriptions should be available for the project. Defaults to a single free plan which is useful for developing and testing your project.
* @default [

Wyświetl plik

@ -21,7 +21,13 @@ export type OriginAdapterLocation = z.infer<typeof originAdapterLocationSchema>
// >
export const commonOriginAdapterSchema = z.object({
location: originAdapterLocationSchema.optional().default('external')
location: originAdapterLocationSchema.optional().default('external'),
/** Required origin API HTTPS base URL */
url: z.string().url()
.describe(`Required base URL of the externally hosted origin API server. Must be a valid \`https\` URL.
NOTE: Agentic currently only supports \`external\` API servers. If you'd like to host your API or MCP server on Agentic's infrastructure, please reach out to support@agentic.so.`)
// TODO: Add support for `internal` hosted API servers
// internalType: originAdapterInternalTypeSchema.optional()
@ -70,6 +76,7 @@ export type MCPOriginAdapterConfig = z.infer<
typeof mcpOriginAdapterConfigSchema
>
// TODO: Decide on whether to support `raw` origin adapters or not.
export const rawOriginAdapterConfigSchema = commonOriginAdapterSchema.merge(
z.object({
/**
@ -89,9 +96,9 @@ export type RawOriginAdapterConfig = z.infer<
/**
* Origin adapter is used to configure the origin API server downstream from
* Agentic's API gateway. It specifies whether the origin API server denoted
* by `originUrl` is hosted externally or deployed internally to Agentic's
* by `url` is hosted externally or deployed internally to Agentic's
* infrastructure. It also specifies the format for how origin tools are
* defined: either an OpenAPI spec, an MCP server, or as a raw HTTP REST API.
* defined: either an OpenAPI spec or an MCP server.
*
* NOTE: Agentic currently only supports `external` API servers. If you'd like
* to host your API or MCP server on Agentic's infrastructure, please reach out
@ -106,7 +113,7 @@ export const originAdapterConfigSchema = z
rawOriginAdapterConfigSchema
])
.describe(
`Origin adapter is used to configure the origin API server downstream from Agentic's API gateway. It specifies whether the origin API server denoted by \`originUrl\` is hosted externally or deployed internally to Agentic's infrastructure. It also specifies the format for how origin tools are defined: either an OpenAPI spec, an MCP server, or a raw HTTP REST API.
`Origin adapter is used to configure the origin API server downstream from Agentic's API gateway. It specifies whether the origin API server denoted by \`url\` is hosted externally or deployed internally to Agentic's infrastructure. It also specifies the format for how origin tools are defined: either an OpenAPI spec or an MCP server.
NOTE: Agentic currently only supports \`external\` API servers. If you'd like to host your API or MCP server on Agentic's infrastructure, please reach out to support@agentic.so.`
)
@ -210,6 +217,8 @@ export const rawOriginAdapterSchema = commonOriginAdapterSchema.merge(
*
* In this mode, Agentic's API gateway acts as a simple reverse-proxy
* to the origin server, without validating tools.
*
* @note This mode is currently only for internal testing.
*/
type: z.literal('raw')
})
@ -219,9 +228,15 @@ export type RawOriginAdapter = z.infer<typeof rawOriginAdapterSchema>
/**
* Origin adapter is used to configure the origin API server downstream from
* Agentic's API gateway. It specifies whether the origin API server denoted
* by `originUrl` is hosted externally or deployed internally to Agentic's
* infrastructure. It also specifies the format for how origin tools are
* defined: either an OpenAPI spec, an MCP server, or as a raw HTTP REST API.
* by `url` is hosted externally or deployed internally to Agentic's
* infrastructure.
*
* It also specifies the format for how origin tools are defined: either an
* OpenAPI spec or an MCP server.
*
* @note Currently, only external origin servers are supported. If you'd like
* to host your API or MCP server on Agentic's infrastructure, please reach
* out to support@agentic.so.
*/
export const originAdapterSchema = z
.discriminatedUnion('type', [
@ -232,7 +247,7 @@ export const originAdapterSchema = z
rawOriginAdapterSchema
])
.describe(
`Origin adapter is used to configure the origin API server downstream from Agentic's API gateway. It specifies whether the origin API server denoted by \`originUrl\` is hosted externally or deployed internally to Agentic's infrastructure. It also specifies the format for how origin tools are defined: either an OpenAPI spec, an MCP server, or a raw HTTP REST API.`
`Origin adapter is used to configure the origin API server downstream from Agentic's API gateway. It specifies whether the origin API server denoted by \`url\` is hosted externally or deployed internally to Agentic's infrastructure. It also specifies the format for how origin tools are defined: either an OpenAPI spec, an MCP server, or a raw HTTP REST API.`
)
.openapi('OriginAdapter')
export type OriginAdapter = z.infer<typeof originAdapterSchema>

Wyświetl plik

@ -7,6 +7,7 @@
import type { Simplify } from 'type-fest'
import type { components } from './openapi.d.ts'
import type { OriginAdapter } from './origin-adapter.js'
import type { PricingPlan } from './pricing'
import type { RateLimit } from './rate-limit.js'
import type { ToolConfig } from './tools'
@ -51,11 +52,12 @@ export type Deployment = Simplify<
export type AdminDeployment = Simplify<
Omit<
components['schemas']['AdminDeployment'],
'pricingPlans' | 'toolConfigs' | 'defaultRateLimit'
'pricingPlans' | 'toolConfigs' | 'defaultRateLimit' | 'origin'
> & {
pricingPlans: PricingPlan[]
toolConfigs: ToolConfig[]
defaultRateLimit: RateLimit
origin: OriginAdapter
project?: components['schemas']['Project']
}
>