| 189 | } |
| 190 | |
| 191 | private async createClient( |
| 192 | config: McpServerConfig, |
| 193 | resolvedIP: string | null, |
| 194 | userId?: string |
| 195 | ): Promise<McpClient> { |
| 196 | const securityPolicy = { |
| 197 | requireConsent: true, |
| 198 | auditLevel: 'basic' as const, |
| 199 | maxToolExecutionsPerHour: 1000, |
| 200 | allowedOrigins: config.url ? [new URL(config.url).origin] : undefined, |
| 201 | } |
| 202 | |
| 203 | if (config.authType !== 'oauth') { |
| 204 | const client = new McpClient({ |
| 205 | config, |
| 206 | securityPolicy, |
| 207 | resolvedIP: resolvedIP ?? undefined, |
| 208 | }) |
| 209 | await client.connect() |
| 210 | return client |
| 211 | } |
| 212 | |
| 213 | if (!userId || !config.workspaceId) { |
| 214 | throw new Error('OAuth MCP server requires both userId and workspaceId') |
| 215 | } |
| 216 | const workspaceId = config.workspaceId |
| 217 | |
| 218 | // Load the row inside the refresh lock so concurrent callers observe tokens |
| 219 | // written by a predecessor refresh, rather than a stale snapshot. Without |
| 220 | // this, the second caller's provider would hold a rotated-out refresh token |
| 221 | // and the SDK would trip `invalid_grant`. The lock is keyed on serverId |
| 222 | // since the row is per-server. |
| 223 | return withMcpOauthRefreshLock(config.id, async () => { |
| 224 | const row = await getOrCreateOauthRow({ |
| 225 | mcpServerId: config.id, |
| 226 | userId, |
| 227 | workspaceId, |
| 228 | }) |
| 229 | if (!row.tokens) { |
| 230 | throw new McpOauthAuthorizationRequiredError(config.id, config.name) |
| 231 | } |
| 232 | const preregistered = await loadPreregisteredClient(config.id) |
| 233 | const authProvider = new SimMcpOauthProvider({ row, preregistered }) |
| 234 | const client = new McpClient({ |
| 235 | config, |
| 236 | securityPolicy, |
| 237 | authProvider, |
| 238 | resolvedIP: resolvedIP ?? undefined, |
| 239 | }) |
| 240 | await client.connect() |
| 241 | return client |
| 242 | }) |
| 243 | } |
| 244 | |
| 245 | /** |
| 246 | * Execute a tool on a specific server with retry logic for session errors. |