( connection: McpConnection, toolName: string, args: Record<string, unknown>, elicit: Elicit, onToolListChanged: (() => void) | undefined, )
| 356 | // --------------------------------------------------------------------------- |
| 357 | |
| 358 | const useConnection = ( |
| 359 | connection: McpConnection, |
| 360 | toolName: string, |
| 361 | args: Record<string, unknown>, |
| 362 | elicit: Elicit, |
| 363 | onToolListChanged: (() => void) | undefined, |
| 364 | ): Effect.Effect<unknown, McpInvocationError | McpOAuthReauthorizationRequired> => |
| 365 | Effect.gen(function* () { |
| 366 | const deadline = yield* Effect.acquireRelease( |
| 367 | Effect.sync(() => makeActiveWorkDeadline()), |
| 368 | (activeWork) => Effect.sync(activeWork.dispose), |
| 369 | ); |
| 370 | installElicitationHandler(connection.client, elicit, deadline); |
| 371 | installToolListChangedHandler(connection.client, onToolListChanged); |
| 372 | return yield* Effect.tryPromise({ |
| 373 | try: () => |
| 374 | connection.client.callTool( |
| 375 | { name: toolName, arguments: args }, |
| 376 | { signal: deadline.signal, timeout: MCP_SDK_TIMEOUT_BACKSTOP_MS }, |
| 377 | ), |
| 378 | catch: (cause) => { |
| 379 | if (Predicate.isTagged(cause, "McpOAuthReauthorizationRequired")) { |
| 380 | return new McpOAuthReauthorizationRequired({ |
| 381 | message: "MCP OAuth re-authorization required", |
| 382 | }); |
| 383 | } |
| 384 | // Raised by the fetch adapter when the 403 carried an RFC 6750 |
| 385 | // insufficient_scope challenge (the authProvider path, where the SDK |
| 386 | // would otherwise consume the challenge before we could see it). |
| 387 | if (Predicate.isTagged(cause, "McpInsufficientScopeError")) { |
| 388 | return new McpInvocationError({ |
| 389 | toolName, |
| 390 | message: `MCP tool call failed for ${toolName}`, |
| 391 | status: 403, |
| 392 | insufficientScope: true, |
| 393 | transportFailure: true, |
| 394 | }); |
| 395 | } |
| 396 | const status = httpStatusFromCause(cause); |
| 397 | const protocolError = asProtocolError(cause); |
| 398 | const sdkFailure = summarizeSdkFailure(cause); |
| 399 | return new McpInvocationError({ |
| 400 | toolName, |
| 401 | // The class and code ride in the message because the dispatch |
| 402 | // defect log renders only `Error#toString()`: without them the |
| 403 | // trace says which tool failed and nothing about how. |
| 404 | message: `MCP tool call failed for ${toolName} (${[ |
| 405 | sdkFailure.name, |
| 406 | ...(sdkFailure.code === undefined ? [] : [String(sdkFailure.code)]), |
| 407 | ...(status === undefined ? [] : [`HTTP ${status}`]), |
| 408 | ].join(" ")})`, |
| 409 | sdkFailure, |
| 410 | ...(status === undefined ? {} : { status }), |
| 411 | ...(protocolError === undefined |
| 412 | ? { transportFailure: true } |
| 413 | : { |
| 414 | // A JSON-RPC error is the server's answer to this call, written |
| 415 | // for the caller (the same trust level as an `isError` result |
no test coverage detected