@webmcp-auto-ui/core 0.2.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/multi-client.d.ts +52 -0
- package/dist/multi-client.d.ts.map +1 -0
- package/dist/multi-client.js +105 -0
- package/dist/multi-client.js.map +1 -0
- package/package.json +1 -1
- package/src/index.ts +4 -0
- package/src/multi-client.ts +140 -0
package/README.md
CHANGED
|
@@ -8,6 +8,10 @@ W3C WebMCP Draft 2026-03-27 polyfill and MCP Streamable HTTP client. Pure TypeSc
|
|
|
8
8
|
|
|
9
9
|
**McpClient** — connects to MCP servers over Streamable HTTP (SSE). Handles `initialize`, `tools/list`, and `tools/call`.
|
|
10
10
|
|
|
11
|
+
**McpMultiClient** — manages simultaneous connections to multiple MCP servers. Aggregates tool lists and routes `callTool` to the correct server. Useful for apps that connect to several data sources at once (e.g. flex with multi-MCP).
|
|
12
|
+
|
|
13
|
+
**Prompt caching** — the `cache_control` property is applied on the tools array (not individual tools) to work correctly with Anthropic's prompt caching. This fix ensures cache hits when the tool set is stable across requests.
|
|
14
|
+
|
|
11
15
|
**createToolGroup** — registers a named group of tools on `navigator.modelContext`. Aborting the group unregisters all tools at once — useful for component lifecycle cleanup.
|
|
12
16
|
|
|
13
17
|
**sanitizeSchema** — strips JSON Schema keywords that Anthropic's API rejects (`oneOf`, `anyOf`, `allOf`, `$ref`, `if/then/else`). Applied automatically before any LLM call.
|
|
@@ -30,6 +34,7 @@ npm install @webmcp-auto-ui/core
|
|
|
30
34
|
import {
|
|
31
35
|
initializeWebMCPPolyfill,
|
|
32
36
|
McpClient,
|
|
37
|
+
McpMultiClient,
|
|
33
38
|
createToolGroup,
|
|
34
39
|
textResult, jsonResult,
|
|
35
40
|
listenForAgentCalls,
|
|
@@ -52,9 +57,17 @@ const init = await client.connect();
|
|
|
52
57
|
const tools = await client.listTools();
|
|
53
58
|
const result = await client.callTool('my_tool', { arg: 'value' });
|
|
54
59
|
|
|
60
|
+
// Multi-server connections
|
|
61
|
+
const multi = new McpMultiClient();
|
|
62
|
+
await multi.addServer('https://mcp1.example.com/mcp');
|
|
63
|
+
await multi.addServer('https://mcp2.example.com/mcp');
|
|
64
|
+
const allTools = multi.listAllTools(); // aggregated from all servers
|
|
65
|
+
const result = await multi.callTool('query_sql', { sql: 'SELECT 1' }); // routes to correct server
|
|
66
|
+
|
|
55
67
|
// Cleanup
|
|
56
68
|
stop();
|
|
57
69
|
group.abort();
|
|
70
|
+
await multi.disconnectAll();
|
|
58
71
|
```
|
|
59
72
|
|
|
60
73
|
## Types
|
package/dist/index.d.ts
CHANGED
|
@@ -7,4 +7,6 @@ export { listenForAgentCalls, stopListening, callToolViaPostMessage, isWebMCPEve
|
|
|
7
7
|
export { dispatchAndWait, signalCompletion, sanitizeSchema, createToolGroup, } from './utils.js';
|
|
8
8
|
export { textResult, jsonResult, registerSkill, unregisterSkill, getSkill, listSkills, clearSkills, } from './webmcp-helpers.js';
|
|
9
9
|
export type { SkillDef } from './webmcp-helpers.js';
|
|
10
|
+
export { McpMultiClient } from './multi-client.js';
|
|
11
|
+
export type { ConnectedServer } from './multi-client.js';
|
|
10
12
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,YAAY,EACV,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,eAAe,EACf,gBAAgB,EAChB,mBAAmB,EACnB,kBAAkB,EAClB,sBAAsB,EACtB,qBAAqB,EACrB,uBAAuB,EACvB,cAAc,EACd,mBAAmB,EACnB,oBAAoB,EACpB,YAAY,EACZ,mBAAmB,EACnB,2BAA2B,EAC3B,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,mBAAmB,EACnB,gBAAgB,EAChB,WAAW,EACX,iBAAiB,EACjB,kBAAkB,EAClB,WAAW,EACX,gBAAgB,EAChB,kBAAkB,EAClB,qBAAqB,EACrB,cAAc,EACd,YAAY,EACZ,eAAe,EACf,aAAa,EACb,eAAe,EACf,mBAAmB,EACnB,OAAO,EACP,oBAAoB,EACpB,aAAa,EACb,kBAAkB,EAClB,gBAAgB,EAChB,mBAAmB,EACnB,qBAAqB,EACrB,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAGvE,OAAO,EACL,wBAAwB,EACxB,qBAAqB,EACrB,eAAe,EACf,mBAAmB,GACpB,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAGxC,OAAO,EACL,mBAAmB,EACnB,aAAa,EACb,sBAAsB,EACtB,aAAa,GACd,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,eAAe,GAChB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,UAAU,EACV,UAAU,EACV,aAAa,EACb,eAAe,EACf,QAAQ,EACR,UAAU,EACV,WAAW,GACZ,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,YAAY,EACV,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,eAAe,EACf,gBAAgB,EAChB,mBAAmB,EACnB,kBAAkB,EAClB,sBAAsB,EACtB,qBAAqB,EACrB,uBAAuB,EACvB,cAAc,EACd,mBAAmB,EACnB,oBAAoB,EACpB,YAAY,EACZ,mBAAmB,EACnB,2BAA2B,EAC3B,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,mBAAmB,EACnB,gBAAgB,EAChB,WAAW,EACX,iBAAiB,EACjB,kBAAkB,EAClB,WAAW,EACX,gBAAgB,EAChB,kBAAkB,EAClB,qBAAqB,EACrB,cAAc,EACd,YAAY,EACZ,eAAe,EACf,aAAa,EACb,eAAe,EACf,mBAAmB,EACnB,OAAO,EACP,oBAAoB,EACpB,aAAa,EACb,kBAAkB,EAClB,gBAAgB,EAChB,mBAAmB,EACnB,qBAAqB,EACrB,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAGvE,OAAO,EACL,wBAAwB,EACxB,qBAAqB,EACrB,eAAe,EACf,mBAAmB,GACpB,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAGxC,OAAO,EACL,mBAAmB,EACnB,aAAa,EACb,sBAAsB,EACtB,aAAa,GACd,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,eAAe,GAChB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,UAAU,EACV,UAAU,EACV,aAAa,EACb,eAAe,EACf,QAAQ,EACR,UAAU,EACV,WAAW,GACZ,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAGpD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,YAAY,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -12,4 +12,6 @@ export { listenForAgentCalls, stopListening, callToolViaPostMessage, isWebMCPEve
|
|
|
12
12
|
export { dispatchAndWait, signalCompletion, sanitizeSchema, createToolGroup, } from './utils.js';
|
|
13
13
|
// WebMCP helpers (skill registry + result builders)
|
|
14
14
|
export { textResult, jsonResult, registerSkill, unregisterSkill, getSkill, listSkills, clearSkills, } from './webmcp-helpers.js';
|
|
15
|
+
// Multi-MCP client
|
|
16
|
+
export { McpMultiClient } from './multi-client.js';
|
|
15
17
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,oCAAoC;AACpC,+DAA+D;AAiD/D,aAAa;AACb,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAGnD,WAAW;AACX,OAAO,EACL,wBAAwB,EACxB,qBAAqB,EACrB,eAAe,EACf,mBAAmB,GACpB,MAAM,eAAe,CAAC;AAEvB,aAAa;AACb,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,qBAAqB;AACrB,OAAO,EACL,mBAAmB,EACnB,aAAa,EACb,sBAAsB,EACtB,aAAa,GACd,MAAM,aAAa,CAAC;AAErB,YAAY;AACZ,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,eAAe,GAChB,MAAM,YAAY,CAAC;AAEpB,oDAAoD;AACpD,OAAO,EACL,UAAU,EACV,UAAU,EACV,aAAa,EACb,eAAe,EACf,QAAQ,EACR,UAAU,EACV,WAAW,GACZ,MAAM,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,oCAAoC;AACpC,+DAA+D;AAiD/D,aAAa;AACb,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAGnD,WAAW;AACX,OAAO,EACL,wBAAwB,EACxB,qBAAqB,EACrB,eAAe,EACf,mBAAmB,GACpB,MAAM,eAAe,CAAC;AAEvB,aAAa;AACb,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,qBAAqB;AACrB,OAAO,EACL,mBAAmB,EACnB,aAAa,EACb,sBAAsB,EACtB,aAAa,GACd,MAAM,aAAa,CAAC;AAErB,YAAY;AACZ,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,eAAe,GAChB,MAAM,YAAY,CAAC;AAEpB,oDAAoD;AACpD,OAAO,EACL,UAAU,EACV,UAAU,EACV,aAAa,EACb,eAAe,EACf,QAAQ,EACR,UAAU,EACV,WAAW,GACZ,MAAM,qBAAqB,CAAC;AAG7B,mBAAmB;AACnB,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { McpTool, McpToolResult } from './types.js';
|
|
2
|
+
export interface ConnectedServer {
|
|
3
|
+
url: string;
|
|
4
|
+
name: string;
|
|
5
|
+
tools: McpTool[];
|
|
6
|
+
}
|
|
7
|
+
export type AggregatedTool = McpTool & {
|
|
8
|
+
serverUrl: string;
|
|
9
|
+
serverName: string;
|
|
10
|
+
};
|
|
11
|
+
export declare class McpMultiClient {
|
|
12
|
+
/** Ordered map — insertion order determines first-match priority */
|
|
13
|
+
private servers;
|
|
14
|
+
/**
|
|
15
|
+
* Add (or reconnect) an MCP server and return its name + tools.
|
|
16
|
+
* If the URL is already registered, the old connection is removed first.
|
|
17
|
+
*/
|
|
18
|
+
addServer(url: string, options?: {
|
|
19
|
+
headers?: Record<string, string>;
|
|
20
|
+
}): Promise<{
|
|
21
|
+
name: string;
|
|
22
|
+
tools: McpTool[];
|
|
23
|
+
}>;
|
|
24
|
+
/**
|
|
25
|
+
* Remove a server and disconnect its client.
|
|
26
|
+
*/
|
|
27
|
+
removeServer(url: string): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* List all connected servers with their metadata.
|
|
30
|
+
*/
|
|
31
|
+
listServers(): ConnectedServer[];
|
|
32
|
+
/**
|
|
33
|
+
* List ALL tools from ALL connected servers.
|
|
34
|
+
* Each tool is augmented with its origin server URL and name.
|
|
35
|
+
* Tools with the same name from different servers are all included.
|
|
36
|
+
*/
|
|
37
|
+
listAllTools(): AggregatedTool[];
|
|
38
|
+
/**
|
|
39
|
+
* Call a tool by name. Automatically routes to the first server (insertion
|
|
40
|
+
* order) that exposes a tool with the given name.
|
|
41
|
+
*/
|
|
42
|
+
callTool(name: string, args?: Record<string, unknown>): Promise<McpToolResult>;
|
|
43
|
+
/**
|
|
44
|
+
* Disconnect from all servers.
|
|
45
|
+
*/
|
|
46
|
+
disconnectAll(): Promise<void>;
|
|
47
|
+
/** Number of connected servers. */
|
|
48
|
+
get serverCount(): number;
|
|
49
|
+
/** True if at least one server is connected. */
|
|
50
|
+
get hasConnections(): boolean;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=multi-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"multi-client.d.ts","sourceRoot":"","sources":["../src/multi-client.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,OAAO,EACP,aAAa,EAEd,MAAM,YAAY,CAAC;AAMpB,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,EAAE,CAAC;CAClB;AAED,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC;AAMjF,qBAAa,cAAc;IACzB,oEAAoE;IACpE,OAAO,CAAC,OAAO,CAA4E;IAM3F;;;OAGG;IACG,SAAS,CACb,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,GAC7C,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,EAAE,CAAA;KAAE,CAAC;IAoB9C;;OAEG;IACG,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAO9C;;OAEG;IACH,WAAW,IAAI,eAAe,EAAE;IAQhC;;;;OAIG;IACH,YAAY,IAAI,cAAc,EAAE;IAUhC;;;OAGG;IACG,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC;IAUpF;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAapC,mCAAmC;IACnC,IAAI,WAAW,IAAI,MAAM,CAExB;IAED,gDAAgD;IAChD,IAAI,cAAc,IAAI,OAAO,CAE5B;CACF"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// @webmcp-auto-ui/core — McpMultiClient
|
|
3
|
+
// Manages multiple simultaneous MCP server connections via McpClient instances.
|
|
4
|
+
// Zero dependencies, SSR-safe.
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
import { McpClient } from './client.js';
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// McpMultiClient
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
export class McpMultiClient {
|
|
11
|
+
/** Ordered map — insertion order determines first-match priority */
|
|
12
|
+
servers = new Map();
|
|
13
|
+
// -------------------------------------------------------------------------
|
|
14
|
+
// Public API
|
|
15
|
+
// -------------------------------------------------------------------------
|
|
16
|
+
/**
|
|
17
|
+
* Add (or reconnect) an MCP server and return its name + tools.
|
|
18
|
+
* If the URL is already registered, the old connection is removed first.
|
|
19
|
+
*/
|
|
20
|
+
async addServer(url, options) {
|
|
21
|
+
// Reconnect semantics: remove existing connection for this URL
|
|
22
|
+
if (this.servers.has(url)) {
|
|
23
|
+
await this.removeServer(url);
|
|
24
|
+
}
|
|
25
|
+
const clientOptions = options?.headers
|
|
26
|
+
? { headers: options.headers }
|
|
27
|
+
: undefined;
|
|
28
|
+
const client = new McpClient(url, clientOptions);
|
|
29
|
+
const initResult = await client.connect();
|
|
30
|
+
const tools = await client.listTools();
|
|
31
|
+
const name = initResult.serverInfo.name;
|
|
32
|
+
this.servers.set(url, { client, name, tools });
|
|
33
|
+
return { name, tools };
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Remove a server and disconnect its client.
|
|
37
|
+
*/
|
|
38
|
+
async removeServer(url) {
|
|
39
|
+
const entry = this.servers.get(url);
|
|
40
|
+
if (!entry)
|
|
41
|
+
return;
|
|
42
|
+
await entry.client.disconnect();
|
|
43
|
+
this.servers.delete(url);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* List all connected servers with their metadata.
|
|
47
|
+
*/
|
|
48
|
+
listServers() {
|
|
49
|
+
const result = [];
|
|
50
|
+
for (const [url, entry] of this.servers) {
|
|
51
|
+
result.push({ url, name: entry.name, tools: entry.tools });
|
|
52
|
+
}
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* List ALL tools from ALL connected servers.
|
|
57
|
+
* Each tool is augmented with its origin server URL and name.
|
|
58
|
+
* Tools with the same name from different servers are all included.
|
|
59
|
+
*/
|
|
60
|
+
listAllTools() {
|
|
61
|
+
const result = [];
|
|
62
|
+
for (const [url, entry] of this.servers) {
|
|
63
|
+
for (const tool of entry.tools) {
|
|
64
|
+
result.push({ ...tool, serverUrl: url, serverName: entry.name });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Call a tool by name. Automatically routes to the first server (insertion
|
|
71
|
+
* order) that exposes a tool with the given name.
|
|
72
|
+
*/
|
|
73
|
+
async callTool(name, args) {
|
|
74
|
+
for (const [, entry] of this.servers) {
|
|
75
|
+
const match = entry.tools.find((t) => t.name === name);
|
|
76
|
+
if (match) {
|
|
77
|
+
return entry.client.callTool(name, args);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
throw new Error(`McpMultiClient: no server exposes tool "${name}"`);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Disconnect from all servers.
|
|
84
|
+
*/
|
|
85
|
+
async disconnectAll() {
|
|
86
|
+
const promises = [];
|
|
87
|
+
for (const [, entry] of this.servers) {
|
|
88
|
+
promises.push(entry.client.disconnect());
|
|
89
|
+
}
|
|
90
|
+
await Promise.all(promises);
|
|
91
|
+
this.servers.clear();
|
|
92
|
+
}
|
|
93
|
+
// -------------------------------------------------------------------------
|
|
94
|
+
// Getters
|
|
95
|
+
// -------------------------------------------------------------------------
|
|
96
|
+
/** Number of connected servers. */
|
|
97
|
+
get serverCount() {
|
|
98
|
+
return this.servers.size;
|
|
99
|
+
}
|
|
100
|
+
/** True if at least one server is connected. */
|
|
101
|
+
get hasConnections() {
|
|
102
|
+
return this.servers.size > 0;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=multi-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"multi-client.js","sourceRoot":"","sources":["../src/multi-client.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// @webmcp-auto-ui/core — McpMultiClient
|
|
3
|
+
// Manages multiple simultaneous MCP server connections via McpClient instances.
|
|
4
|
+
// Zero dependencies, SSR-safe.
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
|
|
7
|
+
import { McpClient } from './client.js';
|
|
8
|
+
import type {
|
|
9
|
+
McpTool,
|
|
10
|
+
McpToolResult,
|
|
11
|
+
McpClientOptions,
|
|
12
|
+
} from './types.js';
|
|
13
|
+
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Types
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
export interface ConnectedServer {
|
|
19
|
+
url: string;
|
|
20
|
+
name: string;
|
|
21
|
+
tools: McpTool[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export type AggregatedTool = McpTool & { serverUrl: string; serverName: string };
|
|
25
|
+
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
// McpMultiClient
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
|
|
30
|
+
export class McpMultiClient {
|
|
31
|
+
/** Ordered map — insertion order determines first-match priority */
|
|
32
|
+
private servers = new Map<string, { client: McpClient; name: string; tools: McpTool[] }>();
|
|
33
|
+
|
|
34
|
+
// -------------------------------------------------------------------------
|
|
35
|
+
// Public API
|
|
36
|
+
// -------------------------------------------------------------------------
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Add (or reconnect) an MCP server and return its name + tools.
|
|
40
|
+
* If the URL is already registered, the old connection is removed first.
|
|
41
|
+
*/
|
|
42
|
+
async addServer(
|
|
43
|
+
url: string,
|
|
44
|
+
options?: { headers?: Record<string, string> },
|
|
45
|
+
): Promise<{ name: string; tools: McpTool[] }> {
|
|
46
|
+
// Reconnect semantics: remove existing connection for this URL
|
|
47
|
+
if (this.servers.has(url)) {
|
|
48
|
+
await this.removeServer(url);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const clientOptions: McpClientOptions | undefined = options?.headers
|
|
52
|
+
? { headers: options.headers }
|
|
53
|
+
: undefined;
|
|
54
|
+
|
|
55
|
+
const client = new McpClient(url, clientOptions);
|
|
56
|
+
const initResult = await client.connect();
|
|
57
|
+
const tools = await client.listTools();
|
|
58
|
+
|
|
59
|
+
const name = initResult.serverInfo.name;
|
|
60
|
+
this.servers.set(url, { client, name, tools });
|
|
61
|
+
|
|
62
|
+
return { name, tools };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Remove a server and disconnect its client.
|
|
67
|
+
*/
|
|
68
|
+
async removeServer(url: string): Promise<void> {
|
|
69
|
+
const entry = this.servers.get(url);
|
|
70
|
+
if (!entry) return;
|
|
71
|
+
await entry.client.disconnect();
|
|
72
|
+
this.servers.delete(url);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* List all connected servers with their metadata.
|
|
77
|
+
*/
|
|
78
|
+
listServers(): ConnectedServer[] {
|
|
79
|
+
const result: ConnectedServer[] = [];
|
|
80
|
+
for (const [url, entry] of this.servers) {
|
|
81
|
+
result.push({ url, name: entry.name, tools: entry.tools });
|
|
82
|
+
}
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* List ALL tools from ALL connected servers.
|
|
88
|
+
* Each tool is augmented with its origin server URL and name.
|
|
89
|
+
* Tools with the same name from different servers are all included.
|
|
90
|
+
*/
|
|
91
|
+
listAllTools(): AggregatedTool[] {
|
|
92
|
+
const result: AggregatedTool[] = [];
|
|
93
|
+
for (const [url, entry] of this.servers) {
|
|
94
|
+
for (const tool of entry.tools) {
|
|
95
|
+
result.push({ ...tool, serverUrl: url, serverName: entry.name });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return result;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Call a tool by name. Automatically routes to the first server (insertion
|
|
103
|
+
* order) that exposes a tool with the given name.
|
|
104
|
+
*/
|
|
105
|
+
async callTool(name: string, args?: Record<string, unknown>): Promise<McpToolResult> {
|
|
106
|
+
for (const [, entry] of this.servers) {
|
|
107
|
+
const match = entry.tools.find((t) => t.name === name);
|
|
108
|
+
if (match) {
|
|
109
|
+
return entry.client.callTool(name, args);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
throw new Error(`McpMultiClient: no server exposes tool "${name}"`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Disconnect from all servers.
|
|
117
|
+
*/
|
|
118
|
+
async disconnectAll(): Promise<void> {
|
|
119
|
+
const promises: Promise<void>[] = [];
|
|
120
|
+
for (const [, entry] of this.servers) {
|
|
121
|
+
promises.push(entry.client.disconnect());
|
|
122
|
+
}
|
|
123
|
+
await Promise.all(promises);
|
|
124
|
+
this.servers.clear();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// -------------------------------------------------------------------------
|
|
128
|
+
// Getters
|
|
129
|
+
// -------------------------------------------------------------------------
|
|
130
|
+
|
|
131
|
+
/** Number of connected servers. */
|
|
132
|
+
get serverCount(): number {
|
|
133
|
+
return this.servers.size;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** True if at least one server is connected. */
|
|
137
|
+
get hasConnections(): boolean {
|
|
138
|
+
return this.servers.size > 0;
|
|
139
|
+
}
|
|
140
|
+
}
|