mcp-proxy 6.5.2 → 6.5.3
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/LICENSE +21 -20
- package/README.md +2 -0
- package/dist/bin/mcp-proxy.mjs +7 -1
- package/dist/bin/mcp-proxy.mjs.map +1 -1
- package/dist/index.d.mts +2 -0
- package/dist/index.mjs +1 -1
- package/dist/{stdio-DLwYts_5.mjs → stdio-DNR9B0BZ.mjs} +5 -2
- package/dist/{stdio-DLwYts_5.mjs.map → stdio-DNR9B0BZ.mjs.map} +1 -1
- package/jsr.json +1 -1
- package/package.json +1 -1
- package/src/bin/mcp-proxy.ts +7 -0
- package/src/startHTTPServer.test.ts +67 -0
- package/src/startHTTPServer.ts +12 -0
package/jsr.json
CHANGED
package/package.json
CHANGED
package/src/bin/mcp-proxy.ts
CHANGED
|
@@ -80,6 +80,12 @@ const argv = await yargs(hideBin(process.argv))
|
|
|
80
80
|
describe: "The host to listen on",
|
|
81
81
|
type: "string",
|
|
82
82
|
},
|
|
83
|
+
keepAliveTimeout: {
|
|
84
|
+
default: 300000,
|
|
85
|
+
describe:
|
|
86
|
+
"The HTTP keep-alive timeout in milliseconds for stateful stream sessions (default: 5 minutes)",
|
|
87
|
+
type: "number",
|
|
88
|
+
},
|
|
83
89
|
port: {
|
|
84
90
|
default: 8080,
|
|
85
91
|
describe: "The port to listen on",
|
|
@@ -244,6 +250,7 @@ const proxy = async () => {
|
|
|
244
250
|
createServer,
|
|
245
251
|
eventStore: new InMemoryEventStore(),
|
|
246
252
|
host: argv.host,
|
|
253
|
+
keepAliveTimeout: argv.keepAliveTimeout,
|
|
247
254
|
port: argv.port,
|
|
248
255
|
sseEndpoint:
|
|
249
256
|
argv.server && argv.server !== "sse"
|
|
@@ -131,6 +131,73 @@ it("proxies messages between HTTP stream and stdio servers", async () => {
|
|
|
131
131
|
expect(onClose).toHaveBeenCalled();
|
|
132
132
|
});
|
|
133
133
|
|
|
134
|
+
it(
|
|
135
|
+
"keeps stateful HTTP stream sessions alive after idle keep-alive timeout window",
|
|
136
|
+
async () => {
|
|
137
|
+
const port = await getRandomPort();
|
|
138
|
+
const onClose = vi.fn().mockResolvedValue(undefined);
|
|
139
|
+
|
|
140
|
+
const httpServer = await startHTTPServer({
|
|
141
|
+
createServer: async () => {
|
|
142
|
+
return new Server(
|
|
143
|
+
{ name: "test", version: "1.0.0" },
|
|
144
|
+
{ capabilities: {} },
|
|
145
|
+
);
|
|
146
|
+
},
|
|
147
|
+
onClose,
|
|
148
|
+
port,
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
const initializeResponse = await fetch(`http://localhost:${port}/mcp`, {
|
|
152
|
+
body: JSON.stringify({
|
|
153
|
+
id: 1,
|
|
154
|
+
jsonrpc: "2.0",
|
|
155
|
+
method: "initialize",
|
|
156
|
+
params: {
|
|
157
|
+
capabilities: {},
|
|
158
|
+
clientInfo: { name: "test", version: "1.0.0" },
|
|
159
|
+
protocolVersion: "2025-03-26",
|
|
160
|
+
},
|
|
161
|
+
}),
|
|
162
|
+
headers: {
|
|
163
|
+
Accept: "application/json, text/event-stream",
|
|
164
|
+
"Content-Type": "application/json",
|
|
165
|
+
},
|
|
166
|
+
method: "POST",
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
expect(initializeResponse.status).toBe(200);
|
|
170
|
+
const sessionId = initializeResponse.headers.get("mcp-session-id");
|
|
171
|
+
expect(sessionId).toBeTruthy();
|
|
172
|
+
await initializeResponse.text();
|
|
173
|
+
|
|
174
|
+
await delay(6_000);
|
|
175
|
+
|
|
176
|
+
const listToolsResponse = await fetch(`http://localhost:${port}/mcp`, {
|
|
177
|
+
body: JSON.stringify({
|
|
178
|
+
id: 2,
|
|
179
|
+
jsonrpc: "2.0",
|
|
180
|
+
method: "tools/list",
|
|
181
|
+
params: {},
|
|
182
|
+
}),
|
|
183
|
+
headers: {
|
|
184
|
+
Accept: "application/json, text/event-stream",
|
|
185
|
+
"Content-Type": "application/json",
|
|
186
|
+
"mcp-session-id": sessionId!,
|
|
187
|
+
},
|
|
188
|
+
method: "POST",
|
|
189
|
+
});
|
|
190
|
+
const listToolsBody = await listToolsResponse.text();
|
|
191
|
+
|
|
192
|
+
expect(listToolsResponse.status).not.toBe(404);
|
|
193
|
+
expect(listToolsBody).not.toContain("Session not found");
|
|
194
|
+
expect(onClose).not.toHaveBeenCalled();
|
|
195
|
+
|
|
196
|
+
await httpServer.close();
|
|
197
|
+
},
|
|
198
|
+
15_000,
|
|
199
|
+
);
|
|
200
|
+
|
|
134
201
|
it("proxies messages between SSE and stdio servers", async () => {
|
|
135
202
|
const stdioTransport = new StdioClientTransport({
|
|
136
203
|
args: ["src/fixtures/simple-stdio-server.ts"],
|
package/src/startHTTPServer.ts
CHANGED
|
@@ -13,6 +13,8 @@ import { randomUUID } from "node:crypto";
|
|
|
13
13
|
import { AuthConfig, AuthenticationMiddleware } from "./authentication.js";
|
|
14
14
|
import { InMemoryEventStore } from "./InMemoryEventStore.js";
|
|
15
15
|
|
|
16
|
+
const DEFAULT_KEEP_ALIVE_TIMEOUT = 300_000;
|
|
17
|
+
|
|
16
18
|
export interface CorsOptions {
|
|
17
19
|
allowedHeaders?: string | string[]; // Allow string[] or '*' for wildcard
|
|
18
20
|
credentials?: boolean;
|
|
@@ -881,6 +883,7 @@ export const startHTTPServer = async <T extends ServerLike>({
|
|
|
881
883
|
enableJsonResponse,
|
|
882
884
|
eventStore,
|
|
883
885
|
host = "::",
|
|
886
|
+
keepAliveTimeout = DEFAULT_KEEP_ALIVE_TIMEOUT,
|
|
884
887
|
oauth,
|
|
885
888
|
onClose,
|
|
886
889
|
onConnect,
|
|
@@ -900,6 +903,7 @@ export const startHTTPServer = async <T extends ServerLike>({
|
|
|
900
903
|
enableJsonResponse?: boolean;
|
|
901
904
|
eventStore?: EventStore;
|
|
902
905
|
host?: string;
|
|
906
|
+
keepAliveTimeout?: number;
|
|
903
907
|
oauth?: AuthConfig["oauth"];
|
|
904
908
|
onClose?: (server: T) => Promise<void>;
|
|
905
909
|
onConnect?: (server: T) => Promise<void>;
|
|
@@ -1055,6 +1059,14 @@ export const startHTTPServer = async <T extends ServerLike>({
|
|
|
1055
1059
|
httpServer = http.createServer(requestListener);
|
|
1056
1060
|
}
|
|
1057
1061
|
|
|
1062
|
+
// Keep stateful stream sessions from being torn down when Node closes
|
|
1063
|
+
// otherwise-idle HTTP keep-alive sockets after its 5 second default.
|
|
1064
|
+
httpServer.keepAliveTimeout = keepAliveTimeout;
|
|
1065
|
+
httpServer.headersTimeout = Math.max(
|
|
1066
|
+
httpServer.headersTimeout,
|
|
1067
|
+
keepAliveTimeout + 1000,
|
|
1068
|
+
);
|
|
1069
|
+
|
|
1058
1070
|
await new Promise((resolve) => {
|
|
1059
1071
|
httpServer.listen(port, host, () => {
|
|
1060
1072
|
resolve(undefined);
|