@cyanmycelium/mcp-broker 0.1.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 (78) hide show
  1. package/.mcp-broker.example/README.md +53 -0
  2. package/.mcp-broker.example/config.json +32 -0
  3. package/.mcp-broker.example/grammars/claude/fr.json +7 -0
  4. package/LICENSE +201 -0
  5. package/README.md +253 -0
  6. package/dist/bin.d.ts +2 -0
  7. package/dist/bin.js +208 -0
  8. package/dist/bin.js.map +1 -0
  9. package/dist/broker/adapters/broker.adapter.info.d.ts +16 -0
  10. package/dist/broker/adapters/broker.adapter.info.js +43 -0
  11. package/dist/broker/adapters/broker.adapter.info.js.map +1 -0
  12. package/dist/broker/adapters/broker.adapter.providers.d.ts +18 -0
  13. package/dist/broker/adapters/broker.adapter.providers.js +61 -0
  14. package/dist/broker/adapters/broker.adapter.providers.js.map +1 -0
  15. package/dist/broker/behaviors/broker.behavior.info.d.ts +15 -0
  16. package/dist/broker/behaviors/broker.behavior.info.js +41 -0
  17. package/dist/broker/behaviors/broker.behavior.info.js.map +1 -0
  18. package/dist/broker/behaviors/broker.behavior.providers.d.ts +19 -0
  19. package/dist/broker/behaviors/broker.behavior.providers.js +69 -0
  20. package/dist/broker/behaviors/broker.behavior.providers.js.map +1 -0
  21. package/dist/broker/broker.context.d.ts +59 -0
  22. package/dist/broker/broker.context.js +2 -0
  23. package/dist/broker/broker.context.js.map +1 -0
  24. package/dist/broker/broker.grammars.d.ts +166 -0
  25. package/dist/broker/broker.grammars.js +258 -0
  26. package/dist/broker/broker.grammars.js.map +1 -0
  27. package/dist/broker/broker.server.d.ts +64 -0
  28. package/dist/broker/broker.server.js +111 -0
  29. package/dist/broker/broker.server.js.map +1 -0
  30. package/dist/broker/grammars/claude/en.json +16 -0
  31. package/dist/broker/grammars/claude/fr.json +16 -0
  32. package/dist/broker/grammars/default/en.json +32 -0
  33. package/dist/broker/grammars/default/fr.json +32 -0
  34. package/dist/broker/grammars/default/zh.json +32 -0
  35. package/dist/broker/index.d.ts +9 -0
  36. package/dist/broker/index.js +7 -0
  37. package/dist/broker/index.js.map +1 -0
  38. package/dist/config.d.ts +101 -0
  39. package/dist/config.js +61 -0
  40. package/dist/config.js.map +1 -0
  41. package/dist/index.d.ts +12 -0
  42. package/dist/index.js +11 -0
  43. package/dist/index.js.map +1 -0
  44. package/dist/stdio.upstream.d.ts +42 -0
  45. package/dist/stdio.upstream.js +85 -0
  46. package/dist/stdio.upstream.js.map +1 -0
  47. package/dist/version.d.ts +2 -0
  48. package/dist/version.js +9 -0
  49. package/dist/version.js.map +1 -0
  50. package/dist/ws.tunnel.builder.d.ts +133 -0
  51. package/dist/ws.tunnel.builder.js +197 -0
  52. package/dist/ws.tunnel.builder.js.map +1 -0
  53. package/dist/ws.tunnel.d.ts +310 -0
  54. package/dist/ws.tunnel.js +971 -0
  55. package/dist/ws.tunnel.js.map +1 -0
  56. package/package.json +86 -0
  57. package/scripts/copy-assets.mjs +34 -0
  58. package/scripts/gen-cert.mjs +94 -0
  59. package/src/bin.ts +231 -0
  60. package/src/broker/adapters/broker.adapter.info.ts +46 -0
  61. package/src/broker/adapters/broker.adapter.providers.ts +67 -0
  62. package/src/broker/behaviors/broker.behavior.info.ts +46 -0
  63. package/src/broker/behaviors/broker.behavior.providers.ts +82 -0
  64. package/src/broker/broker.context.ts +75 -0
  65. package/src/broker/broker.grammars.ts +336 -0
  66. package/src/broker/broker.server.ts +168 -0
  67. package/src/broker/grammars/claude/en.json +16 -0
  68. package/src/broker/grammars/claude/fr.json +16 -0
  69. package/src/broker/grammars/default/en.json +32 -0
  70. package/src/broker/grammars/default/fr.json +32 -0
  71. package/src/broker/grammars/default/zh.json +32 -0
  72. package/src/broker/index.ts +24 -0
  73. package/src/config.ts +155 -0
  74. package/src/index.ts +26 -0
  75. package/src/stdio.upstream.ts +114 -0
  76. package/src/version.ts +10 -0
  77. package/src/ws.tunnel.builder.ts +214 -0
  78. package/src/ws.tunnel.ts +1269 -0
