Skip to content

Commit ccfa60f

Browse files
committed
refactor(mcp): use SDK's typed OAuthError subclasses for error surfacing
1 parent 3d56049 commit ccfa60f

1 file changed

Lines changed: 23 additions & 16 deletions

File tree

  • apps/sim/app/api/mcp/oauth/start

apps/sim/app/api/mcp/oauth/start/route.ts

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { auth as mcpAuth } from '@modelcontextprotocol/sdk/client/auth.js'
2+
import { OAuthError, ServerError } from '@modelcontextprotocol/sdk/server/auth/errors.js'
23
import { db } from '@sim/db'
34
import { mcpServers } from '@sim/db/schema'
45
import { createLogger } from '@sim/logger'
@@ -26,23 +27,29 @@ const OAUTH_START_TTL_MS = 10 * 60 * 1000
2627
const MAX_SURFACED_ERROR_LENGTH = 250
2728

2829
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')
4451
}
45-
return truncate(raw.split('\n')[0] || 'Failed to start OAuth flow')
52+
return 'Failed to start OAuth flow'
4653
}
4754

4855
function truncate(message: string): string {

0 commit comments

Comments
 (0)