MCPcopy Create free account
hub / github.com/UsefulSoftwareCo/executor / makeCloudMcpAgentHandler

Function makeCloudMcpAgentHandler

apps/cloud/src/mcp/agent-handler.ts:143–259  ·  view source on GitHub ↗
()

Source from the content-addressed store, hash-verified

141 });
142
143export const makeCloudMcpAgentHandler = () => {
144 const serveOptions = {
145 binding: "MCP_SESSION",
146 transport: "streamable-http",
147 } as const;
148 // The agents SDK builds an exact-match `URLPattern` from the path handed to
149 // `serve` (see `createStreamingHttpHandler` in `agents/dist/mcp/index.js`) —
150 // a single `/mcp` handler never matches `/mcp/toolkits/<slug>` and falls
151 // through to its own internal 404. A second `serve` mounted on the
152 // parameterized path picks it up (`URLPattern` supports `:slug` segments);
153 // the auth/ownership/props logic above is unchanged and shared, only the
154 // final dispatch target differs.
155 const serve = McpSessionDOSqlite.serve("/mcp", serveOptions);
156 const serveToolkit = McpSessionDOSqlite.serve("/mcp/toolkits/:slug", serveOptions);
157
158 const ALLOWED_METHODS = new Set(["GET", "POST", "DELETE", "OPTIONS"]);
159
160 return async (request: Request, env: Env, ctx: ExecutionContext): Promise<Response> => {
161 if (request.method === "OPTIONS") return corsPreflightResponse();
162 // The old envelope (packages/hosts/mcp/src/envelope.ts) answered anything
163 // outside GET/POST/DELETE/OPTIONS with a JSON-RPC 405; the agents SDK
164 // handler only understands its own transport verbs and falls through to
165 // a bare 404. Reject before authenticating so PUT/PATCH/etc never reach
166 // the session engine.
167 if (!ALLOWED_METHODS.has(request.method)) {
168 return jsonRpcResponse(405, -32001, "Method not allowed");
169 }
170 const sessionId = request.headers.get("mcp-session-id");
171
172 const { auth, outcome } = await runTraced(request, authenticate(request));
173 if (!Predicate.isTagged(outcome, "Authenticated")) {
174 // Destroying a live session on auth grounds requires a POSITIVE
175 // determination that access is genuinely gone — only `Forbidden` carries
176 // that (valid bearer, org absent/revoked). `Unavailable` (transient WorkOS
177 // / JWKS failure) and `Unauthorized` (retry with a fresh token) must leave
178 // the session intact, so the condemn path is gated on `Forbidden` alone.
179 if (Predicate.isTagged(outcome, "Forbidden") && sessionId) {
180 await Effect.runPromise(
181 Effect.ignore(
182 Effect.tryPromise(() =>
183 mcpSessionStub(env.MCP_SESSION, sessionId)._cf_scheduleDestroy(),
184 ),
185 ),
186 );
187 }
188 return renderAuthError(auth, request, outcome);
189 }
190
191 if (!sessionId && request.method === "DELETE") {
192 // Matches the old envelope's contract (@modelcontextprotocol/sdk's
193 // `WebStandardStreamableHTTPServerTransport.handleDeleteRequest`): 200,
194 // not 204 — see e2e/cloud/mcp-protocol.test.ts.
195 return new Response(null, {
196 status: 200,
197 headers: { "access-control-allow-origin": "*" },
198 });
199 }
200

Callers 1

server.tsFile · 0.90

Calls 12

mcpSessionStubFunction · 0.90
wrapMcpSseResponseFunction · 0.90
runTracedFunction · 0.85
resourceFromPathFunction · 0.85
corsPreflightResponseFunction · 0.70
jsonRpcResponseFunction · 0.70
authenticateFunction · 0.70
renderAuthErrorFunction · 0.70
propsForPrincipalFunction · 0.70
getMethod · 0.65
fetchMethod · 0.65

Tested by

no test coverage detected