Skip to content

Commit 27a0cfd

Browse files
committed
fix(mcp): probe-based OAuth detection in test-connection
1 parent 57b9a2f commit 27a0cfd

4 files changed

Lines changed: 27 additions & 15 deletions

File tree

apps/sim/app/api/mcp/servers/test-connection/route.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
validateMcpServerSsrf,
1313
} from '@/lib/mcp/domain-check'
1414
import { getParsedBody, withMcpAuth } from '@/lib/mcp/middleware'
15+
import { detectMcpAuthType } from '@/lib/mcp/oauth'
1516
import { resolveMcpConfigEnvVars } from '@/lib/mcp/resolve-config'
1617
import type { McpTransport } from '@/lib/mcp/types'
1718
import { createMcpErrorResponse, createMcpSuccessResponse } from '@/lib/mcp/utils'
@@ -31,6 +32,8 @@ function isUrlBasedTransport(transport: McpTransport): boolean {
3132
interface TestConnectionResult {
3233
success: boolean
3334
error?: string
35+
authRequired?: boolean
36+
authType?: 'none' | 'headers' | 'oauth'
3437
serverInfo?: {
3538
name: string
3639
version: string
@@ -163,6 +166,16 @@ export const POST = withRouteHandler(
163166
}
164167

165168
const result: TestConnectionResult = { success: false }
169+
170+
// Skip unauth connect when the server returns an RFC 9728 OAuth challenge.
171+
const detectedAuthType = await detectMcpAuthType(testConfig.url)
172+
if (detectedAuthType === 'oauth') {
173+
result.authRequired = true
174+
result.authType = 'oauth'
175+
return createMcpSuccessResponse(result, 200)
176+
}
177+
result.authType = detectedAuthType
178+
166179
let client: McpClient | null = null
167180

168181
try {

apps/sim/app/workspace/[workspaceId]/settings/components/mcp/components/mcp-server-form-modal/mcp-server-form-modal.tsx

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export interface McpServerFormConfig {
5252
timeout: number
5353
oauthClientId?: string
5454
oauthClientSecret?: string
55+
authType?: 'none' | 'headers' | 'oauth'
5556
}
5657

5758
export interface McpServerFormModalProps {
@@ -109,11 +110,12 @@ interface EnvVarDropdownConfig {
109110
}
110111

111112
function getTestButtonLabel(
112-
testResult: { success: boolean; error?: string } | null,
113+
testResult: { success: boolean; error?: string; authRequired?: boolean } | null,
113114
isTestingConnection: boolean
114115
): string {
115116
if (isTestingConnection) return 'Testing...'
116117
if (testResult?.success) return 'Connection success'
118+
if (testResult?.authRequired) return 'Requires OAuth'
117119
if (testResult && !testResult.success) return 'No connection: retry'
118120
return 'Test Connection'
119121
}
@@ -517,19 +519,11 @@ export function McpServerFormModal({
517519
workspaceId,
518520
})
519521

520-
if (!connectionResult.success) {
521-
const errorText = (connectionResult.error || '').toLowerCase()
522-
const looksLikeAuthRequired =
523-
/\b401\b/.test(errorText) ||
524-
errorText.includes('unauthorized') ||
525-
errorText.includes('oauth') ||
526-
errorText.includes('authentication')
527-
if (!looksLikeAuthRequired) {
528-
setSubmitError(
529-
connectionResult.error || 'Connection test failed. Please check the URL and try again.'
530-
)
531-
return
532-
}
522+
if (!connectionResult.success && !connectionResult.authRequired) {
523+
setSubmitError(
524+
connectionResult.error || 'Connection test failed. Please check the URL and try again.'
525+
)
526+
return
533527
}
534528

535529
await onSubmit({
@@ -538,6 +532,7 @@ export function McpServerFormModal({
538532
url: formData.url!,
539533
headers,
540534
timeout: formData.timeout || 30000,
535+
authType: connectionResult.authType,
541536
oauthClientId:
542537
mode === 'edit'
543538
? oauthClientIdChanged
@@ -587,7 +582,7 @@ export function McpServerFormModal({
587582
workspaceId,
588583
})
589584

590-
if (!connectionResult.success) {
585+
if (!connectionResult.success && !connectionResult.authRequired) {
591586
setSubmitError(
592587
connectionResult.error || 'Connection test failed. Please check the URL and try again.'
593588
)
@@ -600,6 +595,7 @@ export function McpServerFormModal({
600595
url: config.url,
601596
headers: config.headers,
602597
timeout: 30000,
598+
authType: connectionResult.authType,
603599
})
604600

605601
onOpenChange(false)

apps/sim/hooks/queries/mcp.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ export interface McpServerInput {
5454
enabled: boolean
5555
oauthClientId?: string
5656
oauthClientSecret?: string
57+
authType?: 'none' | 'headers' | 'oauth'
5758
}
5859

5960
async function fetchMcpServers(workspaceId: string, signal?: AbortSignal): Promise<McpServer[]> {

apps/sim/lib/api/contracts/mcp.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,8 @@ export const mcpServerTestResultSchema = z.object({
252252
success: z.boolean(),
253253
message: z.string().optional(),
254254
error: z.string().optional(),
255+
authRequired: z.boolean().optional(),
256+
authType: mcpAuthTypeSchema.optional(),
255257
serverInfo: z
256258
.object({
257259
name: z.string(),

0 commit comments

Comments
 (0)