mcp-proxy 5.5.4 → 5.5.6

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": "5.5.4"
6
+ "version": "5.5.6"
7
7
  }
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "mcp-proxy",
3
- "version": "5.5.4",
3
+ "version": "5.5.6",
4
4
  "main": "dist/index.js",
5
5
  "scripts": {
6
- "build": "tsup",
6
+ "build": "tsdown",
7
7
  "test": "vitest run && tsc && eslint . && jsr publish --dry-run --allow-dirty",
8
8
  "format": "eslint --fix ."
9
9
  },
@@ -21,11 +21,6 @@
21
21
  "description": "A TypeScript SSE proxy for MCP servers that use stdio transport.",
22
22
  "module": "dist/index.js",
23
23
  "types": "dist/index.d.ts",
24
- "dependencies": {
25
- "@modelcontextprotocol/sdk": "^1.17.2",
26
- "eventsource": "^4.0.0",
27
- "yargs": "^18.0.0"
28
- },
29
24
  "repository": {
30
25
  "url": "https://github.com/punkpeye/mcp-proxy"
31
26
  },
@@ -44,27 +39,30 @@
44
39
  },
45
40
  "devDependencies": {
46
41
  "@eslint/js": "^9.33.0",
42
+ "@modelcontextprotocol/sdk": "^1.17.3",
47
43
  "@sebbo2002/semantic-release-jsr": "^3.0.1",
48
44
  "@tsconfig/node22": "^22.0.2",
49
45
  "@types/express": "^5.0.3",
50
- "@types/node": "^24.2.1",
46
+ "@types/node": "^24.3.0",
51
47
  "@types/yargs": "^17.0.33",
52
48
  "eslint": "^9.33.0",
53
49
  "eslint-config-prettier": "^10.1.8",
54
50
  "eslint-plugin-perfectionist": "^4.15.0",
51
+ "eventsource": "^4.0.0",
55
52
  "express": "^5.0.1",
56
53
  "get-port-please": "^3.2.0",
57
54
  "jiti": "^2.5.1",
58
55
  "jsr": "^0.13.5",
59
56
  "prettier": "^3.6.2",
60
57
  "semantic-release": "^24.2.7",
61
- "tsup": "^8.5.0",
62
- "tsx": "^4.20.3",
58
+ "tsdown": "^0.14.2",
59
+ "tsx": "^4.20.4",
63
60
  "typescript": "^5.9.2",
64
- "typescript-eslint": "^8.39.0",
65
- "vitest": "^3.2.4"
61
+ "typescript-eslint": "^8.39.1",
62
+ "vitest": "^3.2.4",
63
+ "yargs": "^18.0.0"
66
64
  },
