@cyanmycelium/mcp-broker 0.4.0 → 1.2.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.
Files changed (151) hide show
  1. package/.mcp-broker.example/CONFIGURATION-EN.md +861 -0
  2. package/.mcp-broker.example/CONFIGURATION-FR.md +871 -0
  3. package/.mcp-broker.example/README.md +8 -3
  4. package/.mcp-broker.example/config.json +86 -0
  5. package/README.md +122 -3
  6. package/dist/bin.d.ts +0 -1
  7. package/dist/bin.js +180 -208
  8. package/dist/bin.js.map +1 -1
  9. package/dist/chunk-FTDKH2C4.js +3670 -0
  10. package/dist/chunk-FTDKH2C4.js.map +1 -0
  11. package/dist/{broker/grammars → grammars}/claude/en.json +1 -1
  12. package/dist/{broker/grammars → grammars}/claude/fr.json +1 -1
  13. package/dist/index.d.ts +1790 -18
  14. package/dist/index.js +2 -14
  15. package/dist/index.js.map +1 -1
  16. package/package.json +14 -8
  17. package/scripts/copy-assets.mjs +16 -10
  18. package/scripts/gen-cert.mjs +5 -5
  19. package/scripts/pack-mcpb.mjs +5 -5
  20. package/scripts/sign-bundle.mjs +6 -6
  21. package/src/auth/auth.config.ts +98 -0
  22. package/src/auth/auth.types.ts +120 -0
  23. package/src/auth/http.auth.ts +128 -0
  24. package/src/auth/index.ts +34 -0
  25. package/src/auth/jwt.validator.ts +63 -0
  26. package/src/auth/provider.auth.ts +114 -0
  27. package/src/auth/resource.metadata.ts +34 -0
  28. package/src/authorization/audit.ts +35 -0
  29. package/src/authorization/capability.classifier.ts +114 -0
  30. package/src/authorization/index.ts +37 -0
  31. package/src/authorization/policy.engine.ts +212 -0
  32. package/src/authorization/policy.types.ts +105 -0
  33. package/src/authorization/resource.path.ts +126 -0
  34. package/src/authorization/runtime.ts +56 -0
  35. package/src/authorization/slot.resource.ts +50 -0
  36. package/src/authorization/subject.mapper.ts +81 -0
  37. package/src/bin.ts +90 -7
  38. package/src/broker/adapters/broker.adapter.info.ts +3 -3
  39. package/src/broker/adapters/broker.adapter.providers.ts +3 -3
  40. package/src/broker/aggregate/aggregate.catalog.ts +49 -21
  41. package/src/broker/aggregate/aggregate.server.ts +149 -19
  42. package/src/broker/aggregate/provider.client.session.ts +22 -22
  43. package/src/broker/behaviors/broker.behavior.info.ts +4 -4
  44. package/src/broker/behaviors/broker.behavior.providers.ts +6 -6
  45. package/src/broker/broker.context.ts +10 -4
  46. package/src/broker/broker.grammars.ts +16 -13
  47. package/src/broker/broker.server.ts +12 -9
  48. package/src/broker/grammars/claude/en.json +1 -1
  49. package/src/broker/grammars/claude/fr.json +1 -1
  50. package/src/broker/index.ts +9 -9
  51. package/src/config.ts +81 -8
  52. package/src/index.ts +121 -20
  53. package/src/{mcpb.loader.ts → mcpb/mcpb.loader.ts} +24 -21
  54. package/src/{mcpb.unzip.ts → mcpb/mcpb.unzip.ts} +2 -2
  55. package/src/remote.transports.ts +11 -8
  56. package/src/remote.upstream.ts +11 -8
  57. package/src/stdio.upstream.ts +9 -6
  58. package/src/upstream.ts +6 -3
  59. package/src/ws/ws.interfaces.ts +357 -0
  60. package/src/{ws.tunnel.builder.ts → ws/ws.tunnel.builder.ts} +112 -9
  61. package/src/{ws.tunnel.ts → ws/ws.tunnel.ts} +646 -457
  62. package/web/README.md +94 -0
  63. package/web/assets/logo.png +0 -0
  64. package/web/broker-self-mcp.html +338 -0
  65. package/web/css/styles.css +580 -0
  66. package/web/demos/DemoPlaceholder.html +258 -0
  67. package/web/demos/broker-explorer/css/app.css +393 -0
  68. package/web/demos/broker-explorer/index.html +94 -0
  69. package/web/demos/broker-explorer/js/app.js +271 -0
  70. package/web/demos/broker-explorer/js/mcp-ws-client.js +132 -0
  71. package/web/demos/oauth-lab/README.md +94 -0
  72. package/web/demos/oauth-lab/config.json +117 -0
  73. package/web/demos/oauth-lab/css/app.css +1097 -0
  74. package/web/demos/oauth-lab/index.html +323 -0
  75. package/web/demos/oauth-lab/js/app.js +654 -0
  76. package/web/demos/oauth-lab/server/auth-server.mjs +426 -0
  77. package/web/demos/oauth-lab/server/factory-provider.mjs +269 -0
  78. package/web/demos/oauth-lab/server/smoke-test.mjs +308 -0
  79. package/web/demos/oauth-lab/server/start.mjs +106 -0
  80. package/web/demos/provider-tunnel/css/app.css +384 -0
  81. package/web/demos/provider-tunnel/index.html +99 -0
  82. package/web/demos/provider-tunnel/js/app.js +226 -0
  83. package/web/demos/provider-tunnel/js/toolbox-server.js +186 -0
  84. package/web/index.html +558 -0
  85. package/web/js/lib/broker-tunnel.js +173 -0
  86. package/dist/broker/adapters/broker.adapter.info.d.ts +0 -16
  87. package/dist/broker/adapters/broker.adapter.info.js +0 -43
  88. package/dist/broker/adapters/broker.adapter.info.js.map +0 -1
  89. package/dist/broker/adapters/broker.adapter.providers.d.ts +0 -18
  90. package/dist/broker/adapters/broker.adapter.providers.js +0 -61
  91. package/dist/broker/adapters/broker.adapter.providers.js.map +0 -1
  92. package/dist/broker/aggregate/aggregate.catalog.d.ts +0 -54
  93. package/dist/broker/aggregate/aggregate.catalog.js +0 -105
  94. package/dist/broker/aggregate/aggregate.catalog.js.map +0 -1
  95. package/dist/broker/aggregate/aggregate.server.d.ts +0 -47
  96. package/dist/broker/aggregate/aggregate.server.js +0 -151
  97. package/dist/broker/aggregate/aggregate.server.js.map +0 -1
  98. package/dist/broker/aggregate/provider.client.session.d.ts +0 -52
  99. package/dist/broker/aggregate/provider.client.session.js +0 -140
  100. package/dist/broker/aggregate/provider.client.session.js.map +0 -1
  101. package/dist/broker/behaviors/broker.behavior.info.d.ts +0 -15
  102. package/dist/broker/behaviors/broker.behavior.info.js +0 -41
  103. package/dist/broker/behaviors/broker.behavior.info.js.map +0 -1
  104. package/dist/broker/behaviors/broker.behavior.providers.d.ts +0 -19
  105. package/dist/broker/behaviors/broker.behavior.providers.js +0 -69
  106. package/dist/broker/behaviors/broker.behavior.providers.js.map +0 -1
  107. package/dist/broker/broker.context.d.ts +0 -59
  108. package/dist/broker/broker.context.js +0 -2
  109. package/dist/broker/broker.context.js.map +0 -1
  110. package/dist/broker/broker.grammars.d.ts +0 -130
  111. package/dist/broker/broker.grammars.js +0 -229
  112. package/dist/broker/broker.grammars.js.map +0 -1
  113. package/dist/broker/broker.server.d.ts +0 -66
  114. package/dist/broker/broker.server.js +0 -73
  115. package/dist/broker/broker.server.js.map +0 -1
  116. package/dist/broker/index.d.ts +0 -9
  117. package/dist/broker/index.js +0 -7
  118. package/dist/broker/index.js.map +0 -1
  119. package/dist/config.d.ts +0 -136
  120. package/dist/config.js +0 -61
  121. package/dist/config.js.map +0 -1
  122. package/dist/mcpb.loader.d.ts +0 -24
  123. package/dist/mcpb.loader.js +0 -161
  124. package/dist/mcpb.loader.js.map +0 -1
  125. package/dist/mcpb.unzip.d.ts +0 -6
  126. package/dist/mcpb.unzip.js +0 -95
  127. package/dist/mcpb.unzip.js.map +0 -1
  128. package/dist/remote.transports.d.ts +0 -16
  129. package/dist/remote.transports.js +0 -297
  130. package/dist/remote.transports.js.map +0 -1
  131. package/dist/remote.upstream.d.ts +0 -36
  132. package/dist/remote.upstream.js +0 -52
  133. package/dist/remote.upstream.js.map +0 -1
  134. package/dist/stdio.upstream.d.ts +0 -45
  135. package/dist/stdio.upstream.js +0 -85
  136. package/dist/stdio.upstream.js.map +0 -1
  137. package/dist/upstream.d.ts +0 -33
  138. package/dist/upstream.js +0 -2
  139. package/dist/upstream.js.map +0 -1
  140. package/dist/version.d.ts +0 -2
  141. package/dist/version.js +0 -9
  142. package/dist/version.js.map +0 -1
  143. package/dist/ws.tunnel.builder.d.ts +0 -139
  144. package/dist/ws.tunnel.builder.js +0 -205
  145. package/dist/ws.tunnel.builder.js.map +0 -1
  146. package/dist/ws.tunnel.d.ts +0 -373
  147. package/dist/ws.tunnel.js +0 -1090
  148. package/dist/ws.tunnel.js.map +0 -1
  149. /package/dist/{broker/grammars → grammars}/default/en.json +0 -0
  150. /package/dist/{broker/grammars → grammars}/default/fr.json +0 -0
  151. /package/dist/{broker/grammars → grammars}/default/zh.json +0 -0
