(_input, context)
| 83 | return { behavior: 'allow', updatedInput: input } |
| 84 | }, |
| 85 | async call(_input, context) { |
| 86 | // claude.ai connectors use a separate auth flow (handleClaudeAIAuth in |
| 87 | // MCPRemoteServerMenu) that we don't invoke programmatically here — |
| 88 | // just point the user at /mcp. |
| 89 | if (config.type === 'claudeai-proxy') { |
| 90 | return { |
| 91 | data: { |
| 92 | status: 'unsupported' as const, |
| 93 | message: `This is a claude.ai MCP connector. Ask the user to run /mcp and select "${serverName}" to authenticate.`, |
| 94 | }, |
| 95 | } |
| 96 | } |
| 97 | |
| 98 | // performMCPOAuthFlow only accepts sse/http. needs-auth state is only |
| 99 | // set on HTTP 401 (UnauthorizedError) so other transports shouldn't |
| 100 | // reach here, but be defensive. |
| 101 | if (config.type !== 'sse' && config.type !== 'http') { |
| 102 | return { |
| 103 | data: { |
| 104 | status: 'unsupported' as const, |
| 105 | message: `Server "${serverName}" uses ${transport} transport which does not support OAuth from this tool. Ask the user to run /mcp and authenticate manually.`, |
| 106 | }, |
| 107 | } |
| 108 | } |
| 109 | |
| 110 | const sseOrHttpConfig = config as ( |
| 111 | | McpSSEServerConfig |
| 112 | | McpHTTPServerConfig |
| 113 | ) & { scope: ScopedMcpServerConfig['scope'] } |
| 114 | |
| 115 | // Mirror cli/print.ts mcp_authenticate: start the flow, capture the |
| 116 | // URL via onAuthorizationUrl, return it immediately. The flow's |
| 117 | // Promise resolves later when the browser callback fires. |
| 118 | let resolveAuthUrl: ((url: string) => void) | undefined |
| 119 | const authUrlPromise = new Promise<string>(resolve => { |
| 120 | resolveAuthUrl = resolve |
| 121 | }) |
| 122 | |
| 123 | const controller = new AbortController() |
| 124 | const { setAppState } = context |
| 125 | |
| 126 | const oauthPromise = performMCPOAuthFlow( |
| 127 | serverName, |
| 128 | sseOrHttpConfig, |
| 129 | u => resolveAuthUrl?.(u), |
| 130 | controller.signal, |
| 131 | { skipBrowserOpen: true }, |
| 132 | ) |
| 133 | |
| 134 | // Background continuation: once OAuth completes, reconnect and swap |
| 135 | // the real tools into appState. Prefix-based replacement removes this |
| 136 | // pseudo-tool since it shares the mcp__<server>__ prefix. |
| 137 | void oauthPromise |
| 138 | .then(async () => { |
| 139 | clearMcpAuthCache() |
| 140 | const result = await reconnectMcpServerImpl(serverName, config) |
| 141 | const prefix = getMcpPrefix(serverName) |
| 142 | setAppState(prev => ({ |
nothing calls this directly
no test coverage detected