@tanstack/ai-mcp
Version:
Host-side Model Context Protocol client for TanStack AI: discover and run MCP server tools, resources, and prompts in any adapter's chat() loop, with generated end-to-end types.
67 lines (66 loc) • 2.72 kB
JavaScript
import { DuplicateToolNameError, MCPConnectionError } from "./errors.js";
import { createMCPClient } from "./client.js";
//#region src/pool.ts
async function createMCPClients(config) {
const names = Object.keys(config);
const settled = await Promise.allSettled(names.map(async (name) => {
const opts = config[name];
const prefix = opts.prefix === void 0 ? name : opts.prefix || void 0;
return [name, await createMCPClient({
...opts,
prefix
})];
}));
const ok = settled.filter((r) => r.status === "fulfilled");
const failed = settled.map((r, i) => r.status === "rejected" ? names[i] : null).filter((n) => n !== null);
if (failed.length > 0) {
await Promise.allSettled(ok.map((r) => r.value[1].close()));
const firstRejection = settled.find((r) => r.status === "rejected");
throw new MCPConnectionError(`Failed to connect MCP server(s): ${failed.join(", ")}`, firstRejection?.reason);
}
const clients = Object.fromEntries(ok.map((r) => r.value));
const pool = {
clients,
async tools(options) {
const entries = Object.entries(clients);
const results = await Promise.allSettled(entries.map(([, c]) => c.tools(options)));
const failedNames = entries.map(([key], i) => results[i]?.status === "rejected" ? key : null).filter((k) => k !== null);
if (failedNames.length > 0) {
const firstFailure = results.find((r) => r.status === "rejected");
throw new MCPConnectionError(`Failed to list tools from MCP server(s): ${failedNames.join(", ")}`, firstFailure?.reason);
}
const all = results.flatMap((r) => r.status === "fulfilled" ? r.value : []);
const seen = /* @__PURE__ */ new Set();
for (const t of all) {
if (seen.has(t.name)) throw new DuplicateToolNameError(t.name);
seen.add(t.name);
}
return all;
},
getServers() {
return Object.fromEntries(Object.entries(clients).map(([key, c]) => [key, c.getInfo()]));
},
async readResource(uri) {
const errors = [];
const all = Object.values(clients);
for (const c of all) try {
const result = await c.readResource(uri);
if (result.contents.some((entry) => entry.uri === uri)) return result;
} catch (err) {
errors.push(err);
}
if (errors.length > 0) throw new Error(`Failed to read MCP resource "${uri}": no client could resolve it (${errors.length} error(s) attached)`, { cause: new AggregateError(errors) });
throw new Error(`Failed to read MCP resource "${uri}": no configured MCP server owns this uri`);
},
async close() {
await Promise.all(Object.values(clients).map((c) => c.close()));
},
async [Symbol.asyncDispose]() {
await pool.close();
}
};
return pool;
}
//#endregion
export { createMCPClients };
//# sourceMappingURL=pool.js.map