@tanstack/ai
Version:
Type-safe TypeScript AI SDK for streaming chat, tool calling, agents, structured outputs, and multimodal generation.
72 lines (71 loc) • 2.28 kB
JavaScript
class MCPDuplicateToolNameError extends Error {
constructor(toolName) {
super(
`Duplicate MCP tool name "${toolName}" in chat({ mcp.clients }). Set a unique \`prefix\` on one of the MCP clients (or use a pool, which auto-prefixes) to disambiguate.`
);
this.toolName = toolName;
this.name = "MCPDuplicateToolNameError";
}
toolName;
}
class MCPManager {
static from(options) {
return new MCPManager(options);
}
#sources;
#shouldClose;
#lazyTools;
#onDiscoveryError;
constructor(options) {
this.#sources = options?.clients ?? [];
this.#shouldClose = options ? options.connection !== "keep-alive" : false;
this.#lazyTools = options?.lazyTools ?? false;
this.#onDiscoveryError = options?.onDiscoveryError;
}
/**
* Discover + merge tools from all sources. Throws on a fatal discovery error
* (no `onDiscoveryError`, or it re-threw) or a duplicate tool name; in that
* case it first closes any connected sources when the policy is 'close'.
*/
async discover() {
if (this.#sources.length === 0) return [];
try {
const settled = await Promise.allSettled(
this.#sources.map((s) => s.tools({ lazy: this.#lazyTools }))
);
const tools = [];
const zipped = this.#sources.map(
(source, i) => [source, settled[i]]
);
for (const [source, result] of zipped) {
if (result === void 0) continue;
if (result.status === "fulfilled") {
tools.push(...result.value);
} else if (this.#onDiscoveryError) {
await this.#onDiscoveryError(result.reason, source);
} else {
throw result.reason;
}
}
const seen = /* @__PURE__ */ new Set();
for (const t of tools) {
if (seen.has(t.name)) throw new MCPDuplicateToolNameError(t.name);
seen.add(t.name);
}
return tools;
} catch (err) {
await this.dispose();
throw err;
}
}
/** Close sources iff policy is 'close'. Idempotent; never throws. */
async dispose() {
if (!this.#shouldClose || this.#sources.length === 0) return;
await Promise.allSettled(this.#sources.map((s) => s.close()));
}
}
export {
MCPDuplicateToolNameError,
MCPManager
};
//# sourceMappingURL=manager.js.map