mcp-proxy 2.12.2 → 2.13.1

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/dist/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  import {
2
+ InMemoryEventStore,
2
3
  proxyServer,
4
+ startHTTPStreamServer,
3
5
  startSSEServer
4
- } from "./chunk-HTMOSQ6H.js";
6
+ } from "./chunk-F2LFKNGG.js";
5
7
 
6
8
  // src/tapTransport.ts
7
9
  var tapTransport = (transport, eventHandler) => {
@@ -25,22 +27,22 @@ var tapTransport = (transport, eventHandler) => {
25
27
  };
26
28
  transport.onerror = async (error) => {
27
29
  eventHandler({
28
- type: "onerror",
29
- error
30
+ error,
31
+ type: "onerror"
30
32
  });
31
33
  return originalOnError?.(error);
32
34
  };
33
35
  transport.onmessage = async (message) => {
34
36
  eventHandler({
35
- type: "onmessage",
36
- message
37
+ message,
38
+ type: "onmessage"
37
39
  });
38
40
  return originalOnMessage?.(message);
39
41
  };
40
42
  transport.send = async (message) => {
41
43
  eventHandler({
42
- type: "send",
43
- message
44
+ message,
45
+ type: "send"
44
46
  });
45
47
  return originalSend?.(message);
46
48
  };
@@ -53,7 +55,9 @@ var tapTransport = (transport, eventHandler) => {
53
55
  return transport;
54
56
  };
55
57
  export {
58
+ InMemoryEventStore,
56
59
  proxyServer,
60
+ startHTTPStreamServer,
57
61
  startSSEServer,
58
62
  tapTransport
59
63
  };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/tapTransport.ts"],"sourcesContent":["import { Transport } from \"@modelcontextprotocol/sdk/shared/transport.js\";\nimport { JSONRPCMessage } from \"@modelcontextprotocol/sdk/types.js\";\n\ntype TransportEvent =\n | {\n type: \"close\";\n }\n | {\n type: \"onclose\";\n }\n | {\n type: \"onerror\";\n error: Error;\n }\n | {\n type: \"onmessage\";\n message: JSONRPCMessage;\n }\n | {\n type: \"send\";\n message: JSONRPCMessage;\n }\n | {\n type: \"start\";\n };\n\nexport const tapTransport = (\n transport: Transport,\n eventHandler: (event: TransportEvent) => void,\n) => {\n const originalClose = transport.close.bind(transport);\n const originalOnClose = transport.onclose?.bind(transport);\n const originalOnError = transport.onerror?.bind(transport);\n const originalOnMessage = transport.onmessage?.bind(transport);\n const originalSend = transport.send.bind(transport);\n const originalStart = transport.start.bind(transport);\n\n transport.close = async () => {\n eventHandler({\n type: \"close\",\n });\n\n return originalClose?.();\n };\n\n transport.onclose = async () => {\n eventHandler({\n type: \"onclose\",\n });\n\n return originalOnClose?.();\n };\n\n transport.onerror = async (error: Error) => {\n eventHandler({\n type: \"onerror\",\n error,\n });\n\n return originalOnError?.(error);\n };\n\n transport.onmessage = async (message: JSONRPCMessage) => {\n eventHandler({\n type: \"onmessage\",\n message,\n });\n\n return originalOnMessage?.(message);\n };\n\n transport.send = async (message: JSONRPCMessage) => {\n eventHandler({\n type: \"send\",\n message,\n });\n\n return originalSend?.(message);\n };\n\n transport.start = async () => {\n eventHandler({\n type: \"start\",\n });\n\n return originalStart?.();\n };\n\n return transport;\n};\n"],"mappings":";;;;;;AA0BO,IAAM,eAAe,CAC1B,WACA,iBACG;AACH,QAAM,gBAAgB,UAAU,MAAM,KAAK,SAAS;AACpD,QAAM,kBAAkB,UAAU,SAAS,KAAK,SAAS;AACzD,QAAM,kBAAkB,UAAU,SAAS,KAAK,SAAS;AACzD,QAAM,oBAAoB,UAAU,WAAW,KAAK,SAAS;AAC7D,QAAM,eAAe,UAAU,KAAK,KAAK,SAAS;AAClD,QAAM,gBAAgB,UAAU,MAAM,KAAK,SAAS;AAEpD,YAAU,QAAQ,YAAY;AAC5B,iBAAa;AAAA,MACX,MAAM;AAAA,IACR,CAAC;AAED,WAAO,gBAAgB;AAAA,EACzB;AAEA,YAAU,UAAU,YAAY;AAC9B,iBAAa;AAAA,MACX,MAAM;AAAA,IACR,CAAC;AAED,WAAO,kBAAkB;AAAA,EAC3B;AAEA,YAAU,UAAU,OAAO,UAAiB;AAC1C,iBAAa;AAAA,MACX,MAAM;AAAA,MACN;AAAA,IACF,CAAC;AAED,WAAO,kBAAkB,KAAK;AAAA,EAChC;AAEA,YAAU,YAAY,OAAO,YAA4B;AACvD,iBAAa;AAAA,MACX,MAAM;AAAA,MACN;AAAA,IACF,CAAC;AAED,WAAO,oBAAoB,OAAO;AAAA,EACpC;AAEA,YAAU,OAAO,OAAO,YAA4B;AAClD,iBAAa;AAAA,MACX,MAAM;AAAA,MACN;AAAA,IACF,CAAC;AAED,WAAO,eAAe,OAAO;AAAA,EAC/B;AAEA,YAAU,QAAQ,YAAY;AAC5B,iBAAa;AAAA,MACX,MAAM;AAAA,IACR,CAAC;AAED,WAAO,gBAAgB;AAAA,EACzB;AAEA,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../src/tapTransport.ts"],"sourcesContent":["import { Transport } from \"@modelcontextprotocol/sdk/shared/transport.js\";\nimport { JSONRPCMessage } from \"@modelcontextprotocol/sdk/types.js\";\n\ntype TransportEvent =\n | {\n error: Error;\n type: \"onerror\";\n }\n | {\n message: JSONRPCMessage;\n type: \"onmessage\";\n }\n | {\n message: JSONRPCMessage;\n type: \"send\";\n }\n | {\n type: \"close\";\n }\n | {\n type: \"onclose\";\n }\n | {\n type: \"start\";\n };\n\nexport const tapTransport = (\n transport: Transport,\n eventHandler: (event: TransportEvent) => void,\n) => {\n const originalClose = transport.close.bind(transport);\n const originalOnClose = transport.onclose?.bind(transport);\n const originalOnError = transport.onerror?.bind(transport);\n const originalOnMessage = transport.onmessage?.bind(transport);\n const originalSend = transport.send.bind(transport);\n const originalStart = transport.start.bind(transport);\n\n transport.close = async () => {\n eventHandler({\n type: \"close\",\n });\n\n return originalClose?.();\n };\n\n transport.onclose = async () => {\n eventHandler({\n type: \"onclose\",\n });\n\n return originalOnClose?.();\n };\n\n transport.onerror = async (error: Error) => {\n eventHandler({\n error,\n type: \"onerror\",\n });\n\n return originalOnError?.(error);\n };\n\n transport.onmessage = async (message: JSONRPCMessage) => {\n eventHandler({\n message,\n type: \"onmessage\",\n });\n\n return originalOnMessage?.(message);\n };\n\n transport.send = async (message: JSONRPCMessage) => {\n eventHandler({\n message,\n type: \"send\",\n });\n\n return originalSend?.(message);\n };\n\n transport.start = async () => {\n eventHandler({\n type: \"start\",\n });\n\n return originalStart?.();\n };\n\n return transport;\n};\n"],"mappings":";;;;;;;;AA0BO,IAAM,eAAe,CAC1B,WACA,iBACG;AACH,QAAM,gBAAgB,UAAU,MAAM,KAAK,SAAS;AACpD,QAAM,kBAAkB,UAAU,SAAS,KAAK,SAAS;AACzD,QAAM,kBAAkB,UAAU,SAAS,KAAK,SAAS;AACzD,QAAM,oBAAoB,UAAU,WAAW,KAAK,SAAS;AAC7D,QAAM,eAAe,UAAU,KAAK,KAAK,SAAS;AAClD,QAAM,gBAAgB,UAAU,MAAM,KAAK,SAAS;AAEpD,YAAU,QAAQ,YAAY;AAC5B,iBAAa;AAAA,MACX,MAAM;AAAA,IACR,CAAC;AAED,WAAO,gBAAgB;AAAA,EACzB;AAEA,YAAU,UAAU,YAAY;AAC9B,iBAAa;AAAA,MACX,MAAM;AAAA,IACR,CAAC;AAED,WAAO,kBAAkB;AAAA,EAC3B;AAEA,YAAU,UAAU,OAAO,UAAiB;AAC1C,iBAAa;AAAA,MACX;AAAA,MACA,MAAM;AAAA,IACR,CAAC;AAED,WAAO,kBAAkB,KAAK;AAAA,EAChC;AAEA,YAAU,YAAY,OAAO,YAA4B;AACvD,iBAAa;AAAA,MACX;AAAA,MACA,MAAM;AAAA,IACR,CAAC;AAED,WAAO,oBAAoB,OAAO;AAAA,EACpC;AAEA,YAAU,OAAO,OAAO,YAA4B;AAClD,iBAAa;AAAA,MACX;AAAA,MACA,MAAM;AAAA,IACR,CAAC;AAED,WAAO,eAAe,OAAO;AAAA,EAC/B;AAEA,YAAU,QAAQ,YAAY;AAC5B,iBAAa;AAAA,MACX,MAAM;AAAA,IACR,CAAC;AAED,WAAO,gBAAgB;AAAA,EACzB;AAEA,SAAO;AACT;","names":[]}
