|
1 | 1 | import { auth as mcpAuth } from '@modelcontextprotocol/sdk/client/auth.js' |
| 2 | +import { OAuthError, ServerError } from '@modelcontextprotocol/sdk/server/auth/errors.js' |
2 | 3 | import { db } from '@sim/db' |
3 | 4 | import { mcpServers } from '@sim/db/schema' |
4 | 5 | import { createLogger } from '@sim/logger' |
@@ -26,23 +27,29 @@ const OAUTH_START_TTL_MS = 10 * 60 * 1000 |
26 | 27 | const MAX_SURFACED_ERROR_LENGTH = 250 |
27 | 28 |
|
28 | 29 | function surfaceOauthError(error: unknown): string { |
29 | | - const raw = error instanceof Error ? error.message : String(error) |
30 | | - const rawBodyMatch = raw.match(/Raw body:\s*(\{[\s\S]*\})\s*$/) |
31 | | - if (rawBodyMatch) { |
32 | | - try { |
33 | | - const body = JSON.parse(rawBodyMatch[1]) as Record<string, unknown> |
34 | | - const vendorMessage = |
35 | | - typeof body.error_description === 'string' |
36 | | - ? body.error_description |
37 | | - : typeof body.message === 'string' |
38 | | - ? body.message |
39 | | - : typeof body.error === 'string' |
40 | | - ? body.error |
41 | | - : null |
42 | | - if (vendorMessage) return truncate(`Authorization server: ${vendorMessage}`) |
43 | | - } catch {} |
| 30 | + // Spec-compliant OAuth servers throw typed subclasses with clean RFC 6749 fields. |
| 31 | + if (error instanceof OAuthError && !(error instanceof ServerError)) { |
| 32 | + return truncate(`${error.errorCode}: ${error.message}`) |
| 33 | + } |
| 34 | + |
| 35 | + // ServerError wraps non-spec response bodies as "HTTP N: Invalid OAuth error |
| 36 | + // response: ... Raw body: {...}". Dig the vendor message out of the JSON tail. |
| 37 | + if (error instanceof Error) { |
| 38 | + const rawBodyMatch = error.message.match(/Raw body:\s*(\{[\s\S]*\})\s*$/) |
| 39 | + if (rawBodyMatch) { |
| 40 | + try { |
| 41 | + const body = JSON.parse(rawBodyMatch[1]) as Record<string, unknown> |
| 42 | + const vendorMessage = |
| 43 | + (typeof body.error_description === 'string' && body.error_description) || |
| 44 | + (typeof body.message === 'string' && body.message) || |
| 45 | + (typeof body.error === 'string' && body.error) || |
| 46 | + null |
| 47 | + if (vendorMessage) return truncate(`Authorization server: ${vendorMessage}`) |
| 48 | + } catch {} |
| 49 | + } |
| 50 | + return truncate(error.message.split('\n')[0] || 'Failed to start OAuth flow') |
44 | 51 | } |
45 | | - return truncate(raw.split('\n')[0] || 'Failed to start OAuth flow') |
| 52 | + return 'Failed to start OAuth flow' |
46 | 53 | } |
47 | 54 |
|
48 | 55 | function truncate(message: string): string { |
|
0 commit comments