From 0baabaa88dd5c3b17d6f3d4f1d8362e5015edad3 Mon Sep 17 00:00:00 2001 From: jonnyparris <6400000+jonnyparris@users.noreply.github.com> Date: Tue, 26 May 2026 13:22:08 +0100 Subject: [PATCH] fix(mcp): use Seal-pattern Agents-SDK default callback shape (cf-portal compatibility) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reading the Seal implementation (cloudflare/cto/seals) reveals the canonical Agents-SDK callbackPath shape: /agents///callback Seal uses `/agents/user-do/{userId}/callback` and successfully connects to cf-portal. We were using `/agents/oauth/callback` (no instance name in the path), which cf-portal rejected with: Redirect URI not allowed by application configuration cf-portal appears to validate redirect URI shape, requiring the SDK-canonical pattern. Switch Dodo to `/agents/coding-agent/{userEmail}/callback` to match. The path still gets caught by `app.all("/agents/*")` so the existing dispatch logic works unchanged — the Agents SDK's MCP callback handler picks up the state param and routes to the correct DO storage. userEmail is already canonicalized by the auth middleware (lowercased, trimmed). Email chars (@ + .) are safe in URL paths per RFC 3986. Tests: 817/817 pass. Typecheck clean. beep-boop-🤖 --- src/coding-agent.ts | 8 ++++---- src/index.ts | 20 ++++++++++++++------ 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/coding-agent.ts b/src/coding-agent.ts index 3209c18..2afc1ec 100644 --- a/src/coding-agent.ts +++ b/src/coding-agent.ts @@ -6327,12 +6327,12 @@ export class CodingAgent extends Think { const url = server.server_url; const name = server.name ?? new URL(url).host; await this.removeMcpServer(mcpId); - // Mirror the /api/mcp/start-auth callback path. `/agents` (no trailing - // segment) doesn't match `app.all("/agents/*")` and returns 404 when - // the OAuth provider redirects there. + // Mirror the /api/mcp/start-auth callback path shape (Seal pattern): + // /agents///callback. cf-portal rejects + // redirect URIs that don't follow this OAuth-callback shape. await this.addMcpServer(name, url, { callbackHost: this.env.WORKER_URL, - callbackPath: "/agents/oauth/callback", + callbackPath: `/agents/coding-agent/${this.name}/callback`, }); } diff --git a/src/index.ts b/src/index.ts index 199969c..197ba8a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1634,14 +1634,22 @@ app.post("/api/mcp/start-auth", async (c) => { const callbackHost = c.env.WORKER_URL && c.env.WORKER_URL !== "http://localhost:8787" ? c.env.WORKER_URL : inferredHost; - // callbackPath must point at a real route, not just the prefix. We - // mount `app.all("/agents/*")` (one path segment minimum). Registering - // `/agents` as the redirect_uri makes the OAuth provider redirect to a - // path that doesn't match the wildcard, and Dodo returns 404. Use a - // dedicated, stable subpath under `/agents/`. + // callbackPath follows the Agents-SDK convention used by Seal et al: + // `/agents///callback`. The trailing + // /callback segment matters — some OAuth providers (cf-portal in + // particular) reject redirect URIs that don't end in a recognised + // OAuth-callback suffix with "Redirect URI not allowed by application + // configuration". Using the SDK's canonical shape also keeps us + // compatible with `/agents/*` routing on the way back. + // + // userEmail is already canonicalized (lowercased + trimmed) by the + // auth middleware so it's safe to embed in a URL path. Edge case: + // emails contain `@` and `.` which are valid in URL path segments + // per RFC 3986 — no encoding needed and no provider rejects them. + const callbackPath = `/agents/coding-agent/${userEmail}/callback`; const result = await stub.addMcpServer(displayName, mcpUrl, { callbackHost, - callbackPath: "/agents/oauth/callback", + callbackPath, }); if (result.state === "authenticating") { return c.json({ authUrl: result.authUrl });