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/jsr.json CHANGED
@@ -3,5 +3,5 @@
3
3
  "include": ["src/index.ts", "src/bin/mcp-proxy.ts"],
4
4
  "license": "MIT",
5
5
  "name": "@punkpeye/mcp-proxy",
6
- "version": "6.4.3"
6
+ "version": "6.4.5"
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-proxy",
3
- "version": "6.4.3",
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.24.3",
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",
@@ -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
 
@@ -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
 
@@ -938,7 +938,16 @@ export const startHTTPServer = async <T extends ServerLike>({
938
938
  return;
939
939
  }
940
940
 
941
- // Check authentication for all other endpoints
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
- if (onUnhandledRequest) {
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;
@@ -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()