mcp-proxy 6.4.3 → 6.4.5
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/bin/mcp-proxy.mjs +1 -1
- package/dist/bin/mcp-proxy.mjs.map +1 -1
- package/dist/index.mjs +169 -145
- package/dist/index.mjs.map +1 -1
- package/dist/{stdio-CvFTizsx.mjs → stdio-_93Y9W6l.mjs} +1562 -751
- package/dist/stdio-_93Y9W6l.mjs.map +1 -0
- package/jsr.json +1 -1
- package/package.json +2 -2
- package/src/bin/mcp-proxy.ts +2 -3
- package/src/proxyServer.test.ts +2 -4
- package/src/startHTTPServer.test.ts +72 -51
- package/src/startHTTPServer.ts +11 -6
- package/src/startStdioServer.ts +2 -3
- package/dist/stdio-CvFTizsx.mjs.map +0 -1
package/jsr.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-proxy",
|
|
3
|
-
"version": "6.4.
|
|
3
|
+
"version": "6.4.5",
|
|
4
4
|
"main": "dist/index.mjs",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "tsdown",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@eslint/js": "^9.39.1",
|
|
42
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
42
|
+
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
43
43
|
"@sebbo2002/semantic-release-jsr": "^3.1.0",
|
|
44
44
|
"@tsconfig/node24": "^24.0.3",
|
|
45
45
|
"@types/express": "^5.0.6",
|
package/src/bin/mcp-proxy.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
4
4
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
5
|
+
import { ServerCapabilities } from "@modelcontextprotocol/sdk/types.js";
|
|
5
6
|
import { EventSource } from "eventsource";
|
|
6
7
|
import { createRequire } from "node:module";
|
|
7
8
|
import { setTimeout } from "node:timers";
|
|
@@ -196,9 +197,7 @@ const proxy = async () => {
|
|
|
196
197
|
version: string;
|
|
197
198
|
};
|
|
198
199
|
|
|
199
|
-
const serverCapabilities = client.getServerCapabilities() as
|
|
200
|
-
capabilities: Record<string, unknown>;
|
|
201
|
-
};
|
|
200
|
+
const serverCapabilities = client.getServerCapabilities() as ServerCapabilities;
|
|
202
201
|
|
|
203
202
|
console.info("starting server on port %d", argv.port);
|
|
204
203
|
|
package/src/proxyServer.test.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
|
2
2
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
3
3
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
4
4
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
5
|
-
import { McpError } from "@modelcontextprotocol/sdk/types.js";
|
|
5
|
+
import { McpError, ServerCapabilities } from "@modelcontextprotocol/sdk/types.js";
|
|
6
6
|
import { EventSource } from "eventsource";
|
|
7
7
|
import { getRandomPort } from "get-port-please";
|
|
8
8
|
import { describe, expect, it } from "vitest";
|
|
@@ -59,9 +59,7 @@ async function createTestEnvironment(
|
|
|
59
59
|
name: string;
|
|
60
60
|
version: string;
|
|
61
61
|
};
|
|
62
|
-
const serverCapabilities = stdioClient.getServerCapabilities() as
|
|
63
|
-
capabilities: Record<string, unknown>;
|
|
64
|
-
};
|
|
62
|
+
const serverCapabilities = stdioClient.getServerCapabilities() as ServerCapabilities;
|
|
65
63
|
const port = await getRandomPort();
|
|
66
64
|
|
|
67
65
|
const httpServer = await startHTTPServer({
|
|
@@ -3,6 +3,7 @@ import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
|
3
3
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
4
4
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
5
5
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
6
|
+
import { ServerCapabilities } from "@modelcontextprotocol/sdk/types.js";
|
|
6
7
|
import { EventSource } from "eventsource";
|
|
7
8
|
import fs from "fs";
|
|
8
9
|
import { getRandomPort } from "get-port-please";
|
|
@@ -42,9 +43,7 @@ it("proxies messages between HTTP stream and stdio servers", async () => {
|
|
|
42
43
|
version: string;
|
|
43
44
|
};
|
|
44
45
|
|
|
45
|
-
const serverCapabilities = stdioClient.getServerCapabilities() as
|
|
46
|
-
capabilities: Record<string, unknown>;
|
|
47
|
-
};
|
|
46
|
+
const serverCapabilities = stdioClient.getServerCapabilities() as ServerCapabilities;
|
|
48
47
|
|
|
49
48
|
const port = await getRandomPort();
|
|
50
49
|
|
|
@@ -155,9 +154,7 @@ it("proxies messages between SSE and stdio servers", async () => {
|
|
|
155
154
|
version: string;
|
|
156
155
|
};
|
|
157
156
|
|
|
158
|
-
const serverCapabilities = stdioClient.getServerCapabilities() as
|
|
159
|
-
capabilities: Record<string, unknown>;
|
|
160
|
-
};
|
|
157
|
+
const serverCapabilities = stdioClient.getServerCapabilities() as ServerCapabilities;
|
|
161
158
|
|
|
162
159
|
const port = await getRandomPort();
|
|
163
160
|
|
|
@@ -265,9 +262,7 @@ it("supports stateless HTTP streamable transport", async () => {
|
|
|
265
262
|
version: string;
|
|
266
263
|
};
|
|
267
264
|
|
|
268
|
-
const serverCapabilities = stdioClient.getServerCapabilities() as
|
|
269
|
-
capabilities: Record<string, unknown>;
|
|
270
|
-
};
|
|
265
|
+
const serverCapabilities = stdioClient.getServerCapabilities() as ServerCapabilities;
|
|
271
266
|
|
|
272
267
|
const port = await getRandomPort();
|
|
273
268
|
|
|
@@ -354,9 +349,7 @@ it("allows requests when no auth is configured", async () => {
|
|
|
354
349
|
version: string;
|
|
355
350
|
};
|
|
356
351
|
|
|
357
|
-
const serverCapabilities = stdioClient.getServerCapabilities() as
|
|
358
|
-
capabilities: Record<string, unknown>;
|
|
359
|
-
};
|
|
352
|
+
const serverCapabilities = stdioClient.getServerCapabilities() as ServerCapabilities;
|
|
360
353
|
|
|
361
354
|
const port = await getRandomPort();
|
|
362
355
|
|
|
@@ -434,9 +427,7 @@ it("rejects requests without API key when auth is enabled", async () => {
|
|
|
434
427
|
version: string;
|
|
435
428
|
};
|
|
436
429
|
|
|
437
|
-
const serverCapabilities = stdioClient.getServerCapabilities() as
|
|
438
|
-
capabilities: Record<string, unknown>;
|
|
439
|
-
};
|
|
430
|
+
const serverCapabilities = stdioClient.getServerCapabilities() as ServerCapabilities;
|
|
440
431
|
|
|
441
432
|
const port = await getRandomPort();
|
|
442
433
|
|
|
@@ -503,9 +494,7 @@ it("accepts requests with valid API key", async () => {
|
|
|
503
494
|
version: string;
|
|
504
495
|
};
|
|
505
496
|
|
|
506
|
-
const serverCapabilities = stdioClient.getServerCapabilities() as
|
|
507
|
-
capabilities: Record<string, unknown>;
|
|
508
|
-
};
|
|
497
|
+
const serverCapabilities = stdioClient.getServerCapabilities() as ServerCapabilities;
|
|
509
498
|
|
|
510
499
|
const port = await getRandomPort();
|
|
511
500
|
const apiKey = "test-api-key-123";
|
|
@@ -591,9 +580,7 @@ it("works with SSE transport and authentication", async () => {
|
|
|
591
580
|
version: string;
|
|
592
581
|
};
|
|
593
582
|
|
|
594
|
-
const serverCapabilities = stdioClient.getServerCapabilities() as
|
|
595
|
-
capabilities: Record<string, unknown>;
|
|
596
|
-
};
|
|
583
|
+
const serverCapabilities = stdioClient.getServerCapabilities() as ServerCapabilities;
|
|
597
584
|
|
|
598
585
|
const port = await getRandomPort();
|
|
599
586
|
const apiKey = "test-api-key-456";
|
|
@@ -705,6 +692,60 @@ it("does not require auth for OPTIONS requests", async () => {
|
|
|
705
692
|
await httpServer.close();
|
|
706
693
|
});
|
|
707
694
|
|
|
695
|
+
it("allows onUnhandledRequest to serve routes without auth", async () => {
|
|
696
|
+
const port = await getRandomPort();
|
|
697
|
+
const apiKey = "test-api-key-unhandled";
|
|
698
|
+
|
|
699
|
+
const httpServer = await startHTTPServer({
|
|
700
|
+
apiKey,
|
|
701
|
+
createServer: async () => {
|
|
702
|
+
const mcpServer = new Server(
|
|
703
|
+
{ name: "test", version: "1.0.0" },
|
|
704
|
+
{ capabilities: {} },
|
|
705
|
+
);
|
|
706
|
+
return mcpServer;
|
|
707
|
+
},
|
|
708
|
+
onUnhandledRequest: async (req, res) => {
|
|
709
|
+
if (req.url === "/health") {
|
|
710
|
+
res.writeHead(200).end("ok");
|
|
711
|
+
} else if (req.url === "/ready") {
|
|
712
|
+
res.writeHead(200).end("ready");
|
|
713
|
+
}
|
|
714
|
+
// Don't write response for unknown paths — fall through to MCP handlers
|
|
715
|
+
},
|
|
716
|
+
port,
|
|
717
|
+
});
|
|
718
|
+
|
|
719
|
+
// /health works without auth
|
|
720
|
+
const healthResponse = await fetch(`http://localhost:${port}/health`);
|
|
721
|
+
expect(healthResponse.status).toBe(200);
|
|
722
|
+
expect(await healthResponse.text()).toBe("ok");
|
|
723
|
+
|
|
724
|
+
// /ready works without auth
|
|
725
|
+
const readyResponse = await fetch(`http://localhost:${port}/ready`);
|
|
726
|
+
expect(readyResponse.status).toBe(200);
|
|
727
|
+
expect(await readyResponse.text()).toBe("ready");
|
|
728
|
+
|
|
729
|
+
// POST /mcp without auth still returns 401
|
|
730
|
+
const mcpResponse = await fetch(`http://localhost:${port}/mcp`, {
|
|
731
|
+
body: JSON.stringify({
|
|
732
|
+
id: 1,
|
|
733
|
+
jsonrpc: "2.0",
|
|
734
|
+
method: "initialize",
|
|
735
|
+
params: {
|
|
736
|
+
capabilities: {},
|
|
737
|
+
clientInfo: { name: "test", version: "1.0.0" },
|
|
738
|
+
protocolVersion: "2025-03-26",
|
|
739
|
+
},
|
|
740
|
+
}),
|
|
741
|
+
headers: { "Content-Type": "application/json" },
|
|
742
|
+
method: "POST",
|
|
743
|
+
});
|
|
744
|
+
expect(mcpResponse.status).toBe(401);
|
|
745
|
+
|
|
746
|
+
await httpServer.close();
|
|
747
|
+
});
|
|
748
|
+
|
|
708
749
|
// Stateless OAuth 2.0 JWT Bearer Token Authentication Tests (PR #37)
|
|
709
750
|
|
|
710
751
|
it("accepts requests with valid Bearer token in stateless mode", async () => {
|
|
@@ -730,9 +771,7 @@ it("accepts requests with valid Bearer token in stateless mode", async () => {
|
|
|
730
771
|
version: string;
|
|
731
772
|
};
|
|
732
773
|
|
|
733
|
-
const serverCapabilities = stdioClient.getServerCapabilities() as
|
|
734
|
-
capabilities: Record<string, unknown>;
|
|
735
|
-
};
|
|
774
|
+
const serverCapabilities = stdioClient.getServerCapabilities() as ServerCapabilities;
|
|
736
775
|
|
|
737
776
|
const port = await getRandomPort();
|
|
738
777
|
|
|
@@ -825,9 +864,7 @@ it("returns 401 when authenticate callback returns null in stateless mode", asyn
|
|
|
825
864
|
version: string;
|
|
826
865
|
};
|
|
827
866
|
|
|
828
|
-
const serverCapabilities = stdioClient.getServerCapabilities() as
|
|
829
|
-
capabilities: Record<string, unknown>;
|
|
830
|
-
};
|
|
867
|
+
const serverCapabilities = stdioClient.getServerCapabilities() as ServerCapabilities;
|
|
831
868
|
|
|
832
869
|
const port = await getRandomPort();
|
|
833
870
|
|
|
@@ -908,9 +945,7 @@ it("returns 401 when authenticate callback throws error in stateless mode", asyn
|
|
|
908
945
|
version: string;
|
|
909
946
|
};
|
|
910
947
|
|
|
911
|
-
const serverCapabilities = stdioClient.getServerCapabilities() as
|
|
912
|
-
capabilities: Record<string, unknown>;
|
|
913
|
-
};
|
|
948
|
+
const serverCapabilities = stdioClient.getServerCapabilities() as ServerCapabilities;
|
|
914
949
|
|
|
915
950
|
const port = await getRandomPort();
|
|
916
951
|
|
|
@@ -993,9 +1028,7 @@ it("calls authenticate on every request in stateful mode", async () => {
|
|
|
993
1028
|
version: string;
|
|
994
1029
|
};
|
|
995
1030
|
|
|
996
|
-
const serverCapabilities = stdioClient.getServerCapabilities() as
|
|
997
|
-
capabilities: Record<string, unknown>;
|
|
998
|
-
};
|
|
1031
|
+
const serverCapabilities = stdioClient.getServerCapabilities() as ServerCapabilities;
|
|
999
1032
|
|
|
1000
1033
|
const port = await getRandomPort();
|
|
1001
1034
|
|
|
@@ -1083,9 +1116,7 @@ it("calls authenticate on every request in stateless mode", async () => {
|
|
|
1083
1116
|
version: string;
|
|
1084
1117
|
};
|
|
1085
1118
|
|
|
1086
|
-
const serverCapabilities = stdioClient.getServerCapabilities() as
|
|
1087
|
-
capabilities: Record<string, unknown>;
|
|
1088
|
-
};
|
|
1119
|
+
const serverCapabilities = stdioClient.getServerCapabilities() as ServerCapabilities;
|
|
1089
1120
|
|
|
1090
1121
|
const port = await getRandomPort();
|
|
1091
1122
|
|
|
@@ -1211,9 +1242,7 @@ it("returns 401 when authenticate callback returns { authenticated: false } in s
|
|
|
1211
1242
|
version: string;
|
|
1212
1243
|
};
|
|
1213
1244
|
|
|
1214
|
-
const serverCapabilities = stdioClient.getServerCapabilities() as
|
|
1215
|
-
capabilities: Record<string, unknown>;
|
|
1216
|
-
};
|
|
1245
|
+
const serverCapabilities = stdioClient.getServerCapabilities() as ServerCapabilities;
|
|
1217
1246
|
|
|
1218
1247
|
const port = await getRandomPort();
|
|
1219
1248
|
|
|
@@ -1297,9 +1326,7 @@ it("returns 401 with custom error message when { authenticated: false, error: '.
|
|
|
1297
1326
|
version: string;
|
|
1298
1327
|
};
|
|
1299
1328
|
|
|
1300
|
-
const serverCapabilities = stdioClient.getServerCapabilities() as
|
|
1301
|
-
capabilities: Record<string, unknown>;
|
|
1302
|
-
};
|
|
1329
|
+
const serverCapabilities = stdioClient.getServerCapabilities() as ServerCapabilities;
|
|
1303
1330
|
|
|
1304
1331
|
const port = await getRandomPort();
|
|
1305
1332
|
|
|
@@ -1766,9 +1793,7 @@ it("succeeds when authenticate returns { authenticated: true } in stateless mode
|
|
|
1766
1793
|
version: string;
|
|
1767
1794
|
};
|
|
1768
1795
|
|
|
1769
|
-
const serverCapabilities = stdioClient.getServerCapabilities() as
|
|
1770
|
-
capabilities: Record<string, unknown>;
|
|
1771
|
-
};
|
|
1796
|
+
const serverCapabilities = stdioClient.getServerCapabilities() as ServerCapabilities;
|
|
1772
1797
|
|
|
1773
1798
|
const port = await getRandomPort();
|
|
1774
1799
|
|
|
@@ -2167,9 +2192,7 @@ it("DELETE request terminates session cleanly and calls onClose exactly once", a
|
|
|
2167
2192
|
name: string;
|
|
2168
2193
|
version: string;
|
|
2169
2194
|
};
|
|
2170
|
-
const serverCapabilities = stdioClient.getServerCapabilities() as
|
|
2171
|
-
capabilities: Record<string, unknown>;
|
|
2172
|
-
};
|
|
2195
|
+
const serverCapabilities = stdioClient.getServerCapabilities() as ServerCapabilities;
|
|
2173
2196
|
|
|
2174
2197
|
const port = await getRandomPort();
|
|
2175
2198
|
const onClose = vi.fn().mockResolvedValue(undefined);
|
|
@@ -2240,9 +2263,7 @@ it("DELETE request to non-existent session returns 400", async () => {
|
|
|
2240
2263
|
name: string;
|
|
2241
2264
|
version: string;
|
|
2242
2265
|
};
|
|
2243
|
-
const serverCapabilities = stdioClient.getServerCapabilities() as
|
|
2244
|
-
capabilities: Record<string, unknown>;
|
|
2245
|
-
};
|
|
2266
|
+
const serverCapabilities = stdioClient.getServerCapabilities() as ServerCapabilities;
|
|
2246
2267
|
|
|
2247
2268
|
const port = await getRandomPort();
|
|
2248
2269
|
|
package/src/startHTTPServer.ts
CHANGED
|
@@ -938,7 +938,16 @@ export const startHTTPServer = async <T extends ServerLike>({
|
|
|
938
938
|
return;
|
|
939
939
|
}
|
|
940
940
|
|
|
941
|
-
//
|
|
941
|
+
// Let non-MCP routes (e.g. /health, /ready, OAuth metadata) be handled
|
|
942
|
+
// before auth — API key auth protects MCP protocol endpoints, not custom routes.
|
|
943
|
+
if (onUnhandledRequest) {
|
|
944
|
+
await onUnhandledRequest(req, res);
|
|
945
|
+
if (res.writableEnded) {
|
|
946
|
+
return;
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
// Check authentication for MCP protocol endpoints
|
|
942
951
|
if (!authMiddleware.validateRequest(req)) {
|
|
943
952
|
const authResponse = authMiddleware.getUnauthorizedResponse();
|
|
944
953
|
res.writeHead(401, authResponse.headers);
|
|
@@ -982,11 +991,7 @@ export const startHTTPServer = async <T extends ServerLike>({
|
|
|
982
991
|
return;
|
|
983
992
|
}
|
|
984
993
|
|
|
985
|
-
|
|
986
|
-
await onUnhandledRequest(req, res);
|
|
987
|
-
} else {
|
|
988
|
-
res.writeHead(404).end();
|
|
989
|
-
}
|
|
994
|
+
res.writeHead(404).end();
|
|
990
995
|
};
|
|
991
996
|
|
|
992
997
|
let httpServer;
|
package/src/startStdioServer.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { StreamableHTTPClientTransportOptions } from "@modelcontextprotocol/sdk/
|
|
|
5
5
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
6
6
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
7
7
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
8
|
+
import { ServerCapabilities } from "@modelcontextprotocol/sdk/types.js";
|
|
8
9
|
|
|
9
10
|
import { proxyServer } from "./proxyServer.js";
|
|
10
11
|
|
|
@@ -58,9 +59,7 @@ export const startStdioServer = async ({
|
|
|
58
59
|
version: string;
|
|
59
60
|
};
|
|
60
61
|
|
|
61
|
-
const serverCapabilities = streamClient.getServerCapabilities() as
|
|
62
|
-
capabilities: Record<string, unknown>;
|
|
63
|
-
};
|
|
62
|
+
const serverCapabilities = streamClient.getServerCapabilities() as ServerCapabilities;
|
|
64
63
|
|
|
65
64
|
const stdioServer = initStdioServer
|
|
66
65
|
? await initStdioServer()
|