@@ -0,0 +1,173 @@
1
+ /**
2
+ * broker-tunnel.js, connect an MCP server to @cyanmycelium/mcp-broker.
3
+ *
4
+ * This is the ONLY broker-specific code you need to tunnel an MCP server
5
+ * (browser-side or anywhere a WebSocket is available) to the broker. Copy
6
+ * this file as-is; it has zero dependencies.
7
+ *
8
+ * The broker's provider model is "the server dials out": an MCP server opens
9
+ * an outbound WebSocket to `/provider/<name>` and the broker relays every MCP
10
+ * client that targets that slot. No shipped MCP SDK transport covers that
11
+ * direction, so `BrokerTunnelTransport` below is a thin shim implementing the
12
+ * SDK's structural `Transport` interface (start / send / close / onmessage /
13
+ * onclose / onerror) over a browser `WebSocket`. The MCP protocol itself is
14
+ * untouched: any standard MCP server works through it.
15
+ *
16
+ * @example
17
+ * import { Server } from "https://esm.sh/@modelcontextprotocol/sdk@1.12.3/server/index.js";
18
+ * import { BrokerTunnelTransport } from "./lib/broker-tunnel.js";
19
+ *
20
+ * const server = new Server({ name: "my-provider", version: "1.0.0" },
21
+ * { capabilities: { tools: {} } });
22
+ * // server.setRequestHandler(...), your tools and resources
23
+ *
24
+ * const transport = new BrokerTunnelTransport("ws://localhost:3000/provider/my-provider");
25
+ * transport.onTunnelClose = (code, reason) =>
26
+ * console.log(describeTunnelClose(code, reason));
27
+ * await server.connect(transport);
28
+ */
29
+
30
+ /**
31
+ * MCP SDK transport backed by an outbound WebSocket to the broker.
32
+ *
33
+ * The MCP SDK owns `onmessage` / `onclose` / `onerror` (it assigns them inside
34
+ * `Server.connect`). The page observes the raw socket through the separate
35
+ * `onTunnelOpen` / `onTunnelClose` / `onTunnelError` hooks, so application code
36
+ * and the SDK never overwrite each other's callbacks.
37
+ */
38
+ export class BrokerTunnelTransport {
39
+ /**
40
+ * @param {string} url Full provider URL, e.g. `ws://host:3000/provider/<name>`.
41
+ * @param {{ aggregate?: boolean }} [options] When `aggregate` is true, the
42
+ * transport sends a broker registration frame on open so this server
43
+ * joins the broker's `_all` aggregate slot.
44
+ */
45
+ constructor(url, options = {}) {
46
+ this.url = url;
47
+ this._ws = null;
48
+ this._aggregate = options.aggregate === true;
49
+
50
+ // Set by the MCP SDK inside Server.connect(), do not assign these yourself.
51
+ this.onmessage = null;
52
+ this.onclose = null;
53
+ this.onerror = null;
54
+
55
+ // Page-owned observation hooks, assign these before Server.connect().
56
+ /** @type {null | (() => void)} */
57
+ this.onTunnelOpen = null;
58
+ /** @type {null | ((code: number, reason: string) => void)} */
59
+ this.onTunnelClose = null;
60
+ /** @type {null | ((error: Error) => void)} */
61
+ this.onTunnelError = null;
62
+
63
+ // Last close code/reason, useful for diagnostics after the fact.
64
+ this.closeCode = null;
65
+ this.closeReason = "";
66
+ }
67
+
68
+ /**
69
+ * Opens the WebSocket. Called by `Server.connect()`. The returned promise
70
+ * resolves once the broker accepts the WebSocket upgrade.
71
+ *
72
+ * Note: a successful upgrade is not yet a usable slot: the broker may
73
+ * still reject the slot at the application layer with a 1008 close (name
74
+ * already taken, or reserved) a few milliseconds later. Watch
75
+ * `onTunnelClose` for that case.
76
+ *
77
+ * @returns {Promise<void>}
78
+ */
79
+ start() {
80
+ return new Promise((resolve, reject) => {
81
+ let opened = false;
82
+ const ws = new WebSocket(this.url);
83
+ this._ws = ws;
84
+
85
+ ws.onopen = () => {
86
+ opened = true;
87
+ // Optional broker registration frame: it MUST precede any MCP
88
+ // traffic, so it is sent here before start() resolves.
89
+ if (this._aggregate) {
90
+ ws.send(JSON.stringify({ type: "register", aggregate: true }));
91
+ }
92
+ this.onTunnelOpen?.();
93
+ resolve();
94
+ };
95
+
96
+ ws.onerror = () => {
97
+ const err = new Error(`WebSocket error on ${this.url}`);
98
+ this.onTunnelError?.(err);
99
+ this.onerror?.(err);
100
+ // Reject start() only if the socket never opened: a post-open
101
+ // error is a runtime failure, not a connect failure.
102
+ if (!opened) reject(err);
103
+ };
104
+
105
+ ws.onclose = (ev) => {
106
+ this.closeCode = ev.code;
107
+ this.closeReason = ev.reason;
108
+ this.onTunnelClose?.(ev.code, ev.reason);
109
+ this.onclose?.();
110
+ };
111
+
112
+ ws.onmessage = (ev) => {
113
+ let parsed;
114
+ try {
115
+ parsed = JSON.parse(typeof ev.data === "string" ? ev.data : "");
116
+ } catch {
117
+ return; // not JSON, drop
118
+ }
119
+ this.onmessage?.(parsed);
120
+ };
121
+ });
122
+ }
123
+
124
+ /**
125
+ * Sends one JSON-RPC message. Called by the MCP SDK.
126
+ * @param {object} message
127
+ * @returns {Promise<void>}
128
+ */
129
+ send(message) {
130
+ if (this._ws && this._ws.readyState === WebSocket.OPEN) {
131
+ this._ws.send(JSON.stringify(message));
132
+ }
133
+ return Promise.resolve();
134
+ }
135
+
136
+ /**
137
+ * Closes the WebSocket. Called by `Server.close()`.
138
+ * @returns {Promise<void>}
139
+ */
140
+ close() {
141
+ this._ws?.close();
142
+ return Promise.resolve();
143
+ }
144
+ }
145
+
146
+ /** WebSocket close code the broker uses to reject a provider slot. */
147
+ export const BROKER_REJECT_CODE = 1008;
148
+
149
+ /**
150
+ * Turns a tunnel WebSocket close into a human-readable diagnosis: the
151
+ * "handshake check". Three cases matter when tunnelling to the broker:
152
+ *
153
+ * - 1008 → the broker rejected the slot (name already connected, or reserved).
154
+ * - closed before the tunnel was ever up → the broker is unreachable.
155
+ * - any other close while up → the tunnel dropped unexpectedly.
156
+ *
157
+ * @param {number} code WebSocket close code.
158
+ * @param {string} reason WebSocket close reason (may be empty).
159
+ * @param {boolean} wasOpen Whether the tunnel had reached a usable state.
160
+ * @returns {{ level: "error" | "warn", message: string }}
161
+ */
162
+ export function describeTunnelClose(code, reason, wasOpen) {
163
+ if (code === BROKER_REJECT_CODE) {
164
+ return { level: "error", message: `Broker rejected the provider slot: ${reason || "policy violation (1008)"}` };
165
+ }
166
+ if (!wasOpen) {
167
+ return { level: "error", message: "Broker unreachable: the WebSocket closed before the tunnel opened." };
168
+ }
169
+ if (code && code !== 1000 && code !== 1005) {
170
+ return { level: "warn", message: `Tunnel closed unexpectedly (code ${code}${reason ? ", " + reason : ""}).` };
171
+ }
172
+ return { level: "warn", message: "Tunnel closed." };
173
+ }
@@ -1,16 +0,0 @@
1
- import { McpAdapterBase } from "@cyanmycelium/mcp-core";
2
- import type { McpResourceContent, McpToolResult } from "@cyanmycelium/mcp-core";
3
- import type { BrokerContext } from "../broker.context.js";
4
- /** URI of the static resource backing the `broker_info` snapshot. */
5
- export declare const BROKER_INFO_URI = "broker://info";
6
- /**
7
- * Read-only adapter that produces a snapshot of the broker's identity and
8
- * configuration. Backed by the live {@link BrokerContext}.
9
- */
10
- export declare class BrokerInfoAdapter extends McpAdapterBase {
11
- private readonly _context;
12
- constructor(_context: BrokerContext);
13
- readResourceAsync(uri: string): Promise<McpResourceContent | undefined>;
14
- executeToolAsync(_uri: string, toolName: string, _args: Record<string, unknown>): Promise<McpToolResult>;
15
- private _snapshot;
16
- }
@@ -1,43 +0,0 @@
1
- import { McpAdapterBase, McpToolResults } from "@cyanmycelium/mcp-core";
2
- /** URI of the static resource backing the `broker_info` snapshot. */
3
- export const BROKER_INFO_URI = "broker://info";
4
- /**
5
- * Read-only adapter that produces a snapshot of the broker's identity and
6
- * configuration. Backed by the live {@link BrokerContext}.
7
- */
8
- export class BrokerInfoAdapter extends McpAdapterBase {
9
- _context;
10
- constructor(_context) {
11
- super("broker");
12
- this._context = _context;
13
- }
14
- async readResourceAsync(uri) {
15
- if (uri !== BROKER_INFO_URI)
16
- return undefined;
17
- return {
18
- uri,
19
- mimeType: "application/json",
20
- text: JSON.stringify(this._snapshot()),
21
- };
22
- }
23
- async executeToolAsync(_uri, toolName, _args) {
24
- if (toolName !== "broker_info") {
25
- return McpToolResults.error(`Unknown tool: ${toolName}`);
26
- }
27
- return McpToolResults.json(this._snapshot());
28
- }
29
- _snapshot() {
30
- const c = this._context;
31
- return {
32
- name: c.name,
33
- version: c.version,
34
- startedAt: c.startedAt?.toISOString() ?? null,
35
- uptimeSeconds: c.uptimeSeconds,
36
- host: c.host ?? "0.0.0.0",
37
- port: c.port,
38
- tls: c.tls,
39
- paths: c.paths,
40
- };
41
- }
42
- }
43
- //# sourceMappingURL=broker.adapter.info.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"broker.adapter.info.js","sourceRoot":"","sources":["../../../src/broker/adapters/broker.adapter.info.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAIxE,qEAAqE;AACrE,MAAM,CAAC,MAAM,eAAe,GAAG,eAAe,CAAC;AAE/C;;;GAGG;AACH,MAAM,OAAO,iBAAkB,SAAQ,cAAc;IACpB;IAA7B,YAA6B,QAAuB;QAChD,KAAK,CAAC,QAAQ,CAAC,CAAC;QADS,aAAQ,GAAR,QAAQ,CAAe;IAEpD,CAAC;IAEM,KAAK,CAAC,iBAAiB,CAAC,GAAW;QACtC,IAAI,GAAG,KAAK,eAAe;YAAE,OAAO,SAAS,CAAC;QAC9C,OAAO;YACH,GAAG;YACH,QAAQ,EAAE,kBAAkB;YAC5B,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;SACzC,CAAC;IACN,CAAC;IAEM,KAAK,CAAC,gBAAgB,CAAC,IAAY,EAAE,QAAgB,EAAE,KAA8B;QACxF,IAAI,QAAQ,KAAK,aAAa,EAAE,CAAC;YAC7B,OAAO,cAAc,CAAC,KAAK,CAAC,iBAAiB,QAAQ,EAAE,CAAC,CAAC;QAC7D,CAAC;QACD,OAAO,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;IACjD,CAAC;IAEO,SAAS;QACb,MAAM,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC;QACxB,OAAO;YACH,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,OAAO,EAAE,CAAC,CAAC,OAAO;YAClB,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,WAAW,EAAE,IAAI,IAAI;YAC7C,aAAa,EAAE,CAAC,CAAC,aAAa;YAC9B,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,SAAS;YACzB,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,GAAG,EAAE,CAAC,CAAC,GAAG;YACV,KAAK,EAAE,CAAC,CAAC,KAAK;SACjB,CAAC;IACN,CAAC;CACJ"}
@@ -1,18 +0,0 @@
1
- import { McpAdapterBase } from "@cyanmycelium/mcp-core";
2
- import type { McpResourceContent, McpToolResult } from "@cyanmycelium/mcp-core";
3
- import type { BrokerContext } from "../broker.context.js";
4
- /** URI of the static resource that lists every provider slot. */
5
- export declare const PROVIDERS_URI = "broker://providers";
6
- /** RFC 6570 URI template for one specific provider slot. */
7
- export declare const PROVIDER_URI_TEMPLATE = "broker://providers/{name}";
8
- /**
9
- * Adapter that exposes the broker's current provider slots, both as a list
10
- * (read of `broker://providers`) and individually (`broker://providers/<name>`).
11
- */
12
- export declare class BrokerProvidersAdapter extends McpAdapterBase {
13
- private readonly _context;
14
- constructor(_context: BrokerContext);
15
- readResourceAsync(uri: string): Promise<McpResourceContent | undefined>;
16
- executeToolAsync(_uri: string, toolName: string, args: Record<string, unknown>): Promise<McpToolResult>;
17
- private _listSnapshot;
18
- }
@@ -1,61 +0,0 @@
1
- import { McpAdapterBase, McpToolResults } from "@cyanmycelium/mcp-core";
2
- /** URI of the static resource that lists every provider slot. */
3
- export const PROVIDERS_URI = "broker://providers";
4
- /** RFC 6570 URI template for one specific provider slot. */
5
- export const PROVIDER_URI_TEMPLATE = "broker://providers/{name}";
6
- /**
7
- * Adapter that exposes the broker's current provider slots, both as a list
8
- * (read of `broker://providers`) and individually (`broker://providers/<name>`).
9
- */
10
- export class BrokerProvidersAdapter extends McpAdapterBase {
11
- _context;
12
- constructor(_context) {
13
- super("broker");
14
- this._context = _context;
15
- }
16
- async readResourceAsync(uri) {
17
- if (uri === PROVIDERS_URI) {
18
- return {
19
- uri,
20
- mimeType: "application/json",
21
- text: JSON.stringify(this._listSnapshot()),
22
- };
23
- }
24
- // broker://providers/<name>
25
- const match = /^broker:\/\/providers\/([^/]+)$/.exec(uri);
26
- if (match) {
27
- const name = decodeURIComponent(match[1]);
28
- const info = this._context.getProviderInfo(name);
29
- if (!info)
30
- return undefined;
31
- return {
32
- uri,
33
- mimeType: "application/json",
34
- text: JSON.stringify(info),
35
- };
36
- }
37
- return undefined;
38
- }
39
- async executeToolAsync(_uri, toolName, args) {
40
- switch (toolName) {
41
- case "providers_list":
42
- return McpToolResults.json(this._listSnapshot());
43
- case "provider_status": {
44
- const name = typeof args.name === "string" ? args.name : "";
45
- if (!name)
46
- return McpToolResults.error('Missing required argument: "name" (string).');
47
- const info = this._context.getProviderInfo(name);
48
- if (!info)
49
- return McpToolResults.error(`Unknown provider: "${name}".`);
50
- return McpToolResults.json(info);
51
- }
52
- default:
53
- return McpToolResults.error(`Unknown tool: ${toolName}`);
54
- }
55
- }
56
- _listSnapshot() {
57
- const providers = this._context.getProvidersInfo();
58
- return { count: providers.length, providers };
59
- }
60
- }
61
- //# sourceMappingURL=broker.adapter.providers.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"broker.adapter.providers.js","sourceRoot":"","sources":["../../../src/broker/adapters/broker.adapter.providers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAIxE,iEAAiE;AACjE,MAAM,CAAC,MAAM,aAAa,GAAG,oBAAoB,CAAC;AAElD,4DAA4D;AAC5D,MAAM,CAAC,MAAM,qBAAqB,GAAG,2BAA2B,CAAC;AAEjE;;;GAGG;AACH,MAAM,OAAO,sBAAuB,SAAQ,cAAc;IACzB;IAA7B,YAA6B,QAAuB;QAChD,KAAK,CAAC,QAAQ,CAAC,CAAC;QADS,aAAQ,GAAR,QAAQ,CAAe;IAEpD,CAAC;IAEM,KAAK,CAAC,iBAAiB,CAAC,GAAW;QACtC,IAAI,GAAG,KAAK,aAAa,EAAE,CAAC;YACxB,OAAO;gBACH,GAAG;gBACH,QAAQ,EAAE,kBAAkB;gBAC5B,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;aAC7C,CAAC;QACN,CAAC;QAED,4BAA4B;QAC5B,MAAM,KAAK,GAAG,iCAAiC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC1D,IAAI,KAAK,EAAE,CAAC;YACR,MAAM,IAAI,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YAC1C,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;YACjD,IAAI,CAAC,IAAI;gBAAE,OAAO,SAAS,CAAC;YAC5B,OAAO;gBACH,GAAG;gBACH,QAAQ,EAAE,kBAAkB;gBAC5B,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;aAC7B,CAAC;QACN,CAAC;QAED,OAAO,SAAS,CAAC;IACrB,CAAC;IAEM,KAAK,CAAC,gBAAgB,CAAC,IAAY,EAAE,QAAgB,EAAE,IAA6B;QACvF,QAAQ,QAAQ,EAAE,CAAC;YACf,KAAK,gBAAgB;gBACjB,OAAO,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC;YAErD,KAAK,iBAAiB,CAAC,CAAC,CAAC;gBACrB,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC5D,IAAI,CAAC,IAAI;oBAAE,OAAO,cAAc,CAAC,KAAK,CAAC,6CAA6C,CAAC,CAAC;gBACtF,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;gBACjD,IAAI,CAAC,IAAI;oBAAE,OAAO,cAAc,CAAC,KAAK,CAAC,sBAAsB,IAAI,IAAI,CAAC,CAAC;gBACvE,OAAO,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACrC,CAAC;YAED;gBACI,OAAO,cAAc,CAAC,KAAK,CAAC,iBAAiB,QAAQ,EAAE,CAAC,CAAC;QACjE,CAAC;IACL,CAAC;IAEO,aAAa;QACjB,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,gBAAgB,EAAE,CAAC;QACnD,OAAO,EAAE,KAAK,EAAE,SAAS,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC;IAClD,CAAC;CACJ"}
@@ -1,54 +0,0 @@
1
- /**
2
- * AggregateCatalog — merges the tool and prompt catalogs of several providers
3
- * into a single namespaced view, and routes aggregated names back to their
4
- * origin provider.
5
- *
6
- * Aggregated names use the `<provider>-<original>` shape. Routing never parses
7
- * that string back: a forward lookup table is the single source of truth, so
8
- * the `-` separator stays safe even when provider or tool names contain dashes.
9
- */
10
- /** Minimal shape of an MCP tool entry. Extra fields are preserved as-is. */
11
- export interface CatalogTool {
12
- name: string;
13
- description?: string;
14
- [key: string]: unknown;
15
- }
16
- /** Minimal shape of an MCP prompt entry. Extra fields are preserved as-is. */
17
- export interface CatalogPrompt {
18
- name: string;
19
- title?: string;
20
- description?: string;
21
- [key: string]: unknown;
22
- }
23
- /** One provider's raw contribution to the aggregate. */
24
- export interface ProviderEntry {
25
- tools: CatalogTool[];
26
- prompts: CatalogPrompt[];
27
- }
28
- /** Where an aggregated name routes back to. */
29
- export interface Route {
30
- provider: string;
31
- original: string;
32
- }
33
- export declare class AggregateCatalog {
34
- private readonly _providers;
35
- private _tools;
36
- private _prompts;
37
- private _toolRoutes;
38
- private _promptRoutes;
39
- /** Number of providers currently contributing to the aggregate. */
40
- get providerCount(): number;
41
- /** The merged, namespaced tool list. */
42
- get tools(): CatalogTool[];
43
- /** The merged, namespaced prompt list. */
44
- get prompts(): CatalogPrompt[];
45
- /** Adds or replaces a provider's contribution, then rebuilds the merged view. */
46
- setProvider(provider: string, entry: ProviderEntry): void;
47
- /** Drops a provider from the aggregate, then rebuilds. */
48
- removeProvider(provider: string): void;
49
- /** Resolves an aggregated tool name to its origin, or `undefined` if unknown. */
50
- resolveTool(name: string): Route | undefined;
51
- /** Resolves an aggregated prompt name to its origin, or `undefined` if unknown. */
52
- resolvePrompt(name: string): Route | undefined;
53
- private _rebuild;
54
- }
@@ -1,105 +0,0 @@
1
- /**
2
- * AggregateCatalog — merges the tool and prompt catalogs of several providers
3
- * into a single namespaced view, and routes aggregated names back to their
4
- * origin provider.
5
- *
6
- * Aggregated names use the `<provider>-<original>` shape. Routing never parses
7
- * that string back: a forward lookup table is the single source of truth, so
8
- * the `-` separator stays safe even when provider or tool names contain dashes.
9
- */
10
- /** Separator between the provider prefix and the original name. */
11
- const SEPARATOR = "-";
12
- /** Anthropic API tool-name limit; aggregated tool names must fit inside it. */
13
- const MAX_TOOL_NAME_LENGTH = 64;
14
- export class AggregateCatalog {
15
- _providers = new Map();
16
- _tools = [];
17
- _prompts = [];
18
- _toolRoutes = new Map();
19
- _promptRoutes = new Map();
20
- /** Number of providers currently contributing to the aggregate. */
21
- get providerCount() {
22
- return this._providers.size;
23
- }
24
- /** The merged, namespaced tool list. */
25
- get tools() {
26
- return this._tools;
27
- }
28
- /** The merged, namespaced prompt list. */
29
- get prompts() {
30
- return this._prompts;
31
- }
32
- /** Adds or replaces a provider's contribution, then rebuilds the merged view. */
33
- setProvider(provider, entry) {
34
- this._providers.set(provider, entry);
35
- this._rebuild();
36
- }
37
- /** Drops a provider from the aggregate, then rebuilds. */
38
- removeProvider(provider) {
39
- if (this._providers.delete(provider))
40
- this._rebuild();
41
- }
42
- /** Resolves an aggregated tool name to its origin, or `undefined` if unknown. */
43
- resolveTool(name) {
44
- return this._toolRoutes.get(name);
45
- }
46
- /** Resolves an aggregated prompt name to its origin, or `undefined` if unknown. */
47
- resolvePrompt(name) {
48
- return this._promptRoutes.get(name);
49
- }
50
- _rebuild() {
51
- const tools = [];
52
- const prompts = [];
53
- const toolRoutes = new Map();
54
- const promptRoutes = new Map();
55
- for (const [provider, entry] of this._providers) {
56
- for (const tool of entry.tools) {
57
- const name = unique(capToolName(prefixed(provider, tool.name)), toolRoutes);
58
- toolRoutes.set(name, { provider, original: tool.name });
59
- tools.push({ ...tool, name, description: tag(provider, tool.description) });
60
- }
61
- for (const prompt of entry.prompts) {
62
- const name = unique(prefixed(provider, prompt.name), promptRoutes);
63
- promptRoutes.set(name, { provider, original: prompt.name });
64
- prompts.push({ ...prompt, name, description: tag(provider, prompt.description) });
65
- }
66
- }
67
- this._tools = tools;
68
- this._prompts = prompts;
69
- this._toolRoutes = toolRoutes;
70
- this._promptRoutes = promptRoutes;
71
- }
72
- }
73
- function prefixed(provider, original) {
74
- return `${provider}${SEPARATOR}${original}`;
75
- }
76
- /** Tags a description with the origin provider so the model and user see the source. */
77
- function tag(provider, description) {
78
- const label = `[${provider}]`;
79
- return description ? `${label} ${description}` : label;
80
- }
81
- /** Caps a tool name to the API limit, replacing the overflow with a stable hash. */
82
- function capToolName(name) {
83
- if (name.length <= MAX_TOOL_NAME_LENGTH)
84
- return name;
85
- const hash = djb2(name);
86
- return name.slice(0, MAX_TOOL_NAME_LENGTH - hash.length - 1) + SEPARATOR + hash;
87
- }
88
- /** Disambiguates a name already present in `taken` by appending `-2`, `-3`, ... */
89
- function unique(candidate, taken) {
90
- if (!taken.has(candidate))
91
- return candidate;
92
- let n = 2;
93
- while (taken.has(`${candidate}${SEPARATOR}${n}`))
94
- n++;
95
- return `${candidate}${SEPARATOR}${n}`;
96
- }
97
- /** Small deterministic string hash rendered as 8 base-36 characters. */
98
- function djb2(input) {
99
- let h = 5381;
100
- for (let i = 0; i < input.length; i++) {
101
- h = ((h << 5) + h + input.charCodeAt(i)) >>> 0;
102
- }
103
- return h.toString(36).padStart(8, "0").slice(-8);
104
- }
105
- //# sourceMappingURL=aggregate.catalog.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"aggregate.catalog.js","sourceRoot":"","sources":["../../../src/broker/aggregate/aggregate.catalog.ts"],"names":[],"mappings":"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"}
@@ -1,47 +0,0 @@
1
- import type { IMessageTransport } from "@cyanmycelium/mcp-core";
2
- import type { InternalClient } from "../../ws.tunnel.js";
3
- /** Opens an in-process client to a named provider slot. Supplied by WsTunnel. */
4
- export type InternalClientFactory = (providerName: string) => InternalClient;
5
- /**
6
- * The `_all` aggregate MCP server. Presents the union of every opted-in
7
- * provider's tools and prompts as a single MCP server, reachable on the
8
- * reserved `_all` slot.
9
- *
10
- * It implements {@link IMessageTransport} so it can be registered on the
11
- * WsTunnel as a loopback provider: `send` receives client requests, `onMessage`
12
- * (assigned by the tunnel) carries responses and notifications back to clients.
13
- */
14
- export declare class AggregateServer implements IMessageTransport {
15
- /** Reserved provider slot the aggregate is published on. */
16
- static readonly SLOT = "_all";
17
- private readonly _catalog;
18
- private readonly _sessions;
19
- private readonly _openClient;
20
- private _running;
21
- onMessage: ((data: string) => void) | null;
22
- onOpen: (() => void) | null;
23
- onClose: (() => void) | null;
24
- onError: ((error: Error) => void) | null;
25
- constructor(openClient: InternalClientFactory);
26
- get isOpen(): boolean;
27
- /** Number of providers currently in the aggregate. */
28
- get providerCount(): number;
29
- /** Marks the aggregate transport open. Call before registering it. */
30
- start(): void;
31
- /** WsTunnel hands a client request for the `_all` slot here. */
32
- send(data: string): void;
33
- /** Closes every provider session and the aggregate transport. */
34
- close(): void;
35
- /**
36
- * Adds a provider to the aggregate: opens an internal client, runs the
37
- * session handshake, and merges the provider's catalog. A no-op for an
38
- * already-aggregated provider or the reserved `_all` slot itself.
39
- */
40
- addProvider(name: string): Promise<void>;
41
- /** Removes a provider from the aggregate. */
42
- removeProvider(name: string): void;
43
- private _handleClientMessage;
44
- private _route;
45
- private _reply;
46
- private _emitListChanged;
47
- }