67
- "tsup": {
65
+ "tsdown": {
68
66
  "entry": [
69
67
  "src/index.ts",
70
68
  "src/bin/mcp-proxy.ts"
@@ -0,0 +1,160 @@
1
+ import type { JSONRPCMessage } from "@modelcontextprotocol/sdk/types.js";
2
+
3
+ import { describe, expect, it, vi } from "vitest";
4
+
5
+ import { InMemoryEventStore } from "./InMemoryEventStore.js";
6
+
7
+ describe("InMemoryEventStore", () => {
8
+ it("stores events and replays them after a specific event ID", async () => {
9
+ const store = new InMemoryEventStore();
10
+ const streamId = "test-stream-123";
11
+
12
+ // Create test messages
13
+ const messages: JSONRPCMessage[] = [
14
+ { id: 1, jsonrpc: "2.0", method: "initialize" },
15
+ { id: 2, jsonrpc: "2.0", method: "tools/list" },
16
+ { id: 3, jsonrpc: "2.0", method: "tools/call", params: { name: "test" } },
17
+ { id: 3, jsonrpc: "2.0", result: { success: true } },
18
+ { id: 4, jsonrpc: "2.0", method: "shutdown" },
19
+ ];
20
+
21
+ // Store all events and keep track of event IDs
22
+ // Add small delays to ensure different timestamps for proper ordering
23
+ const eventIds: string[] = [];
24
+ for (const message of messages) {
25
+ const eventId = await store.storeEvent(streamId, message);
26
+ expect(eventId).toContain(streamId);
27
+ eventIds.push(eventId);
28
+ // Small delay to ensure different timestamps
29
+ await new Promise(resolve => setTimeout(resolve, 1));
30
+ }
31
+
32
+ // Test replaying events after the second event
33
+ const replayedEvents: Array<{ eventId: string; message: JSONRPCMessage }> = [];
34
+ const sendMock = vi.fn(async (eventId: string, message: JSONRPCMessage) => {
35
+ replayedEvents.push({ eventId, message });
36
+ });
37
+
38
+ const returnedStreamId = await store.replayEventsAfter(
39
+ eventIds[1], // Replay after the second event
40
+ { send: sendMock }
41
+ );
42
+
43
+ // Verify the correct stream ID was returned
44
+ expect(returnedStreamId).toBe(streamId);
45
+
46
+ // Verify that events 3, 4, and 5 were replayed (after event 2)
47
+ expect(replayedEvents).toHaveLength(3);
48
+ expect(sendMock).toHaveBeenCalledTimes(3);
49
+
50
+ // Verify the replayed messages are correct and in order
51
+ expect(replayedEvents[0].message).toEqual(messages[2]);
52
+ expect(replayedEvents[1].message).toEqual(messages[3]);
53
+ expect(replayedEvents[2].message).toEqual(messages[4]);
54
+
55
+ // Verify event IDs are preserved
56
+ expect(replayedEvents[0].eventId).toBe(eventIds[2]);
57
+ expect(replayedEvents[1].eventId).toBe(eventIds[3]);
58
+ expect(replayedEvents[2].eventId).toBe(eventIds[4]);
59
+ });
60
+
61
+ it("isolates events by stream ID and only replays events from the same stream", async () => {
62
+ const store = new InMemoryEventStore();
63
+ const streamId1 = "stream-alpha";
64
+ const streamId2 = "stream-beta";
65
+
66
+ // Create messages for two different streams
67
+ const stream1Messages: JSONRPCMessage[] = [
68
+ { id: 1, jsonrpc: "2.0", method: "stream1.init" },
69
+ { id: 2, jsonrpc: "2.0", method: "stream1.process" },
70
+ { id: 3, jsonrpc: "2.0", method: "stream1.complete" },
71
+ ];
72
+
73
+ const stream2Messages: JSONRPCMessage[] = [
74
+ { id: 10, jsonrpc: "2.0", method: "stream2.init" },
75
+ { id: 20, jsonrpc: "2.0", method: "stream2.process" },
76
+ { id: 30, jsonrpc: "2.0", method: "stream2.complete" },
77
+ ];
78
+
79
+ // Interleave storing events from both streams with small delays
80
+ const stream1EventIds: string[] = [];
81
+ const stream2EventIds: string[] = [];
82
+
83
+ // Store first event from each stream
84
+ stream1EventIds.push(await store.storeEvent(streamId1, stream1Messages[0]));
85
+ await new Promise(resolve => setTimeout(resolve, 1));
86
+ stream2EventIds.push(await store.storeEvent(streamId2, stream2Messages[0]));
87
+ await new Promise(resolve => setTimeout(resolve, 1));
88
+
89
+ // Store second event from each stream
90
+ stream1EventIds.push(await store.storeEvent(streamId1, stream1Messages[1]));
91
+ await new Promise(resolve => setTimeout(resolve, 1));
92
+ stream2EventIds.push(await store.storeEvent(streamId2, stream2Messages[1]));
93
+ await new Promise(resolve => setTimeout(resolve, 1));
94
+
95
+ // Store third event from each stream
96
+ stream1EventIds.push(await store.storeEvent(streamId1, stream1Messages[2]));
97
+ await new Promise(resolve => setTimeout(resolve, 1));
98
+ stream2EventIds.push(await store.storeEvent(streamId2, stream2Messages[2]));
99
+
100
+ // Replay events from stream 1 after its first event
101
+ const stream1ReplayedEvents: Array<{ eventId: string; message: JSONRPCMessage }> = [];
102
+ const stream1SendMock = vi.fn(async (eventId: string, message: JSONRPCMessage) => {
103
+ stream1ReplayedEvents.push({ eventId, message });
104
+ });
105
+
106
+ const returnedStreamId1 = await store.replayEventsAfter(
107
+ stream1EventIds[0],
108
+ { send: stream1SendMock }
109
+ );
110
+
111
+ // Verify only stream 1 events were replayed
112
+ expect(returnedStreamId1).toBe(streamId1);
113
+ expect(stream1ReplayedEvents).toHaveLength(2);
114
+ expect(stream1ReplayedEvents[0].message).toEqual(stream1Messages[1]);
115
+ expect(stream1ReplayedEvents[1].message).toEqual(stream1Messages[2]);
116
+
117
+ // Verify no stream 2 events were included
118
+ for (const event of stream1ReplayedEvents) {
119
+ expect(event.eventId).toContain(streamId1);
120
+ expect(event.eventId).not.toContain(streamId2);
121
+ }
122
+
123
+ // Now replay events from stream 2 after its first event
124
+ const stream2ReplayedEvents: Array<{ eventId: string; message: JSONRPCMessage }> = [];
125
+ const stream2SendMock = vi.fn(async (eventId: string, message: JSONRPCMessage) => {
126
+ stream2ReplayedEvents.push({ eventId, message });
127
+ });
128
+
129
+ const returnedStreamId2 = await store.replayEventsAfter(
130
+ stream2EventIds[0],
131
+ { send: stream2SendMock }
132
+ );
133
+
134
+ // Verify only stream 2 events were replayed
135
+ expect(returnedStreamId2).toBe(streamId2);
136
+ expect(stream2ReplayedEvents).toHaveLength(2);
137
+ expect(stream2ReplayedEvents[0].message).toEqual(stream2Messages[1]);
138
+ expect(stream2ReplayedEvents[1].message).toEqual(stream2Messages[2]);
139
+
140
+ // Verify no stream 1 events were included
141
+ for (const event of stream2ReplayedEvents) {
142
+ expect(event.eventId).toContain(streamId2);
143
+ expect(event.eventId).not.toContain(streamId1);
144
+ }
145
+
146
+ // Test edge case: replay with non-existent event ID returns empty string
147
+ const invalidResult = await store.replayEventsAfter(
148
+ "non-existent-event-id",
149
+ { send: vi.fn() }
150
+ );
151
+ expect(invalidResult).toBe("");
152
+
153
+ // Test edge case: replay with empty event ID returns empty string
154
+ const emptyResult = await store.replayEventsAfter(
155
+ "",
156
+ { send: vi.fn() }
157
+ );
158
+ expect(emptyResult).toBe("");
159
+ });
160
+ });
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * This is a copy of the InMemoryEventStore from the typescript-sdk
3
- * https://github.com/modelcontextprotocol/typescript-sdk/blob/main/src/inMemoryEventStore.ts
3
+ * https://github.com/modelcontextprotocol/typescript-sdk/blob/main/src/examples/shared/inMemoryEventStore.ts
4
4
  */
5
5
 
6
6
  import type { EventStore } from "@modelcontextprotocol/sdk/server/streamableHttp.js";