( connection: McpConnection, )
| 39 | * returns one page, not the catalog). |
| 40 | */ |
| 41 | const listAllTools = ( |
| 42 | connection: McpConnection, |
| 43 | ): Effect.Effect<McpToolManifest, McpToolDiscoveryError> => |
| 44 | Effect.gen(function* () { |
| 45 | const tools: unknown[] = []; |
| 46 | let cursor: string | undefined = undefined; |
| 47 | |
| 48 | for (let page = 0; page < MAX_LIST_TOOLS_PAGES; page++) { |
| 49 | const params: { cursor?: string } | undefined = cursor === undefined ? undefined : { cursor }; |
| 50 | const listResult = yield* Effect.tryPromise({ |
| 51 | try: () => connection.client.listTools(params), |
| 52 | catch: () => |
| 53 | new McpToolDiscoveryError({ |
| 54 | stage: "list_tools", |
| 55 | message: "Failed listing MCP tools", |
| 56 | }), |
| 57 | }); |
| 58 | |
| 59 | const decoded = decodeListToolsPage(listResult); |
| 60 | if (Option.isNone(decoded)) { |
| 61 | return yield* new McpToolDiscoveryError({ |
| 62 | stage: "list_tools", |
| 63 | message: "MCP listTools response did not match the expected schema", |
| 64 | }); |
| 65 | } |
| 66 | |
| 67 | tools.push(...decoded.value.tools); |
| 68 | const nextCursor = decoded.value.nextCursor; |
| 69 | if (nextCursor == null || nextCursor === "") break; |
| 70 | cursor = nextCursor; |
| 71 | } |
| 72 | |
| 73 | return extractManifestFromListToolsResult( |
| 74 | { tools }, |
| 75 | { |
| 76 | serverInfo: connection.client.getServerVersion?.(), |
| 77 | instructions: connection.client.getInstructions?.(), |
| 78 | }, |
| 79 | ); |
| 80 | }); |
| 81 | |
| 82 | /** |
| 83 | * Connect to an MCP server and discover all available tools. |
no test coverage detected