@@ -0,0 +1,14 @@
1
+ import eslint from "@eslint/js";
2
+ import eslintConfigPrettier from "eslint-config-prettier/flat";
3
+ import perfectionist from "eslint-plugin-perfectionist";
4
+ import tseslint from "typescript-eslint";
5
+
6
+ export default tseslint.config(
7
+ eslint.configs.recommended,
8
+ tseslint.configs.recommended,
9
+ perfectionist.configs["recommended-alphabetical"],
10
+ eslintConfigPrettier,
11
+ {
12
+ ignores: ["**/*.js"],
13
+ },
14
+ );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-proxy",
3
- "version": "2.12.2",
3
+ "version": "2.13.1",
4
4
  "main": "dist/index.js",
5
5
  "scripts": {
6
6
  "build": "tsup",
@@ -42,18 +42,22 @@
42
42
  ]
43
43
  },
44
44
  "devDependencies": {
45
+ "@eslint/js": "^9.25.1",
45
46
  "@sebbo2002/semantic-release-jsr": "^2.0.5",
46
47
  "@tsconfig/node22": "^22.0.1",
47
48
  "@types/node": "^22.14.1",
48
49
  "@types/yargs": "^17.0.33",
49
50
  "eslint": "^9.25.0",
51
+ "eslint-config-prettier": "^10.1.2",
50
52
  "eslint-plugin-perfectionist": "^4.11.0",
51
53
  "get-port-please": "^3.1.2",
54
+ "jiti": "^2.4.2",
52
55
  "prettier": "^3.5.3",
53
56
  "semantic-release": "^24.2.3",
54
57
  "tsup": "^8.4.0",
55
58
  "tsx": "^4.19.3",
56
59
  "typescript": "^5.8.3",
60
+ "typescript-eslint": "^8.31.1",
57
61
  "vitest": "^3.1.1"
58
62
  },