@@ -0,0 +1,197 @@
1
+ import * as fs from "fs";
2
+ import { WsTunnel } from "./ws.tunnel.js";
3
+ /**
4
+ * Fluent builder that constructs a configured {@link WsTunnel}.
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * const tunnel = new WsTunnelBuilder()
9
+ * .withPort(3000)
10
+ * .withHost("localhost")
11
+ * .withStaticMount("/", "/abs/path/to/www")
12
+ * .build();
13
+ *
14
+ * await tunnel.start();
15
+ * console.log("Broker listening on ws://localhost:3000");
16
+ * console.log(" Provider connects to: ws://localhost:3000/provider/<name>");
17
+ * console.log(" Clients connect to: ws://localhost:3000/<name>");
18
+ * ```
19
+ */
20
+ export class WsTunnelBuilder {
21
+ _port = 3000;
22
+ _host;
23
+ _providerPath = "/provider";
24
+ _providersPath = "/providers";
25
+ _clientPath = "/";
26
+ _ssePath = "/sse";
27
+ _messagesPath = "/messages";
28
+ _mcpPath = "/mcp";
29
+ _samplesIndexPath = "/__samples_index__";
30
+ _staticMounts = [];
31
+ _stdioUpstreams = [];
32
+ _stdioClient = undefined;
33
+ _tls = undefined;
34
+ _brokerLocalGrammarsDir = undefined;
35
+ /** Sets the TCP port the broker listens on. */
36
+ withPort(port) {
37
+ this._port = port;
38
+ return this;
39
+ }
40
+ /**
41
+ * Sets the host/interface to bind to.
42
+ * @default "0.0.0.0" (all interfaces)
43
+ */
44
+ withHost(host) {
45
+ this._host = host;
46
+ return this;
47
+ }
48
+ /**
49
+ * Sets the URL path the MCP provider connects to.
50
+ * @default "/provider"
51
+ */
52
+ withProviderPath(path) {
53
+ this._providerPath = path;
54
+ return this;
55
+ }
56
+ /**
57
+ * Sets the URL path for multiplexed provider connections.
58
+ * Multiple providers share a single WebSocket using the envelope protocol.
59
+ * @default "/providers"
60
+ */
61
+ withProvidersPath(path) {
62
+ this._providersPath = path;
63
+ return this;
64
+ }
65
+ /**
66
+ * Sets the URL path MCP clients connect to.
67
+ * @default "/"
68
+ */
69
+ withClientPath(path) {
70
+ this._clientPath = path;
71
+ return this;
72
+ }
73
+ /**
74
+ * Sets the URL path for the SSE stream (legacy Claude transport, GET).
75
+ * @default "/sse"
76
+ */
77
+ withSsePath(path) {
78
+ this._ssePath = path;
79
+ return this;
80
+ }
81
+ /**
82
+ * Sets the URL path for JSON-RPC POST requests (legacy Claude transport).
83
+ * @default "/messages"
84
+ */
85
+ withMessagesPath(path) {
86
+ this._messagesPath = path;
87
+ return this;
88
+ }
89
+ /**
90
+ * Sets the URL path for the Streamable HTTP transport (MCP 2025-03-26).
91
+ * MCP Inspector and other 2025+ clients POST JSON-RPC here.
92
+ * @default "/mcp"
93
+ */
94
+ withMcpPath(path) {
95
+ this._mcpPath = path;
96
+ return this;
97
+ }
98
+ /**
99
+ * Sets the URL path that returns a `{ files: string[] }` listing of the
100
+ * `samples/` subdirectory under the root static mount.
101
+ * @default "/__samples_index__"
102
+ */
103
+ withSamplesIndexPath(path) {
104
+ this._samplesIndexPath = path;
105
+ return this;
106
+ }
107
+ /**
108
+ * Adds a static-file mount served over plain HTTP.
109
+ * Can be called multiple times; longest-prefix match wins at runtime.
110
+ *
111
+ * @param urlPrefix URL prefix that triggers this mount (e.g. `"/"` or `"/bundle"`).
112
+ * @param dir Absolute path to the directory to serve.
113
+ */
114
+ withStaticMount(urlPrefix, dir) {
115
+ this._staticMounts.push({ urlPrefix, dir });
116
+ return this;
117
+ }
118
+ /**
119
+ * Registers a stdio upstream provider. The broker will spawn the given
120
+ * command and bridge its stdin/stdout as an MCP transport.
121
+ * Clients reach it using `name` directly (e.g. `/<name>/mcp`).
122
+ *
123
+ * Can be called multiple times to register multiple providers.
124
+ *
125
+ * @param name Provider name (must be unique across all upstream types).
126
+ * @param command Executable to spawn.
127
+ * @param args Arguments passed to the command.
128
+ * @param env Extra environment variables merged with `process.env`.
129
+ */
130
+ withStdioUpstream(name, command, args, env) {
131
+ this._stdioUpstreams.push({ name, command, args, env });
132
+ return this;
133
+ }
134
+ /**
135
+ * Enables the stdio client transport. The broker will read JSON-RPC from
136
+ * `process.stdin` and write responses to `process.stdout`, bridging Claude
137
+ * Desktop (or any stdio MCP client) to the named provider.
138
+ *
139
+ * All console output is automatically redirected to stderr in this mode so
140
+ * stdout stays clean for the JSON-RPC stream.
141
+ *
142
+ * @param providerName The provider the stdio client maps to.
143
+ */
144
+ withStdioClient(providerName) {
145
+ this._stdioClient = { providerName };
146
+ return this;
147
+ }
148
+ /**
149
+ * Enables HTTPS/WSS mode by supplying PEM-encoded certificate and key strings directly.
150
+ * Call this when you already have the PEM content in memory.
151
+ */
152
+ withTls(cert, key) {
153
+ this._tls = { cert, key };
154
+ return this;
155
+ }
156
+ /**
157
+ * Enables HTTPS/WSS mode by reading the certificate and key from the given file paths.
158
+ * Files are read synchronously at call time.
159
+ *
160
+ * @param certPath Path to the PEM certificate file (e.g. `fullchain.pem`).
161
+ * @param keyPath Path to the PEM private-key file (e.g. `privkey.pem`).
162
+ */
163
+ withTlsFiles(certPath, keyPath) {
164
+ return this.withTls(fs.readFileSync(certPath, "utf8"), fs.readFileSync(keyPath, "utf8"));
165
+ }
166
+ /**
167
+ * Sets the path to a user-supplied grammars directory whose
168
+ * `<userAgent>/<locale>.json` files are merged on top of the packaged
169
+ * grammars used by the embedded broker server (the reserved `_broker`
170
+ * provider slot). Typically pointed at `.mcp-broker/grammars/`.
171
+ */
172
+ withBrokerLocalGrammarsDir(dir) {
173
+ this._brokerLocalGrammarsDir = dir;
174
+ return this;
175
+ }
176
+ /** Constructs and returns a configured {@link WsTunnel}. */
177
+ build() {
178
+ const options = {
179
+ port: this._port,
180
+ host: this._host,
181
+ providerPath: this._providerPath,
182
+ providersPath: this._providersPath,
183
+ clientPath: this._clientPath,
184
+ ssePath: this._ssePath,
185
+ messagesPath: this._messagesPath,
186
+ mcpPath: this._mcpPath,
187
+ samplesIndexPath: this._samplesIndexPath,
188
+ staticMounts: this._staticMounts.length > 0 ? [...this._staticMounts] : undefined,
189
+ stdioUpstreams: this._stdioUpstreams.length > 0 ? [...this._stdioUpstreams] : undefined,
190
+ stdioClient: this._stdioClient,
191
+ tls: this._tls,
192
+ brokerLocalGrammarsDir: this._brokerLocalGrammarsDir,
193
+ };
194
+ return new WsTunnel(options);
195
+ }
196
+ }
197
+ //# sourceMappingURL=ws.tunnel.builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ws.tunnel.builder.js","sourceRoot":"","sources":["../src/ws.tunnel.builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,EAAE,QAAQ,EAA0C,MAAM,gBAAgB,CAAC;AAGlF;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,OAAO,eAAe;IAChB,KAAK,GAAG,IAAI,CAAC;IACb,KAAK,CAAqB;IAC1B,aAAa,GAAG,WAAW,CAAC;IAC5B,cAAc,GAAG,YAAY,CAAC;IAC9B,WAAW,GAAG,GAAG,CAAC;IAClB,QAAQ,GAAG,MAAM,CAAC;IAClB,aAAa,GAAG,WAAW,CAAC;IAC5B,QAAQ,GAAG,MAAM,CAAC;IAClB,iBAAiB,GAAG,oBAAoB,CAAC;IACzC,aAAa,GAAkB,EAAE,CAAC;IAClC,eAAe,GAA0B,EAAE,CAAC;IAC5C,YAAY,GAAyC,SAAS,CAAC;IAC/D,IAAI,GAA8C,SAAS,CAAC;IAC5D,uBAAuB,GAAuB,SAAS,CAAC;IAEhE,+CAA+C;IAC/C,QAAQ,CAAC,IAAY;QACjB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;;OAGG;IACH,QAAQ,CAAC,IAAY;QACjB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;;OAGG;IACH,gBAAgB,CAAC,IAAY;QACzB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAC1B,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;;;OAIG;IACH,iBAAiB,CAAC,IAAY;QAC1B,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAC3B,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;;OAGG;IACH,cAAc,CAAC,IAAY;QACvB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QACxB,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;;OAGG;IACH,WAAW,CAAC,IAAY;QACpB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;;OAGG;IACH,gBAAgB,CAAC,IAAY;QACzB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAC1B,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;;;OAIG;IACH,WAAW,CAAC,IAAY;QACpB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;;;OAIG;IACH,oBAAoB,CAAC,IAAY;QAC7B,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC;QAC9B,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;;;;;OAMG;IACH,eAAe,CAAC,SAAiB,EAAE,GAAW;QAC1C,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;QAC5C,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;;;;;;;;;;OAWG;IACH,iBAAiB,CAAC,IAAY,EAAE,OAAe,EAAE,IAAe,EAAE,GAAuB;QACrF,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;QACxD,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;;;;;;;;OASG;IACH,eAAe,CAAC,YAAoB;QAChC,IAAI,CAAC,YAAY,GAAG,EAAE,YAAY,EAAE,CAAC;QACrC,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;;OAGG;IACH,OAAO,CAAC,IAAY,EAAE,GAAW;QAC7B,IAAI,CAAC,IAAI,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;QAC1B,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;;;;;OAMG;IACH,YAAY,CAAC,QAAgB,EAAE,OAAe;QAC1C,OAAO,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;IAC7F,CAAC;IAED;;;;;OAKG;IACH,0BAA0B,CAAC,GAAW;QAClC,IAAI,CAAC,uBAAuB,GAAG,GAAG,CAAC;QACnC,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,4DAA4D;IAC5D,KAAK;QACD,MAAM,OAAO,GAAoB;YAC7B,IAAI,EAAE,IAAI,CAAC,KAAK;YAChB,IAAI,EAAE,IAAI,CAAC,KAAK;YAChB,YAAY,EAAE,IAAI,CAAC,aAAa;YAChC,aAAa,EAAE,IAAI,CAAC,cAAc;YAClC,UAAU,EAAE,IAAI,CAAC,WAAW;YAC5B,OAAO,EAAE,IAAI,CAAC,QAAQ;YACtB,YAAY,EAAE,IAAI,CAAC,aAAa;YAChC,OAAO,EAAE,IAAI,CAAC,QAAQ;YACtB,gBAAgB,EAAE,IAAI,CAAC,iBAAiB;YACxC,YAAY,EAAE,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,SAAS;YACjF,cAAc,EAAE,IAAI,CAAC,eAAe,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,SAAS;YACvF,WAAW,EAAE,IAAI,CAAC,YAAY;YAC9B,GAAG,EAAE,IAAI,CAAC,IAAI;YACd,sBAAsB,EAAE,IAAI,CAAC,uBAAuB;SACvD,CAAC;QACF,OAAO,IAAI,QAAQ,CAAC,OAAO,CAAC,CAAC;IACjC,CAAC;CACJ"}
@@ -0,0 +1,310 @@
1
+ import type { IMessageTransport } from "@cyanmycelium/mcp-core";
2
+ import { type StdioUpstreamConfig } from "./stdio.upstream.js";
3
+ import type { BrokerContext, BrokerLocaleResolver, BrokerProviderInfo, BrokerUserAgentResolver } from "./broker/index.js";
4
+ /**
5
+ * A single static-file mount: serves the contents of `dir` under `urlPrefix`.
6
+ *
7
+ * @example
8
+ * { urlPrefix: "/", dir: "/absolute/path/to/www" }
9
+ * { urlPrefix: "/bundle", dir: "/absolute/path/to/bundle" }
10
+ */
11
+ export interface StaticMount {
12
+ /** URL prefix that triggers this mount (e.g. `"/"` or `"/bundle"`). */
13
+ urlPrefix: string;
14
+ /** Absolute path to the directory to serve. */
15
+ dir: string;
16
+ }
17
+ /**
18
+ * Configuration options for a {@link WsTunnel} instance.
19
+ */
20
+ export interface WsTunnelOptions {
21
+ /** TCP port to listen on. */
22
+ port: number;
23
+ /**
24
+ * Host/interface to bind to.
25
+ * @default "0.0.0.0"
26
+ */
27
+ host?: string;
28
+ /**
29
+ * URL path **prefix** the MCP provider connects to via WebSocket.
30
+ * Each provider appends its name: `<providerPath>/<encodedName>`.
31
+ * @default "/provider"
32
+ */
33
+ providerPath?: string;
34
+ /**
35
+ * URL path for **multiplexed** provider connections.
36
+ * A single WebSocket carries traffic for multiple providers using the
37
+ * envelope protocol `{ provider: string, payload: object }`.
38
+ * @default "/providers"
39
+ */
40
+ providersPath?: string;
41
+ /**
42
+ * URL path raw WebSocket MCP clients connect to.
43
+ * @default "/"
44
+ */
45
+ clientPath?: string;
46
+ /**
47
+ * **Suffix** appended to a provider name for the SSE endpoint.
48
+ * Full URL: `/<providerName>/sse`
49
+ * @default "/sse"
50
+ */
51
+ ssePath?: string;
52
+ /**
53
+ * **Suffix** appended to a provider name for the legacy SSE POST endpoint.
54
+ * Full URL: `/<providerName>/messages`
55
+ * @default "/messages"
56
+ */
57
+ messagesPath?: string;
58
+ /**
59
+ * **Suffix** appended to a provider name for the Streamable HTTP endpoint (MCP 2025-03-26).
60
+ * Full URL: `/<providerName>/mcp`
61
+ * MCP Inspector connects here.
62
+ * @default "/mcp"
63
+ */
64
+ mcpPath?: string;
65
+ /**
66
+ * URL path that returns a `{ files: string[] }` JSON listing of every file
67
+ * inside the `samples/` subdirectory of the root static mount.
68
+ * @default "/__samples_index__"
69
+ */
70
+ samplesIndexPath?: string;
71
+ /**
72
+ * Optional static-file mounts served over plain HTTP.
73
+ * Matched by longest URL prefix; directory requests fall back to `index.html`.
74
+ */
75
+ staticMounts?: StaticMount[];
76
+ /**
77
+ * Stdio upstream providers. Each entry spawns a child process and wires its
78
+ * stdin/stdout as an MCP transport. Clients reach the process using its `name`
79
+ * directly.
80
+ *
81
+ * If a WebSocket provider connects with the same name as a stdio upstream, the
82
+ * connection is rejected and a warning is logged — stdio takes priority.
83
+ * @default undefined — no stdio providers
84
+ */
85
+ stdioUpstreams?: StdioUpstreamConfig[];
86
+ /**
87
+ * Stdio client transport. When set, the broker reads JSON-RPC from
88
+ * `process.stdin` and writes responses to `process.stdout`, bridging an
89
+ * external MCP client (e.g. Claude Desktop) to the named provider.
90
+ *
91
+ * In this mode ALL logging is redirected to stderr so stdout stays clean
92
+ * for the JSON-RPC stream.
93
+ *
94
+ * Claude Desktop config example:
95
+ * ```json
96
+ * {
97
+ * "command": "npx",
98
+ * "args": ["-y", "@cyanmycelium/mcp-broker"],
99
+ * "env": { "MCP_BROKER_STDIO_PROVIDER": "my-provider" }
100
+ * }
101
+ * ```
102
+ * @default undefined — stdio client transport disabled
103
+ */
104
+ stdioClient?: {
105
+ providerName: string;
106
+ };
107
+ /**
108
+ * TLS configuration. When provided, the server uses HTTPS and WSS instead of HTTP and WS.
109
+ * Both `cert` and `key` must be PEM-encoded strings (file contents, not file paths).
110
+ * Use {@link WsTunnelBuilder.withTlsFiles} to load from disk paths.
111
+ * @default undefined — plain HTTP/WS
112
+ */
113
+ tls?: {
114
+ /** PEM-encoded TLS certificate. */
115
+ cert: string;
116
+ /** PEM-encoded private key. */
117
+ key: string;
118
+ };
119
+ /**
120
+ * When `true` (default), the broker exposes itself as an MCP server under the
121
+ * reserved slot `_broker`. Tier-1 behaviors (`broker_info`, `providers_list`,
122
+ * `provider_status`) become callable at `<host>/_broker/mcp`.
123
+ *
124
+ * Set to `false` to keep the broker invisible to MCP clients.
125
+ * @default true
126
+ */
127
+ enableBrokerProvider?: boolean;
128
+ /**
129
+ * Logical name reported by `broker_info`. Useful when running multiple
130
+ * broker instances and you want to tell them apart from the agent side
131
+ * (e.g. `"broker-eu-west"`).
132
+ * @default PACKAGE_NAME — `@cyanmycelium/mcp-broker`
133
+ */
134
+ brokerName?: string;
135
+ /**
136
+ * Custom resolver picking the grammar locale for the embedded broker
137
+ * server. Defaults to `defaultBrokerLocaleResolver` (keeps the ISO 639-1
138
+ * prefix of a BCP-47 tag read from `MCP_BROKER_LOCALE`).
139
+ */
140
+ brokerLocaleResolver?: BrokerLocaleResolver;
141
+ /**
142
+ * Custom resolver mapping a connecting client's identity to a user-agent
143
+ * family. Defaults to `defaultBrokerUserAgentResolver` (substring match on
144
+ * `clientInfo.name` against known LLM families).
145
+ */
146
+ brokerUserAgentResolver?: BrokerUserAgentResolver;
147
+ /**
148
+ * Custom source of the raw locale string fed to the locale resolver.
149
+ * Defaults to `() => process.env.MCP_BROKER_LOCALE`. Override when the
150
+ * locale should come from a config file, HTTP header, etc.
151
+ */
152
+ brokerLocaleSource?: () => string | undefined;
153
+ /**
154
+ * Path to a user-supplied grammars directory whose `<userAgent>/<locale>.json`
155
+ * files are merged **on top of** the packaged grammars used by the embedded
156
+ * broker server. Typically pointed at `.mcp-broker/grammars/`.
157
+ */
158
+ brokerLocalGrammarsDir?: string;
159
+ }
160
+ /**
161
+ * A multi-provider relay that bridges any number of MCP server instances
162
+ * (the **providers**) with their respective MCP clients.
163
+ *
164
+ * ## Transport overview
165
+ * ```
166
+ * Provider "<name>"
167
+ * ws://host/provider/<name> ← WebSocket registration
168
+ *
169
+ * MCP Inspector (Streamable HTTP, 2025-03-26)
170
+ * GET http://host/<name>/mcp ← persistent SSE notification stream
171
+ * POST http://host/<name>/mcp → JSON-RPC requests
172
+ *
173
+ * Claude (legacy SSE transport)
174
+ * GET http://host/<name>/sse ← SSE notification stream
175
+ * POST http://host/<name>/messages → JSON-RPC requests
176
+ * ```
177
+ *
178
+ * Each provider gets its own isolated set of sessions, pending requests, and
179
+ * notification streams. Multiple providers can be connected simultaneously.
180
+ */
181
+ export declare class WsTunnel implements BrokerContext {
182
+ private readonly _options;
183
+ private _httpServer;
184
+ private _wss;
185
+ /**
186
+ * Per-provider state, keyed by provider name.
187
+ * Created lazily: a slot is allocated the first time any client references
188
+ * a provider name, even before the provider WebSocket connects.
189
+ */
190
+ private readonly _providers;
191
+ /** Maps a multiplexed WebSocket to the set of provider names it feeds. */
192
+ private readonly _multiplexSockets;
193
+ /** Stdio upstream providers, keyed by provider name. */
194
+ private readonly _stdioUpstreams;
195
+ /**
196
+ * In-process loopback transports registered as provider slots.
197
+ * Used by the embedded broker server (`_broker`) and any other component
198
+ * that wants to expose itself as a provider without going through a network.
199
+ */
200
+ private readonly _loopbackProviders;
201
+ /** The embedded broker MCP server, when {@link WsTunnelOptions.enableBrokerProvider} is on. */
202
+ private _brokerServer;
203
+ /** Provider name that the stdio client transport is bridged to, or null when disabled. */
204
+ private _stdioClientProvider;
205
+ /** Buffered partial line from stdin (stdio client transport). */
206
+ private _stdioClientBuffer;
207
+ /** Timestamp of the most recent successful `start()`. */
208
+ private _startedAt;
209
+ constructor(options: WsTunnelOptions);
210
+ get version(): string;
211
+ get name(): string;
212
+ get startedAt(): Date | null;
213
+ get uptimeSeconds(): number;
214
+ get host(): string | undefined;
215
+ get port(): number;
216
+ get tls(): boolean;
217
+ get paths(): BrokerContext["paths"];
218
+ getProvidersInfo(): BrokerProviderInfo[];
219
+ getProviderInfo(name: string): BrokerProviderInfo | undefined;
220
+ private _buildProviderInfo;
221
+ /**
222
+ * Registers an in-process transport as a provider slot. Used by the embedded
223
+ * broker server and may be used by application code that wants to host an
224
+ * MCP server inside the same process without opening a real WebSocket.
225
+ *
226
+ * @throws if the name is already used by a stdio upstream or another loopback.
227
+ */
228
+ registerLoopbackProvider(name: string, transport: IMessageTransport): void;
229
+ get isListening(): boolean;
230
+ /** Total number of connected MCP clients across all providers. */
231
+ get clientCount(): number;
232
+ /** Names of all providers that currently have an active connection. */
233
+ get providerNames(): readonly string[];
234
+ /** @deprecated Check `providerNames.length > 0` instead. */
235
+ get hasProvider(): boolean;
236
+ /**
237
+ * Starts the broker. Resolves once the HTTP server is listening.
238
+ */
239
+ start(): Promise<void>;
240
+ /**
241
+ * Starts the in-process MCP server that exposes the broker's own behaviors
242
+ * (`broker_info`, `providers_list`, `provider_status`) under the reserved
243
+ * provider slot `_broker`. No-op when {@link WsTunnelOptions.enableBrokerProvider}
244
+ * is `false`.
245
+ */
246
+ private _maybeStartBrokerServer;
247
+ /**
248
+ * Gracefully closes all connections and stops the HTTP server.
249
+ */
250
+ stop(): Promise<void>;
251
+ private _handleHttp;
252
+ /**
253
+ * Parses `/<providerName>/<endpoint>` from a URL path.
254
+ * Returns `null` if the URL does not match this two-segment pattern.
255
+ */
256
+ private _parseProviderRoute;
257
+ /**
258
+ * Handles `GET /<providerName>/sse` — opens a long-lived SSE stream for Claude.
259
+ * Sends an `endpoint` event so Claude knows where to POST its requests.
260
+ */
261
+ private _handleSseConnect;
262
+ /**
263
+ * Handles `POST /<providerName>/messages?sessionId=…` — receives a JSON-RPC
264
+ * request from Claude and forwards it to the provider.
265
+ * Always responds 202 Accepted; the real response arrives over SSE.
266
+ */
267
+ private _handleSseMessage;
268
+ /**
269
+ * Handles `POST /<providerName>/mcp` — Streamable HTTP transport (MCP 2025-03-26).
270
+ * Forwards the JSON-RPC request to the provider and holds the HTTP response
271
+ * open until the reply arrives, then writes it as `application/json`.
272
+ */
273
+ private _handleMcpPost;
274
+ /**
275
+ * Handles `GET /<providerName>/mcp` — opens a persistent SSE stream per MCP 2025-03-26.
276
+ * Streamable HTTP clients (e.g. MCP Inspector) use this to receive
277
+ * server-initiated notifications without re-polling.
278
+ */
279
+ private _handleMcpGetStream;
280
+ /** Writes one JSON-RPC message as an SSE `message` event. */
281
+ private _sendSseEvent;
282
+ private _onProviderConnect;
283
+ private _onClientConnect;
284
+ /**
285
+ * Handles a multiplexed provider WebSocket (`/providers`).
286
+ * A single socket carries traffic for multiple providers using the
287
+ * envelope format `{ provider: string, payload: object }`.
288
+ * Provider names are registered lazily on first message.
289
+ */
290
+ private _onMultiplexProviderConnect;
291
+ /**
292
+ * Sends a raw JSON-RPC message to a provider, wrapping it in a multiplex
293
+ * envelope when the provider's WebSocket is a multiplexed connection.
294
+ */
295
+ private _sendToProvider;
296
+ private _routeFromStdioClient;
297
+ private _routeFromClient;
298
+ private _routeFromProvider;
299
+ /** Sends a message to all clients connected to one provider. */
300
+ private _broadcast;
301
+ /**
302
+ * Returns `true` if the provider is reachable — via a WebSocket connection,
303
+ * a stdio upstream, or an in-process loopback transport.
304
+ */
305
+ private _isProviderConnected;
306
+ /** Returns the state for `name`, creating it lazily if it doesn't exist yet. */
307
+ private _getOrCreateProviderState;
308
+ private _handleSamplesIndex;
309
+ private _serveStatic;
310
+ }