The AI trust layer between agents: identity, agreements, safeguards, and outcome intelligence for A2A.

Commit db345778

db34577898cfMerged

aryaamin <[email protected]> · 2 months ago · 4 files · ~4 modified

Description

fix: restart OAuth for unauthenticated MCP clients

Details

Commit
db34577898cf
Parent
90ba0b085978

api/mcp.tsmodifiedView file

@@ -82,12 +82,40 @@
8282 };
8383 }
8484
85-const handler = withMcpAuth(mcp, verifyToken, {
85+const resourceMetadataPath = '/.well-known/oauth-protected-resource/mcp';
86+
87+const authenticatedHandler = withMcpAuth(mcp, verifyToken, {
8688 required: true,
87- resourceMetadataPath: '/.well-known/oauth-protected-resource',
89+ resourceMetadataPath,
8890 ...(process.env.OPENCLASP_MCP_URL
8991 ? { resourceUrl: new URL(process.env.OPENCLASP_MCP_URL).origin }
9092 : {}),
9193 });
9294
95+async function handler(request: Request): Promise<Response> {
96+ const response = await authenticatedHandler(request);
97+ if (response.status !== 401 || request.headers.get('authorization')?.trim()) return response;
98+
99+ // A missing credential is an authentication discovery challenge, not an invalid token.
100+ // Cursor's V2 MCP client treats `invalid_token` while auth is still unknown as a
101+ // transient connection failure instead of starting OAuth.
102+ const configuredResource = process.env.OPENCLASP_MCP_URL;
103+ const publicOrigin = configuredResource
104+ ? new URL(configuredResource).origin
105+ : new URL(request.url).origin;
106+ const headers = new Headers(response.headers);
107+ headers.set(
108+ 'www-authenticate',
109+ `Bearer resource_metadata="${new URL(resourceMetadataPath, publicOrigin).href}"`,
110+ );
111+ headers.set('cache-control', 'no-store');
112+ headers.delete('content-length');
113+ headers.delete('content-type');
114+ return new Response(null, {
115+ status: response.status,
116+ statusText: response.statusText,
117+ headers,
118+ });
119+}
120+
93121 export { handler as GET, handler as POST };