MCPcopy Create free account
hub / github.com/UsefulSoftwareCo/executor / listAllTools

Function listAllTools

packages/plugins/mcp/src/sdk/discover.ts:57–128  ·  view source on GitHub ↗
(
  connection: McpConnection,
)

Source from the content-addressed store, hash-verified

55 * returns one page, not the catalog).
56 */
57const listAllTools = (
58 connection: McpConnection,
59): Effect.Effect<McpToolManifest, McpToolDiscoveryError> =>
60 Effect.gen(function* () {
61 const tools: unknown[] = [];
62 let cursor: string | undefined = undefined;
63
64 for (let page = 0; page < MAX_LIST_TOOLS_PAGES; page++) {
65 const params: { cursor?: string } | undefined = cursor === undefined ? undefined : { cursor };
66 const listResult = yield* Effect.tryPromise({
67 try: () => connection.client.listTools(params),
68 // A bearer accepted at the handshake can be rejected by the time the
69 // listing runs (revocation landing mid-discovery). The fetch adapter's
70 // reauthorization interception fires here exactly as during connect,
71 // so keep the structural signals — the reauthorization tag and the
72 // HTTP status — instead of collapsing every listing failure into a
73 // generic discovery error; discarding them left the connection on a
74 // sticky degraded verdict instead of reconnect-required. Statuses come
75 // from `httpStatusFromCause` only: the connection-only numeric-code
76 // decode must stay out, since JSON-RPC error codes are not HTTP
77 // statuses.
78 catch: (cause) => {
79 if (hasNestedOAuthReauthorization(cause)) {
80 return new McpToolDiscoveryError({
81 stage: "list_tools",
82 message: "Failed listing MCP tools: MCP OAuth re-authorization required",
83 reauthorizationRequired: true,
84 });
85 }
86 const failure = Option.getOrNull(decodeSdkFailure(cause));
87 // A modern-era connection whose server breaks the modern response
88 // contract means the server echoed our proposed revision without
89 // implementing it; callers can retry with legacy negotiation.
90 const modernContractViolation =
91 connection.client.getProtocolEra?.() === "modern" && failure?.code === "INVALID_RESULT";
92 const httpStatus = httpStatusFromCause(cause);
93 return new McpToolDiscoveryError({
94 stage: "list_tools",
95 message:
96 failure?.message !== undefined
97 ? `Failed listing MCP tools: ${failure.message.slice(0, 300)}`
98 : httpStatus === undefined
99 ? "Failed listing MCP tools"
100 : `Failed listing MCP tools (HTTP ${httpStatus})`,
101 ...(httpStatus === undefined ? {} : { httpStatus }),
102 ...(modernContractViolation ? { modernContractViolation: true } : {}),
103 });
104 },
105 });
106
107 const decoded = decodeListToolsPage(listResult);
108 if (Option.isNone(decoded)) {
109 return yield* new McpToolDiscoveryError({
110 stage: "list_tools",
111 message: "MCP listTools response did not match the expected schema",
112 });
113 }
114

Callers 1

discoverToolsFunction · 0.85

Calls 5

httpStatusFromCauseFunction · 0.90
decodeListToolsPageFunction · 0.90
pushMethod · 0.80

Tested by

no test coverage detected