@cyanmycelium/mcp-broker 0.2.0 → 0.4.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/.mcp-broker.example/README.md +23 -0
- package/.mcp-broker.example/config.json +11 -0
- package/README.md +1 -16
- package/dist/bin.js +30 -3
- package/dist/bin.js.map +1 -1
- package/dist/broker/aggregate/aggregate.catalog.d.ts +54 -0
- package/dist/broker/aggregate/aggregate.catalog.js +105 -0
- package/dist/broker/aggregate/aggregate.catalog.js.map +1 -0
- package/dist/broker/aggregate/aggregate.server.d.ts +47 -0
- package/dist/broker/aggregate/aggregate.server.js +151 -0
- package/dist/broker/aggregate/aggregate.server.js.map +1 -0
- package/dist/broker/aggregate/provider.client.session.d.ts +52 -0
- package/dist/broker/aggregate/provider.client.session.js +140 -0
- package/dist/broker/aggregate/provider.client.session.js.map +1 -0
- package/dist/broker/broker.grammars.d.ts +50 -86
- package/dist/broker/broker.grammars.js +55 -84
- package/dist/broker/broker.grammars.js.map +1 -1
- package/dist/broker/broker.server.d.ts +23 -21
- package/dist/broker/broker.server.js +33 -71
- package/dist/broker/broker.server.js.map +1 -1
- package/dist/broker/index.d.ts +2 -2
- package/dist/broker/index.js +1 -1
- package/dist/broker/index.js.map +1 -1
- package/dist/config.d.ts +35 -0
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +8 -2
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/mcpb.loader.d.ts +24 -0
- package/dist/mcpb.loader.js +161 -0
- package/dist/mcpb.loader.js.map +1 -0
- package/dist/mcpb.unzip.d.ts +6 -0
- package/dist/mcpb.unzip.js +95 -0
- package/dist/mcpb.unzip.js.map +1 -0
- package/dist/remote.transports.d.ts +16 -0
- package/dist/remote.transports.js +297 -0
- package/dist/remote.transports.js.map +1 -0
- package/dist/remote.upstream.d.ts +36 -0
- package/dist/remote.upstream.js +52 -0
- package/dist/remote.upstream.js.map +1 -0
- package/dist/stdio.upstream.d.ts +4 -1
- package/dist/stdio.upstream.js.map +1 -1
- package/dist/upstream.d.ts +33 -0
- package/dist/upstream.js +2 -0
- package/dist/upstream.js.map +1 -0
- package/dist/ws.tunnel.builder.d.ts +14 -8
- package/dist/ws.tunnel.builder.js +17 -9
- package/dist/ws.tunnel.builder.js.map +1 -1
- package/dist/ws.tunnel.d.ts +85 -22
- package/dist/ws.tunnel.js +201 -82
- package/dist/ws.tunnel.js.map +1 -1
- package/package.json +3 -2
- package/scripts/pack-mcpb.mjs +84 -0
- package/scripts/sign-bundle.mjs +61 -0
- package/src/bin.ts +32 -3
- package/src/broker/aggregate/aggregate.catalog.ts +145 -0
- package/src/broker/aggregate/aggregate.server.ts +178 -0
- package/src/broker/aggregate/provider.client.session.ts +172 -0
- package/src/broker/broker.grammars.ts +74 -122
- package/src/broker/broker.server.ts +57 -99
- package/src/broker/index.ts +3 -5
- package/src/config.ts +37 -0
- package/src/index.ts +10 -10
- package/src/mcpb.loader.ts +186 -0
- package/src/mcpb.unzip.ts +103 -0
- package/src/remote.transports.ts +316 -0
- package/src/remote.upstream.ts +75 -0
- package/src/stdio.upstream.ts +4 -1
- package/src/upstream.ts +33 -0
- package/src/ws.tunnel.builder.ts +19 -9
- package/src/ws.tunnel.ts +258 -99
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hand-rolled client transports for reaching a remote MCP server by URL.
|
|
3
|
+
*
|
|
4
|
+
* No `@modelcontextprotocol/sdk` dependency: the broker stays SDK-free, the
|
|
5
|
+
* official SDK is only used outside the broker (in the demo) to prove the
|
|
6
|
+
* broker is implementation-agnostic. Each transport is a thin frame relay —
|
|
7
|
+
* it does not interpret MCP, it pipes JSON-RPC strings both ways.
|
|
8
|
+
*/
|
|
9
|
+
import * as http from "node:http";
|
|
10
|
+
import * as https from "node:https";
|
|
11
|
+
import { WebSocket } from "ws";
|
|
12
|
+
/** Issues an http/https request, picking the module from the URL scheme. */
|
|
13
|
+
function makeRequest(url, options, onResponse) {
|
|
14
|
+
return url.protocol === "https:" ? https.request(url, options, onResponse) : http.request(url, options, onResponse);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Incremental Server-Sent-Events decoder. Normalizes line endings, buffers
|
|
18
|
+
* across chunks, and emits one `(event, data)` pair per complete SSE block.
|
|
19
|
+
*/
|
|
20
|
+
class SseDecoder {
|
|
21
|
+
_buf = "";
|
|
22
|
+
feed(chunk, emit) {
|
|
23
|
+
this._buf += chunk.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
24
|
+
let sep;
|
|
25
|
+
while ((sep = this._buf.indexOf("\n\n")) !== -1) {
|
|
26
|
+
const block = this._buf.slice(0, sep);
|
|
27
|
+
this._buf = this._buf.slice(sep + 2);
|
|
28
|
+
let event = "message";
|
|
29
|
+
const data = [];
|
|
30
|
+
for (const line of block.split("\n")) {
|
|
31
|
+
if (line.startsWith(":"))
|
|
32
|
+
continue; // comment
|
|
33
|
+
const colon = line.indexOf(":");
|
|
34
|
+
const field = colon === -1 ? line : line.slice(0, colon);
|
|
35
|
+
let value = colon === -1 ? "" : line.slice(colon + 1);
|
|
36
|
+
if (value.startsWith(" "))
|
|
37
|
+
value = value.slice(1);
|
|
38
|
+
if (field === "event")
|
|
39
|
+
event = value;
|
|
40
|
+
else if (field === "data")
|
|
41
|
+
data.push(value);
|
|
42
|
+
}
|
|
43
|
+
if (data.length > 0)
|
|
44
|
+
emit(event, data.join("\n"));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
// WebSocket
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
class WebSocketRemoteTransport {
|
|
52
|
+
_url;
|
|
53
|
+
_headers;
|
|
54
|
+
onMessage = null;
|
|
55
|
+
onOpen = null;
|
|
56
|
+
onClose = null;
|
|
57
|
+
onError = null;
|
|
58
|
+
_ws = null;
|
|
59
|
+
constructor(_url, _headers) {
|
|
60
|
+
this._url = _url;
|
|
61
|
+
this._headers = _headers;
|
|
62
|
+
}
|
|
63
|
+
connect() {
|
|
64
|
+
const ws = new WebSocket(this._url, { headers: this._headers });
|
|
65
|
+
this._ws = ws;
|
|
66
|
+
ws.on("open", () => this.onOpen?.());
|
|
67
|
+
ws.on("message", (data) => this.onMessage?.(data.toString("utf8")));
|
|
68
|
+
ws.on("close", () => this.onClose?.());
|
|
69
|
+
ws.on("error", (err) => this.onError?.(err));
|
|
70
|
+
}
|
|
71
|
+
send(data) {
|
|
72
|
+
if (this._ws && this._ws.readyState === WebSocket.OPEN)
|
|
73
|
+
this._ws.send(data);
|
|
74
|
+
}
|
|
75
|
+
close() {
|
|
76
|
+
this._ws?.close();
|
|
77
|
+
this._ws = null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
// Legacy MCP SSE (GET event stream + POST message endpoint)
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
class SseRemoteTransport {
|
|
84
|
+
_url;
|
|
85
|
+
_headers;
|
|
86
|
+
onMessage = null;
|
|
87
|
+
onOpen = null;
|
|
88
|
+
onClose = null;
|
|
89
|
+
onError = null;
|
|
90
|
+
_decoder = new SseDecoder();
|
|
91
|
+
_streamReq = null;
|
|
92
|
+
_postUrl = null;
|
|
93
|
+
_closed = false;
|
|
94
|
+
constructor(_url, _headers) {
|
|
95
|
+
this._url = _url;
|
|
96
|
+
this._headers = _headers;
|
|
97
|
+
}
|
|
98
|
+
connect() {
|
|
99
|
+
const req = makeRequest(new URL(this._url), { method: "GET", headers: { ...this._headers, Accept: "text/event-stream" } }, (res) => {
|
|
100
|
+
const status = res.statusCode ?? 0;
|
|
101
|
+
if (status >= 400) {
|
|
102
|
+
this.onError?.(new Error(`SSE GET responded ${status}`));
|
|
103
|
+
res.resume();
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
res.setEncoding("utf8");
|
|
107
|
+
res.on("data", (chunk) => this._decoder.feed(chunk, (ev, data) => this._onEvent(ev, data)));
|
|
108
|
+
res.on("end", () => {
|
|
109
|
+
if (!this._closed)
|
|
110
|
+
this.onClose?.();
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
this._streamReq = req;
|
|
114
|
+
req.on("error", (err) => {
|
|
115
|
+
if (!this._closed)
|
|
116
|
+
this.onError?.(err);
|
|
117
|
+
});
|
|
118
|
+
req.end();
|
|
119
|
+
}
|
|
120
|
+
_onEvent(event, data) {
|
|
121
|
+
if (event === "endpoint") {
|
|
122
|
+
this._postUrl = new URL(data, this._url).toString();
|
|
123
|
+
this.onOpen?.();
|
|
124
|
+
}
|
|
125
|
+
else if (event === "message") {
|
|
126
|
+
this.onMessage?.(data);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
send(data) {
|
|
130
|
+
if (!this._postUrl)
|
|
131
|
+
return;
|
|
132
|
+
const req = makeRequest(new URL(this._postUrl), { method: "POST", headers: { ...this._headers, "Content-Type": "application/json" } }, (res) => res.resume());
|
|
133
|
+
req.on("error", (err) => this.onError?.(err));
|
|
134
|
+
req.end(data);
|
|
135
|
+
}
|
|
136
|
+
close() {
|
|
137
|
+
this._closed = true;
|
|
138
|
+
this._streamReq?.destroy();
|
|
139
|
+
this._streamReq = null;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
// Streamable HTTP (MCP 2025-03-26)
|
|
144
|
+
// ---------------------------------------------------------------------------
|
|
145
|
+
class StreamableHttpRemoteTransport {
|
|
146
|
+
_url;
|
|
147
|
+
_headers;
|
|
148
|
+
onMessage = null;
|
|
149
|
+
onOpen = null;
|
|
150
|
+
onClose = null;
|
|
151
|
+
onError = null;
|
|
152
|
+
_sessionId = null;
|
|
153
|
+
_streamReq = null;
|
|
154
|
+
_closed = false;
|
|
155
|
+
constructor(_url, _headers) {
|
|
156
|
+
this._url = _url;
|
|
157
|
+
this._headers = _headers;
|
|
158
|
+
}
|
|
159
|
+
connect() {
|
|
160
|
+
// Streamable HTTP is stateless — there is no connection to open. The
|
|
161
|
+
// transport is usable as soon as connect() is called; the session id
|
|
162
|
+
// is captured later from the first response that carries one.
|
|
163
|
+
this._closed = false;
|
|
164
|
+
this.onOpen?.();
|
|
165
|
+
}
|
|
166
|
+
send(data) {
|
|
167
|
+
if (this._closed)
|
|
168
|
+
return;
|
|
169
|
+
const headers = {
|
|
170
|
+
...this._headers,
|
|
171
|
+
"Content-Type": "application/json",
|
|
172
|
+
Accept: "application/json, text/event-stream",
|
|
173
|
+
};
|
|
174
|
+
if (this._sessionId)
|
|
175
|
+
headers["Mcp-Session-Id"] = this._sessionId;
|
|
176
|
+
const req = makeRequest(new URL(this._url), { method: "POST", headers }, (res) => this._handleResponse(res));
|
|
177
|
+
req.on("error", (err) => {
|
|
178
|
+
if (!this._closed)
|
|
179
|
+
this.onError?.(err);
|
|
180
|
+
});
|
|
181
|
+
req.end(data);
|
|
182
|
+
}
|
|
183
|
+
_handleResponse(res) {
|
|
184
|
+
const sid = res.headers["mcp-session-id"];
|
|
185
|
+
if (typeof sid === "string" && sid.length > 0 && sid !== this._sessionId) {
|
|
186
|
+
this._sessionId = sid;
|
|
187
|
+
this._openServerStream();
|
|
188
|
+
}
|
|
189
|
+
const status = res.statusCode ?? 0;
|
|
190
|
+
if (status === 202) {
|
|
191
|
+
res.resume();
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
if (status >= 400) {
|
|
195
|
+
this.onError?.(new Error(`Streamable HTTP POST responded ${status}`));
|
|
196
|
+
res.resume();
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
res.setEncoding("utf8");
|
|
200
|
+
if (String(res.headers["content-type"] ?? "").includes("text/event-stream")) {
|
|
201
|
+
const decoder = new SseDecoder();
|
|
202
|
+
res.on("data", (chunk) => decoder.feed(chunk, (ev, data) => {
|
|
203
|
+
if (ev === "message")
|
|
204
|
+
this.onMessage?.(data);
|
|
205
|
+
}));
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
let body = "";
|
|
209
|
+
res.on("data", (chunk) => {
|
|
210
|
+
body += chunk;
|
|
211
|
+
});
|
|
212
|
+
res.on("end", () => this._emitJsonBody(body));
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
_emitJsonBody(body) {
|
|
216
|
+
const trimmed = body.trim();
|
|
217
|
+
if (!trimmed)
|
|
218
|
+
return;
|
|
219
|
+
try {
|
|
220
|
+
const parsed = JSON.parse(trimmed);
|
|
221
|
+
if (Array.isArray(parsed)) {
|
|
222
|
+
for (const message of parsed)
|
|
223
|
+
this.onMessage?.(JSON.stringify(message));
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
/* not JSON — forward verbatim */
|
|
229
|
+
}
|
|
230
|
+
this.onMessage?.(trimmed);
|
|
231
|
+
}
|
|
232
|
+
/** Opens the optional standalone GET stream for server-initiated messages. */
|
|
233
|
+
_openServerStream() {
|
|
234
|
+
if (this._closed || this._streamReq)
|
|
235
|
+
return;
|
|
236
|
+
const headers = { ...this._headers, Accept: "text/event-stream" };
|
|
237
|
+
if (this._sessionId)
|
|
238
|
+
headers["Mcp-Session-Id"] = this._sessionId;
|
|
239
|
+
const req = makeRequest(new URL(this._url), { method: "GET", headers }, (res) => {
|
|
240
|
+
if ((res.statusCode ?? 0) >= 400) {
|
|
241
|
+
// The server may not support the standalone GET stream (405). Fine.
|
|
242
|
+
res.resume();
|
|
243
|
+
this._streamReq = null;
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
const decoder = new SseDecoder();
|
|
247
|
+
res.setEncoding("utf8");
|
|
248
|
+
res.on("data", (chunk) => decoder.feed(chunk, (ev, data) => {
|
|
249
|
+
if (ev === "message")
|
|
250
|
+
this.onMessage?.(data);
|
|
251
|
+
}));
|
|
252
|
+
res.on("end", () => {
|
|
253
|
+
this._streamReq = null;
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
this._streamReq = req;
|
|
257
|
+
req.on("error", () => {
|
|
258
|
+
this._streamReq = null;
|
|
259
|
+
});
|
|
260
|
+
req.end();
|
|
261
|
+
}
|
|
262
|
+
close() {
|
|
263
|
+
this._closed = true;
|
|
264
|
+
this._streamReq?.destroy();
|
|
265
|
+
this._streamReq = null;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
// ---------------------------------------------------------------------------
|
|
269
|
+
// Factory
|
|
270
|
+
// ---------------------------------------------------------------------------
|
|
271
|
+
/** Builds the transport of the given kind. */
|
|
272
|
+
export function createRemoteTransport(url, kind, headers) {
|
|
273
|
+
switch (kind) {
|
|
274
|
+
case "websocket":
|
|
275
|
+
return new WebSocketRemoteTransport(url, headers);
|
|
276
|
+
case "sse":
|
|
277
|
+
return new SseRemoteTransport(url, headers);
|
|
278
|
+
case "streamable-http":
|
|
279
|
+
return new StreamableHttpRemoteTransport(url, headers);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
/** Heuristic transport detection from the URL when none is configured. */
|
|
283
|
+
export function detectTransport(url) {
|
|
284
|
+
let parsed;
|
|
285
|
+
try {
|
|
286
|
+
parsed = new URL(url);
|
|
287
|
+
}
|
|
288
|
+
catch {
|
|
289
|
+
return "streamable-http";
|
|
290
|
+
}
|
|
291
|
+
if (parsed.protocol === "ws:" || parsed.protocol === "wss:")
|
|
292
|
+
return "websocket";
|
|
293
|
+
if (parsed.pathname.replace(/\/+$/, "").endsWith("/sse"))
|
|
294
|
+
return "sse";
|
|
295
|
+
return "streamable-http";
|
|
296
|
+
}
|
|
297
|
+
//# sourceMappingURL=remote.transports.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote.transports.js","sourceRoot":"","sources":["../src/remote.transports.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Upstream } from "./upstream.js";
|
|
2
|
+
import { type RemoteTransportKind } from "./remote.transports.js";
|
|
3
|
+
export interface RemoteUpstreamConfig {
|
|
4
|
+
/** Provider slot name this upstream is bound to. */
|
|
5
|
+
name: string;
|
|
6
|
+
/** URL of the remote MCP server. */
|
|
7
|
+
url: string;
|
|
8
|
+
/** Transport to use. Auto-detected from the URL scheme/path when omitted. */
|
|
9
|
+
transport?: RemoteTransportKind;
|
|
10
|
+
/** Extra HTTP / WebSocket headers (e.g. an `Authorization` header). */
|
|
11
|
+
headers?: Record<string, string>;
|
|
12
|
+
/** When `true`, this upstream joins the `_all` aggregate slot once connected. */
|
|
13
|
+
aggregate?: boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Bridges a remote MCP server (reachable by URL) into a broker provider slot.
|
|
17
|
+
*
|
|
18
|
+
* The broker-facing contract is identical to {@link StdioUpstream}: the broker
|
|
19
|
+
* muxes the slot's clients onto this single upstream. The only difference is
|
|
20
|
+
* the transport — Streamable HTTP / SSE / WebSocket instead of a child process.
|
|
21
|
+
*/
|
|
22
|
+
export declare class RemoteUpstream implements Upstream {
|
|
23
|
+
readonly name: string;
|
|
24
|
+
onMessage: ((data: string) => void) | null;
|
|
25
|
+
onOpen: (() => void) | null;
|
|
26
|
+
onClose: (() => void) | null;
|
|
27
|
+
onError: ((error: Error) => void) | null;
|
|
28
|
+
private readonly _config;
|
|
29
|
+
private _transport;
|
|
30
|
+
private _open;
|
|
31
|
+
constructor(config: RemoteUpstreamConfig);
|
|
32
|
+
get isOpen(): boolean;
|
|
33
|
+
connect(): void;
|
|
34
|
+
send(data: string): void;
|
|
35
|
+
close(): void;
|
|
36
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { createRemoteTransport, detectTransport } from "./remote.transports.js";
|
|
2
|
+
/**
|
|
3
|
+
* Bridges a remote MCP server (reachable by URL) into a broker provider slot.
|
|
4
|
+
*
|
|
5
|
+
* The broker-facing contract is identical to {@link StdioUpstream}: the broker
|
|
6
|
+
* muxes the slot's clients onto this single upstream. The only difference is
|
|
7
|
+
* the transport — Streamable HTTP / SSE / WebSocket instead of a child process.
|
|
8
|
+
*/
|
|
9
|
+
export class RemoteUpstream {
|
|
10
|
+
name;
|
|
11
|
+
onMessage = null;
|
|
12
|
+
onOpen = null;
|
|
13
|
+
onClose = null;
|
|
14
|
+
onError = null;
|
|
15
|
+
_config;
|
|
16
|
+
_transport = null;
|
|
17
|
+
_open = false;
|
|
18
|
+
constructor(config) {
|
|
19
|
+
this.name = config.name;
|
|
20
|
+
this._config = config;
|
|
21
|
+
}
|
|
22
|
+
get isOpen() {
|
|
23
|
+
return this._open;
|
|
24
|
+
}
|
|
25
|
+
connect() {
|
|
26
|
+
const kind = this._config.transport ?? detectTransport(this._config.url);
|
|
27
|
+
const transport = createRemoteTransport(this._config.url, kind, this._config.headers ?? {});
|
|
28
|
+
this._transport = transport;
|
|
29
|
+
transport.onOpen = () => {
|
|
30
|
+
this._open = true;
|
|
31
|
+
this.onOpen?.();
|
|
32
|
+
};
|
|
33
|
+
transport.onMessage = (data) => this.onMessage?.(data);
|
|
34
|
+
transport.onClose = () => {
|
|
35
|
+
this._open = false;
|
|
36
|
+
this.onClose?.();
|
|
37
|
+
};
|
|
38
|
+
transport.onError = (err) => {
|
|
39
|
+
this.onError?.(new Error(`RemoteUpstream "${this.name}": ${err.message}`));
|
|
40
|
+
};
|
|
41
|
+
transport.connect();
|
|
42
|
+
}
|
|
43
|
+
send(data) {
|
|
44
|
+
this._transport?.send(data);
|
|
45
|
+
}
|
|
46
|
+
close() {
|
|
47
|
+
this._open = false;
|
|
48
|
+
this._transport?.close();
|
|
49
|
+
this._transport = null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=remote.upstream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote.upstream.js","sourceRoot":"","sources":["../src/remote.upstream.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAkD,MAAM,wBAAwB,CAAC;AAehI;;;;;;GAMG;AACH,MAAM,OAAO,cAAc;IACd,IAAI,CAAS;IAEtB,SAAS,GAAoC,IAAI,CAAC;IAClD,MAAM,GAAwB,IAAI,CAAC;IACnC,OAAO,GAAwB,IAAI,CAAC;IACpC,OAAO,GAAoC,IAAI,CAAC;IAE/B,OAAO,CAAuB;IACvC,UAAU,GAA2B,IAAI,CAAC;IAC1C,KAAK,GAAG,KAAK,CAAC;IAEtB,YAAY,MAA4B;QACpC,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;QACxB,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;IAC1B,CAAC;IAED,IAAI,MAAM;QACN,OAAO,IAAI,CAAC,KAAK,CAAC;IACtB,CAAC;IAED,OAAO;QACH,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,IAAI,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACzE,MAAM,SAAS,GAAG,qBAAqB,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;QAC5F,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAE5B,SAAS,CAAC,MAAM,GAAG,GAAS,EAAE;YAC1B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;YAClB,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;QACpB,CAAC,CAAC;QACF,SAAS,CAAC,SAAS,GAAG,CAAC,IAAY,EAAQ,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,CAAC;QACrE,SAAS,CAAC,OAAO,GAAG,GAAS,EAAE;YAC3B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;YACnB,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC;QACrB,CAAC,CAAC;QACF,SAAS,CAAC,OAAO,GAAG,CAAC,GAAU,EAAQ,EAAE;YACrC,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,KAAK,CAAC,mBAAmB,IAAI,CAAC,IAAI,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAC/E,CAAC,CAAC;QAEF,SAAS,CAAC,OAAO,EAAE,CAAC;IACxB,CAAC;IAED,IAAI,CAAC,IAAY;QACb,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IAED,KAAK;QACD,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,UAAU,EAAE,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;IAC3B,CAAC;CACJ"}
|
package/dist/stdio.upstream.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Upstream } from "./upstream.js";
|
|
1
2
|
export interface StdioUpstreamConfig {
|
|
2
3
|
/** Logical name of this provider (matched against incoming WebSocket provider names). */
|
|
3
4
|
name: string;
|
|
@@ -7,6 +8,8 @@ export interface StdioUpstreamConfig {
|
|
|
7
8
|
args?: string[];
|
|
8
9
|
/** Extra environment variables merged with `process.env`. */
|
|
9
10
|
env?: NodeJS.ProcessEnv;
|
|
11
|
+
/** When `true`, this upstream joins the `_all` aggregate slot once connected. */
|
|
12
|
+
aggregate?: boolean;
|
|
10
13
|
}
|
|
11
14
|
/**
|
|
12
15
|
* Manages a stdio-based MCP server process.
|
|
@@ -16,7 +19,7 @@ export interface StdioUpstreamConfig {
|
|
|
16
19
|
* One instance per configured provider. The broker uses this to bridge
|
|
17
20
|
* WebSocket/SSE/HTTP clients to local MCP server processes.
|
|
18
21
|
*/
|
|
19
|
-
export declare class StdioUpstream {
|
|
22
|
+
export declare class StdioUpstream implements Upstream {
|
|
20
23
|
readonly name: string;
|
|
21
24
|
private readonly _config;
|
|
22
25
|
private _proc;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stdio.upstream.js","sourceRoot":"","sources":["../src/stdio.upstream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAgB,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"stdio.upstream.js","sourceRoot":"","sources":["../src/stdio.upstream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAgB,MAAM,oBAAoB,CAAC;AAoBzD,8EAA8E;AAC9E,gBAAgB;AAChB,8EAA8E;AAE9E;;;;;;;GAOG;AACH,MAAM,OAAO,aAAa;IACb,IAAI,CAAS;IAEL,OAAO,CAAsB;IACtC,KAAK,GAAwB,IAAI,CAAC;IAClC,OAAO,GAAG,EAAE,CAAC;IACb,KAAK,GAAG,KAAK,CAAC;IACd,QAAQ,GAAG,KAAK,CAAC;IAEzB,4EAA4E;IAC5E,SAAS,GAAoC,IAAI,CAAC;IAElD,iEAAiE;IACjE,MAAM,GAAwB,IAAI,CAAC;IAEnC,4DAA4D;IAC5D,OAAO,GAAwB,IAAI,CAAC;IAEpC,yCAAyC;IACzC,OAAO,GAAoC,IAAI,CAAC;IAEhD,YAAY,MAA2B;QACnC,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;QACxB,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;IAC1B,CAAC;IAED,IAAI,MAAM;QACN,OAAO,IAAI,CAAC,KAAK,CAAC;IACtB,CAAC;IAED,6DAA6D;IAC7D,OAAO;QACH,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;QACtB,MAAM,EAAE,OAAO,EAAE,IAAI,GAAG,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC;QAEjD,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE;YAC9B,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,GAAG,EAAE;YAC/B,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC;SACrC,CAAC,CAAC;QAEH,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAU,EAAE,EAAE;YAClC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;YACnB,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,KAAK,CAAC,kBAAkB,IAAI,CAAC,IAAI,sBAAsB,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAC9F,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACxB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;YAClB,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;QACpB,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,KAAK,CAAC,MAAO,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YAC5C,IAAI,CAAC,OAAO,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YACvC,IAAI,EAAU,CAAC;YACf,OAAO,CAAC,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;gBAC9C,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;gBAC9C,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;gBAC1C,IAAI,IAAI;oBAAE,IAAI,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,CAAC;YACrC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;YAC5B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;YACnB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;YAClB,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC;YACjB,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACjB,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,KAAK,CAAC,kBAAkB,IAAI,CAAC,IAAI,+BAA+B,IAAI,IAAI,MAAM,EAAE,CAAC,CAAC,CAAC;YAC1G,CAAC;QACL,CAAC,CAAC,CAAC;IACP,CAAC;IAED,uEAAuE;IACvE,IAAI,CAAC,IAAY;QACb,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ;YAAE,OAAO;QACxD,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;IAChD,CAAC;IAED,0EAA0E;IAC1E,KAAK;QACD,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;QACzB,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC;QACnB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;IACtB,CAAC;CACJ"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Common contract for an upstream MCP server bound to a provider slot.
|
|
3
|
+
*
|
|
4
|
+
* The broker treats every upstream the same way: it muxes the slot's clients
|
|
5
|
+
* onto the single upstream connection, regardless of the underlying transport.
|
|
6
|
+
*
|
|
7
|
+
* Implementations:
|
|
8
|
+
* - `StdioUpstream` — a local child process (newline-delimited JSON-RPC over stdio).
|
|
9
|
+
* - `RemoteUpstream` — a remote MCP server reached by URL (Streamable HTTP / SSE / WebSocket).
|
|
10
|
+
*
|
|
11
|
+
* `StdioUpstream` is reused as-is by the future `.mcpb` bundle loader: a bundle
|
|
12
|
+
* is a local server whose `mcp_config` maps directly onto a stdio upstream.
|
|
13
|
+
*/
|
|
14
|
+
export interface Upstream {
|
|
15
|
+
/** Provider slot name this upstream is bound to. */
|
|
16
|
+
readonly name: string;
|
|
17
|
+
/** Whether the upstream connection is currently usable. */
|
|
18
|
+
readonly isOpen: boolean;
|
|
19
|
+
/** Receives one complete JSON-RPC message from the upstream server. */
|
|
20
|
+
onMessage: ((data: string) => void) | null;
|
|
21
|
+
/** Fires once the upstream connection is established and ready to send. */
|
|
22
|
+
onOpen: (() => void) | null;
|
|
23
|
+
/** Fires when the upstream connection closes. */
|
|
24
|
+
onClose: (() => void) | null;
|
|
25
|
+
/** Fires on a connection or runtime error. */
|
|
26
|
+
onError: ((error: Error) => void) | null;
|
|
27
|
+
/** Opens the upstream connection. */
|
|
28
|
+
connect(): void;
|
|
29
|
+
/** Sends one JSON-RPC message to the upstream server. */
|
|
30
|
+
send(data: string): void;
|
|
31
|
+
/** Closes the upstream connection. */
|
|
32
|
+
close(): void;
|
|
33
|
+
}
|
package/dist/upstream.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upstream.js","sourceRoot":"","sources":["../src/upstream.ts"],"names":[],"mappings":""}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { WsTunnel } from "./ws.tunnel.js";
|
|
2
|
+
import type { StdioUpstreamConfig } from "./stdio.upstream.js";
|
|
3
|
+
import type { RemoteUpstreamConfig } from "./remote.upstream.js";
|
|
2
4
|
/**
|
|
3
5
|
* Fluent builder that constructs a configured {@link WsTunnel}.
|
|
4
6
|
*
|
|
@@ -28,6 +30,7 @@ export declare class WsTunnelBuilder {
|
|
|
28
30
|
private _samplesIndexPath;
|
|
29
31
|
private _staticMounts;
|
|
30
32
|
private _stdioUpstreams;
|
|
33
|
+
private _remoteUpstreams;
|
|
31
34
|
private _stdioClient;
|
|
32
35
|
private _tls;
|
|
33
36
|
private _brokerLocalGrammarsDir;
|
|
@@ -85,18 +88,21 @@ export declare class WsTunnelBuilder {
|
|
|
85
88
|
*/
|
|
86
89
|
withStaticMount(urlPrefix: string, dir: string): this;
|
|
87
90
|
/**
|
|
88
|
-
* Registers a stdio upstream provider. The broker
|
|
89
|
-
* command and
|
|
90
|
-
*
|
|
91
|
+
* Registers a stdio upstream provider. The broker spawns the configured
|
|
92
|
+
* command and bridges its stdin/stdout as an MCP transport. Clients reach
|
|
93
|
+
* it using `config.name` directly (e.g. `/<name>/mcp`).
|
|
91
94
|
*
|
|
92
95
|
* Can be called multiple times to register multiple providers.
|
|
96
|
+
*/
|
|
97
|
+
withStdioUpstream(config: StdioUpstreamConfig): this;
|
|
98
|
+
/**
|
|
99
|
+
* Registers a remote MCP server reached by URL. The broker connects out to
|
|
100
|
+
* it and exposes it as a provider slot named `config.name`, bridging the
|
|
101
|
+
* Streamable HTTP / SSE / WebSocket transport for the slot's clients.
|
|
93
102
|
*
|
|
94
|
-
*
|
|
95
|
-
* @param command Executable to spawn.
|
|
96
|
-
* @param args Arguments passed to the command.
|
|
97
|
-
* @param env Extra environment variables merged with `process.env`.
|
|
103
|
+
* Can be called multiple times to register multiple servers.
|
|
98
104
|
*/
|
|
99
|
-
|
|
105
|
+
withRemoteUpstream(config: RemoteUpstreamConfig): this;
|
|
100
106
|
/**
|
|
101
107
|
* Enables the stdio client transport. The broker will read JSON-RPC from
|
|
102
108
|
* `process.stdin` and write responses to `process.stdout`, bridging Claude
|
|
@@ -29,6 +29,7 @@ export class WsTunnelBuilder {
|
|
|
29
29
|
_samplesIndexPath = "/__samples_index__";
|
|
30
30
|
_staticMounts = [];
|
|
31
31
|
_stdioUpstreams = [];
|
|
32
|
+
_remoteUpstreams = [];
|
|
32
33
|
_stdioClient = undefined;
|
|
33
34
|
_tls = undefined;
|
|
34
35
|
_brokerLocalGrammarsDir = undefined;
|
|
@@ -116,19 +117,25 @@ export class WsTunnelBuilder {
|
|
|
116
117
|
return this;
|
|
117
118
|
}
|
|
118
119
|
/**
|
|
119
|
-
* Registers a stdio upstream provider. The broker
|
|
120
|
-
* command and
|
|
121
|
-
*
|
|
120
|
+
* Registers a stdio upstream provider. The broker spawns the configured
|
|
121
|
+
* command and bridges its stdin/stdout as an MCP transport. Clients reach
|
|
122
|
+
* it using `config.name` directly (e.g. `/<name>/mcp`).
|
|
122
123
|
*
|
|
123
124
|
* Can be called multiple times to register multiple providers.
|
|
125
|
+
*/
|
|
126
|
+
withStdioUpstream(config) {
|
|
127
|
+
this._stdioUpstreams.push(config);
|
|
128
|
+
return this;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Registers a remote MCP server reached by URL. The broker connects out to
|
|
132
|
+
* it and exposes it as a provider slot named `config.name`, bridging the
|
|
133
|
+
* Streamable HTTP / SSE / WebSocket transport for the slot's clients.
|
|
124
134
|
*
|
|
125
|
-
*
|
|
126
|
-
* @param command Executable to spawn.
|
|
127
|
-
* @param args Arguments passed to the command.
|
|
128
|
-
* @param env Extra environment variables merged with `process.env`.
|
|
135
|
+
* Can be called multiple times to register multiple servers.
|
|
129
136
|
*/
|
|
130
|
-
|
|
131
|
-
this.
|
|
137
|
+
withRemoteUpstream(config) {
|
|
138
|
+
this._remoteUpstreams.push(config);
|
|
132
139
|
return this;
|
|
133
140
|
}
|
|
134
141
|
/**
|
|
@@ -187,6 +194,7 @@ export class WsTunnelBuilder {
|
|
|
187
194
|
samplesIndexPath: this._samplesIndexPath,
|
|
188
195
|
staticMounts: this._staticMounts.length > 0 ? [...this._staticMounts] : undefined,
|
|
189
196
|
stdioUpstreams: this._stdioUpstreams.length > 0 ? [...this._stdioUpstreams] : undefined,
|
|
197
|
+
remoteUpstreams: this._remoteUpstreams.length > 0 ? [...this._remoteUpstreams] : undefined,
|
|
190
198
|
stdioClient: this._stdioClient,
|
|
191
199
|
tls: this._tls,
|
|
192
200
|
brokerLocalGrammarsDir: this._brokerLocalGrammarsDir,
|
|
@@ -1 +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;
|
|
1
|
+
{"version":3,"file":"ws.tunnel.builder.js","sourceRoot":"","sources":["../src/ws.tunnel.builder.ts"],"names":[],"mappings":"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"}
|