From 4223db8a9a907045883eda61c749be54cf5b2d5a Mon Sep 17 00:00:00 2001 From: Travis Fischer Date: Sat, 31 May 2025 23:28:09 +0700 Subject: [PATCH] feat: WIP kittens --- apps/gateway/src/lib/get-tool.ts | 2 +- .../gateway/src/lib/resolve-origin-request.ts | 33 +++++++++++-------- apps/gateway/src/worker.ts | 25 ++++---------- readme.md | 4 +-- 4 files changed, 29 insertions(+), 35 deletions(-) diff --git a/apps/gateway/src/lib/get-tool.ts b/apps/gateway/src/lib/get-tool.ts index 10d52851..a9847f4e 100644 --- a/apps/gateway/src/lib/get-tool.ts +++ b/apps/gateway/src/lib/get-tool.ts @@ -24,8 +24,8 @@ export function getTool({ return false }) - assert(tool, 404, `Tool not found "${toolPath}"`) + if (deployment.originAdapter.type === 'openapi') { const operation = deployment.originAdapter.toolToOperationMap[tool.name] assert( diff --git a/apps/gateway/src/lib/resolve-origin-request.ts b/apps/gateway/src/lib/resolve-origin-request.ts index 54871e7b..d2d70e01 100644 --- a/apps/gateway/src/lib/resolve-origin-request.ts +++ b/apps/gateway/src/lib/resolve-origin-request.ts @@ -25,10 +25,10 @@ export async function resolveOriginRequest( const { search, pathname } = requestUrl const method = req.method.toLowerCase() const requestPathParts = pathname.split('/') - const requestPath = - requestPathParts[0] === 'mcp' - ? requestPathParts.slice(1).join('/') - : pathname + const isMCPRequest = requestPathParts[0] === 'mcp' + const requestPath = isMCPRequest + ? requestPathParts.slice(1).join('/') + : pathname const { deployment, toolPath } = await getAdminDeployment(ctx, requestPath) @@ -171,23 +171,30 @@ export async function resolveOriginRequest( }) } - // TODO: Everything from here on depends on the origin adapter type. - // For MCP, we need(?) to use an McpClient and SSEClientTransport? - // For OpenAPI and raw, we need to make an origin HTTP request. - // TODO: what do we want the API gateway's interface to be? // - support both MCP and OpenAPI / raw? + const { originAdapter } = deployment let originRequest: Request | undefined - if ( - deployment.originAdapter.type === 'openapi' || - deployment.originAdapter.type === 'raw' - ) { + + if (originAdapter.type === 'openapi' || originAdapter.type === 'raw') { const originRequestUrl = `${deployment.originUrl}${toolPath}${search}` console.log('originRequestUrl', originRequestUrl) originRequest = new Request(originRequestUrl, req) - updateOriginRequest(originRequest, { consumer, deployment, ip }) + + // TODO: For OpenAPI, we need to convert from POST to the correct operation? + // Or, do we only support a single public MCP interface? + if (originAdapter.type === 'openapi') { + const operation = originAdapter.toolToOperationMap[tool.name] + assert(operation, 404, `Tool "${tool.name}" not found in OpenAPI spec`) + + // req.method = operation.method + } else { + originRequest = new Request(originRequestUrl, req) + } + + updateOriginRequest(originRequest, { consumer, deployment }) } return { diff --git a/apps/gateway/src/worker.ts b/apps/gateway/src/worker.ts index 1eb8e96c..e180bb04 100644 --- a/apps/gateway/src/worker.ts +++ b/apps/gateway/src/worker.ts @@ -62,40 +62,27 @@ export default { const resolvedOriginRequest = await resolveOriginRequest(ctx) try { + originStartTime = Date.now() + switch (resolvedOriginRequest.deployment.originAdapter.type) { case 'openapi': - break + return fetch(resolvedOriginRequest.originRequest!) case 'raw': - break + return fetch(resolvedOriginRequest.originRequest!) case 'mcp': break } - const originReqCacheKey = await getOriginRequestCacheKey(originReq) - originStartTime = Date.now() - - const originRes = await fetchCache({ - event, - cacheKey: originReqCacheKey, - fetch: () => fetchOriginRequest(event, { originReq, call }) - }) - - res = new Response(originRes.body, originRes) recordTimespans() // Record the time it took for both the origin and gateway proxy to respond res.headers.set('x-response-time', `${originTimespan!}ms`) res.headers.set('x-proxy-response-time', `${gatewayTimespan!}ms`) - // Upsert Vary header so browser will cache response correctly - // TODO: why is this necessary according to cloudflare? it's actually incorrect - // in that the responses explicitly do not vary by request origin... - // res.headers.append('vary', 'Origin') - - // Reset server to saasify because Cloudflare likes to override things - res.headers.set('server', 'saasify') + // Reset server to agentic because Cloudflare likes to override things + res.headers.set('server', 'agentic') // const id: DurableObjectId = env.DO_RATE_LIMITER.idFromName('foo') // const stub = env.DO_RATE_LIMITER.get(id) diff --git a/readme.md b/readme.md index d03ee3fe..375728cf 100644 --- a/readme.md +++ b/readme.md @@ -45,11 +45,11 @@ - REST: `POST gateway.agentic.so/deploymentIdentifier/toolName` - => MCP: `MCPClient.callTool` with JSON body parameters - => OpenAPI: `GET/POST/ETC originUrl/toolName` operation with transformed JSON body params - - MCP: `mcp.agentic.so/deploymentIdentifier/sse` MCP server + - MCP: `mcp.agentic.so/deploymentIdentifier/sse` MCP server? - => MCP: `MCPClient.callTool` just proxying tool call - => OpenAPI: `GET/POST/ETC originUrl/toolName` operation with transformed tool params - add support for caching - - add support for custom headers on response + - add support for custom headers on responses - signed requests ## License