59
63
  "tsup": {
@@ -0,0 +1,91 @@
1
+ /**
2
+ * This is a copy of the InMemoryEventStore from the typescript-sdk
3
+ * https://github.com/modelcontextprotocol/typescript-sdk/blob/main/src/inMemoryEventStore.ts
4
+ */
5
+
6
+ import type { EventStore } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
7
+ import type { JSONRPCMessage } from "@modelcontextprotocol/sdk/types.js";
8
+
9
+ /**
10
+ * Simple in-memory implementation of the EventStore interface for resumability
11
+ * This is primarily intended for examples and testing, not for production use
12
+ * where a persistent storage solution would be more appropriate.
13
+ */
14
+ export class InMemoryEventStore implements EventStore {
15
+ private events: Map<string, { message: JSONRPCMessage; streamId: string }> =
16
+ new Map();
17
+
18
+ /**
19
+ * Replays events that occurred after a specific event ID
20
+ * Implements EventStore.replayEventsAfter
21
+ */
22
+ async replayEventsAfter(
23
+ lastEventId: string,
24
+ {
25
+ send,
26
+ }: { send: (eventId: string, message: JSONRPCMessage) => Promise<void> },
27
+ ): Promise<string> {
28
+ if (!lastEventId || !this.events.has(lastEventId)) {
29
+ return "";
30
+ }
31
+
32
+ // Extract the stream ID from the event ID
33
+ const streamId = this.getStreamIdFromEventId(lastEventId);
34
+ if (!streamId) {
35
+ return "";
36
+ }
37
+
38
+ let foundLastEvent = false;
39
+
40
+ // Sort events by eventId for chronological ordering
41
+ const sortedEvents = [...this.events.entries()].sort((a, b) =>
42
+ a[0].localeCompare(b[0]),
43
+ );
44
+
45
+ for (const [
46
+ eventId,
47
+ { message, streamId: eventStreamId },
48
+ ] of sortedEvents) {
49
+ // Only include events from the same stream
50
+ if (eventStreamId !== streamId) {
51
+ continue;
52
+ }
53
+
54
+ // Start sending events after we find the lastEventId
55
+ if (eventId === lastEventId) {
56
+ foundLastEvent = true;
57
+ continue;
58
+ }
59
+
60
+ if (foundLastEvent) {
61
+ await send(eventId, message);
62
+ }
63
+ }
64
+ return streamId;
65
+ }
66
+
67
+ /**
68
+ * Stores an event with a generated event ID
69
+ * Implements EventStore.storeEvent
70
+ */
71
+ async storeEvent(streamId: string, message: JSONRPCMessage): Promise<string> {
72
+ const eventId = this.generateEventId(streamId);
73
+ this.events.set(eventId, { message, streamId });
74
+ return eventId;
75
+ }
76
+
77
+ /**
78
+ * Generates a unique event ID for a given stream ID
79
+ */
80
+ private generateEventId(streamId: string): string {
81
+ return `${streamId}_${Date.now()}_${Math.random().toString(36).substring(2, 10)}`;
82
+ }
83
+
84
+ /**
85
+ * Extracts the stream ID from an event ID
86
+ */
87
+ private getStreamIdFromEventId(eventId: string): string {
88
+ const parts = eventId.split("_");
89
+ return parts.length > 0 ? parts[0] : "";
90
+ }
91
+ }
@@ -11,33 +11,23 @@ import { JSONRPCMessage } from "@modelcontextprotocol/sdk/types.js";
11
11
  import { ChildProcess, IOType, spawn } from "node:child_process";
12
12
  import { Stream } from "node:stream";
13
13
 
14
- type TransportEvent =
15
- | {
16
- type: "close";
17
- }
18
- | {
19
- type: "error";
20
- error: Error;
21
- }
22
- | {
23
- type: "data";
24
- chunk: string;
25
- }
26
- | {
27
- type: "message";
28
- message: JSONRPCMessage;
29
- };
30
-
31
14
  export type StdioServerParameters = {
15
+ /**
16
+ * Command line arguments to pass to the executable.
17
+ */
18
+ args?: string[];
19
+
32
20
  /**
33
21
  * The executable to run to start the server.
34
22
  */
35
23
  command: string;
36
24
 
37
25
  /**
38
- * Command line arguments to pass to the executable.
26
+ * The working directory to use when spawning the process.
27
+ *
28
+ * If not specified, the current working directory will be inherited.
39
29
  */
40
- args?: string[];
30
+ cwd?: string;
41
31
 
42
32
  /**
43
33
  * The environment to use when spawning the process.
@@ -47,46 +37,90 @@ export type StdioServerParameters = {
47
37
  env: Record<string, string>;
48
38
 
49
39
  /**
50
- * How to handle stderr of the child process. This matches the semantics of Node's `child_process.spawn`.
51
- *
52
- * The default is "inherit", meaning messages to stderr will be printed to the parent process's stderr.
40
+ * A function to call when an event occurs.
53
41
  */
54
- stderr?: IOType | Stream | number;
42
+ onEvent?: (event: TransportEvent) => void;
55
43
 
56
44
  /**
57
- * The working directory to use when spawning the process.
45
+ * How to handle stderr of the child process. This matches the semantics of Node's `child_process.spawn`.
58
46
  *
59
- * If not specified, the current working directory will be inherited.
60
- */
61
- cwd?: string;
62
-
63
- /**
64
- * A function to call when an event occurs.
47
+ * The default is "inherit", meaning messages to stderr will be printed to the parent process's stderr.
65
48
  */
66
- onEvent?: (event: TransportEvent) => void;
49
+ stderr?: IOType | number | Stream;
67
50
  };
68
51
 
52
+ type TransportEvent =
53
+ | {
54
+ chunk: string;
55
+ type: "data";
56
+ }
57
+ | {
58
+ error: Error;
59
+ type: "error";
60
+ }
61
+ | {
62
+ message: JSONRPCMessage;
63
+ type: "message";
64
+ }
65
+ | {
66
+ type: "close";
67
+ };
68
+
69
69
  /**
70
70
  * Client transport for stdio: this will connect to a server by spawning a process and communicating with it over stdin/stdout.
71
71
  *
72
72
  * This transport is only available in Node.js environments.
73
73
  */
74
74
  export class StdioClientTransport implements Transport {
75
- private process?: ChildProcess;
76
- private abortController: AbortController = new AbortController();
77
- private readBuffer: ReadBuffer = new ReadBuffer();
78
- private serverParams: StdioServerParameters;
79
- private onEvent?: (event: TransportEvent) => void;
80
-
81
75
  onclose?: () => void;
82
76
  onerror?: (error: Error) => void;
83
77
  onmessage?: (message: JSONRPCMessage) => void;
78
+ /**
79
+ * The stderr stream of the child process, if `StdioServerParameters.stderr` was set to "pipe" or "overlapped".
80
+ *
81
+ * This is only available after the process has been started.
82
+ */
83
+ get stderr(): null | Stream {
84
+ return this.process?.stderr ?? null;
85
+ }
86
+ private abortController: AbortController = new AbortController();
87
+
88
+ private onEvent?: (event: TransportEvent) => void;
89
+ private process?: ChildProcess;
90
+ private readBuffer: ReadBuffer = new ReadBuffer();
91
+
92
+ private serverParams: StdioServerParameters;
84
93
 
85
94
  constructor(server: StdioServerParameters) {
86
95
  this.serverParams = server;
87
96
  this.onEvent = server.onEvent;
88
97
  }
89
98
 
99
+ async close(): Promise<void> {
100
+ this.onEvent?.({
101
+ type: "close",
102
+ });
103
+
104
+ this.abortController.abort();
105
+ this.process = undefined;
106
+ this.readBuffer.clear();
107
+ }
108
+
109
+ send(message: JSONRPCMessage): Promise<void> {
110
+ return new Promise((resolve) => {
111
+ if (!this.process?.stdin) {
112
+ throw new Error("Not connected");
113
+ }
114
+
115
+ const json = serializeMessage(message);
116
+ if (this.process.stdin.write(json)) {
117
+ resolve();
118
+ } else {
119
+ this.process.stdin.once("drain", resolve);
120
+ }
121
+ });
122
+ }
123
+
90
124
  /**
91
125
  * Starts the server process and prepares to communicate with it.
92
126
  */
@@ -102,11 +136,11 @@ export class StdioClientTransport implements Transport {
102
136
  this.serverParams.command,
103
137
  this.serverParams.args ?? [],
104
138
  {
139
+ cwd: this.serverParams.cwd,
105
140
  env: this.serverParams.env,
106
- stdio: ["pipe", "pipe", this.serverParams.stderr ?? "inherit"],
107
141
  shell: false,
108
142
  signal: this.abortController.signal,
109
- cwd: this.serverParams.cwd,
143
+ stdio: ["pipe", "pipe", this.serverParams.stderr ?? "inherit"],
110
144
  },
111
145
  );
112
146
 
@@ -125,6 +159,7 @@ export class StdioClientTransport implements Transport {
125
159
  resolve();
126
160
  });
127
161
 
162
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
128
163
  this.process.on("close", (_code) => {
129
164
  this.onEvent?.({
130
165
  type: "close",
@@ -136,8 +171,8 @@ export class StdioClientTransport implements Transport {
136
171
 
137
172
  this.process.stdin?.on("error", (error) => {
138
173
  this.onEvent?.({
139
- type: "error",
140
174
  error,
175
+ type: "error",
141
176
  });
142
177
 
143
178
  this.onerror?.(error);
@@ -145,8 +180,8 @@ export class StdioClientTransport implements Transport {
145
180
 
146
181
  this.process.stdout?.on("data", (chunk) => {
147
182
  this.onEvent?.({
148
- type: "data",
149
183
  chunk: chunk.toString(),
184
+ type: "data",
150
185
  });
151
186
 
152
187
  this.readBuffer.append(chunk);
@@ -155,8 +190,8 @@ export class StdioClientTransport implements Transport {
155
190
 
156
191
  this.process.stdout?.on("error", (error) => {
157
192
  this.onEvent?.({
158
- type: "error",
159
193
  error,
194
+ type: "error",
160
195
  });
161
196
 
162
197
  this.onerror?.(error);
@@ -164,15 +199,6 @@ export class StdioClientTransport implements Transport {
164
199
  });
165
200
  }
166
201
 
167
- /**
168
- * The stderr stream of the child process, if `StdioServerParameters.stderr` was set to "pipe" or "overlapped".
169
- *
170
- * This is only available after the process has been started.
171
- */
172
- get stderr(): Stream | null {
173
- return this.process?.stderr ?? null;
174
- }
175
-
176
202
  private processReadBuffer() {
177
203
  while (true) {
178
204
  try {
@@ -183,44 +209,19 @@ export class StdioClientTransport implements Transport {
183
209
  }
184
210
 
185
211
  this.onEvent?.({
186
- type: "message",
187
212
  message,
213
+ type: "message",
188
214
  });
189
215
 
190
216
  this.onmessage?.(message);
191
217
  } catch (error) {
192
218
  this.onEvent?.({
193
- type: "error",
194
219
  error: error as Error,
220
+ type: "error",
195
221
  });
196
222
 
197
223
  this.onerror?.(error as Error);
198
224
  }
199
225
  }
200
226
  }
201
-
202
- async close(): Promise<void> {
203
- this.onEvent?.({
204
- type: "close",
205
- });
206
-
207
- this.abortController.abort();
208
- this.process = undefined;
209
- this.readBuffer.clear();
210
- }
211
-
212
- send(message: JSONRPCMessage): Promise<void> {
213
- return new Promise((resolve) => {
214
- if (!this.process?.stdin) {
215
- throw new Error("Not connected");
216
- }
217
-
218
- const json = serializeMessage(message);
219
- if (this.process.stdin.write(json)) {
220
- resolve();
221
- } else {
222
- this.process.stdin.once("drain", resolve);
223
- }
224
- });
225
- }
226
227
  }
@@ -1,15 +1,18 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import yargs from "yargs";
4
- import { hideBin } from "yargs/helpers";
5
3
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
6
4
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
7
5
  import { EventSource } from "eventsource";
8
6
  import { setTimeout } from "node:timers";
9
- import { StdioClientTransport } from "../StdioClientTransport.js";
10
7
  import util from "node:util";
11
- import { startSSEServer } from "../startSSEServer.js";
8
+ import yargs from "yargs";
9
+ import { hideBin } from "yargs/helpers";
10
+
11
+ import { InMemoryEventStore } from "../InMemoryEventStore.js";
12
12
  import { proxyServer } from "../proxyServer.js";
13
+ import { startHTTPStreamServer } from "../startHTTPStreamServer.js";
14
+ import { startSSEServer } from "../startSSEServer.js";
15
+ import { StdioClientTransport } from "../StdioClientTransport.js";
13
16
 
14
17
  util.inspect.defaultOptions.depth = 8;
15
18
 
@@ -22,31 +25,36 @@ const argv = await yargs(hideBin(process.argv))
22
25
  .scriptName("mcp-proxy")
23
26
  .command("$0 <command> [args...]", "Run a command with MCP arguments")
24
27
  .positional("command", {
25
- type: "string",
26
- describe: "The command to run",
27
28
  demandOption: true,
29
+ describe: "The command to run",
30
+ type: "string",
28
31
  })
29
32
  .positional("args", {
30
- type: "string",
31
33
  array: true,
32
34
  describe: "The arguments to pass to the command",
35
+ type: "string",
33
36
  })
34
37
  .env("MCP_PROXY")
35
38
  .options({
36
39
  debug: {
37
- type: "boolean",
38
- describe: "Enable debug logging",
39
40
  default: false,
41
+ describe: "Enable debug logging",
42
+ type: "boolean",
40
43
  },
41
44
  endpoint: {
45
+ describe: "The endpoint to listen on",
42
46
  type: "string",
43
- describe: "The endpoint to listen on for SSE",
44
- default: "/sse",
45
47
  },
46
48
  port: {
47
- type: "number",
48
- describe: "The port to listen on for SSE",
49
49
  default: 8080,
50
+ describe: "The port to listen on",
51
+ type: "number",
52
+ },
53
+ server: {
54
+ choices: ["sse", "stream"],
55
+ default: "sse",
56
+ describe: "The server type to use (sse or stream)",
57
+ type: "string",
50
58
  },
51
59
  })
52
60
  .help()
@@ -54,15 +62,15 @@ const argv = await yargs(hideBin(process.argv))
54
62
 
55
63
  const connect = async (client: Client) => {
56
64
  const transport = new StdioClientTransport({
57
- command: argv.command,
58
65
  args: argv.args,
66
+ command: argv.command,
59
67
  env: process.env as Record<string, string>,
60
- stderr: "pipe",
61
68
  onEvent: (event) => {
62
69
  if (argv.debug) {
63
70
  console.debug("transport event", event);
64
71
  }
65
72
  },
73
+ stderr: "pipe",
66
74
  });
67
75
 
68
76
  await client.connect(transport);
@@ -86,27 +94,40 @@ const proxy = async () => {
86
94
  version: string;
87
95
  };
88
96
 
89
- const serverCapabilities = client.getServerCapabilities() as {};
97
+ const serverCapabilities = client.getServerCapabilities() as {
98
+ capabilities: Record<string, unknown>;
99
+ };
90
100
 
91
- console.info("starting the SSE server on port %d", argv.port);
101
+ console.info("starting the %s server on port %d", argv.server, argv.port);
92
102
 
93
- await startSSEServer({
94
- createServer: async () => {
95
- const server = new Server(serverVersion, {
96
- capabilities: serverCapabilities,
97
- });
103
+ const createServer = async () => {
104
+ const server = new Server(serverVersion, {
105
+ capabilities: serverCapabilities,
106
+ });
98
107
 
99
- proxyServer({
100
- server,
101
- client,
102
- serverCapabilities,
103
- });
108
+ proxyServer({
109
+ client,
110
+ server,
111
+ serverCapabilities,
112
+ });
104
113
 
105
- return server;
106
- },
107
- port: argv.port,
108
- endpoint: argv.endpoint as `/${string}`,
109
- });
114
+ return server;
115
+ };
116
+
117
+ if (argv.server === "sse") {
118
+ await startSSEServer({
119
+ createServer,
120
+ endpoint: argv.endpoint || ("/sse" as `/${string}`),
121
+ port: argv.port,
122
+ });
123
+ } else {
124
+ await startHTTPStreamServer({
125
+ createServer,
126
+ endpoint: argv.endpoint || ("/stream" as `/${string}`),
127
+ eventStore: new InMemoryEventStore(),
128
+ port: argv.port,
129
+ });
130
+ }
110
131
  };
111
132
 
112
133
  const main = async () => {
package/src/index.ts CHANGED
@@ -1,3 +1,5 @@
1
- export { tapTransport } from "./tapTransport.js";
1
+ export { InMemoryEventStore } from "./InMemoryEventStore.js";
2
2
  export { proxyServer } from "./proxyServer.js";
3
+ export { startHTTPStreamServer } from "./startHTTPStreamServer.js";
3
4
  export { startSSEServer } from "./startSSEServer.js";
5
+ export { tapTransport } from "./tapTransport.js";
@@ -1,3 +1,4 @@
1
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
1
2
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
3
  import {
3
4
  CallToolRequestSchema,
@@ -9,20 +10,19 @@ import {
9
10
  ListToolsRequestSchema,
10
11
  LoggingMessageNotificationSchema,
11
12
  ReadResourceRequestSchema,
12
- SubscribeRequestSchema,
13
- UnsubscribeRequestSchema,
14
13
  ResourceUpdatedNotificationSchema,
15
14
  ServerCapabilities,
15
+ SubscribeRequestSchema,
16
+ UnsubscribeRequestSchema,
16
17
  } from "@modelcontextprotocol/sdk/types.js";
17
- import { Client } from "@modelcontextprotocol/sdk/client/index.js";
18
18
 
19
19
  export const proxyServer = async ({
20
- server,
21
20
  client,
21
+ server,
22
22
  serverCapabilities,
23
23
  }: {
24
- server: Server;
25
24
  client: Client;
25
+ server: Server;
26
26
  serverCapabilities: ServerCapabilities;
27
27
  }) => {
28
28
  if (serverCapabilities?.logging) {
@@ -62,10 +62,10 @@ export const proxyServer = async ({
62
62
 
63
63
  if (serverCapabilities?.resources.subscribe) {
64
64
  server.setNotificationHandler(
65
- ResourceUpdatedNotificationSchema,
66
- async (args) => {
67
- return client.notification(args);
68
- },
65
+ ResourceUpdatedNotificationSchema,
66
+ async (args) => {
67
+ return client.notification(args);
68
+ },
69
69
  );
70
70
 
71
71
  server.setRequestHandler(SubscribeRequestSchema, async (args) => {