mcp-proxy 2.13.0 → 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/bin/mcp-proxy.js +71 -71
- package/dist/bin/mcp-proxy.js.map +1 -1
- package/dist/{chunk-BLDWVJ5G.js → chunk-F2LFKNGG.js} +48 -44
- package/dist/chunk-F2LFKNGG.js.map +1 -0
- package/dist/index.d.ts +32 -32
- package/dist/index.js +7 -7
- package/dist/index.js.map +1 -1
- package/eslint.config.ts +14 -0
- package/package.json +5 -1
- package/src/InMemoryEventStore.ts +30 -30
- package/src/StdioClientTransport.ts +79 -78
- package/src/bin/mcp-proxy.ts +26 -23
- package/src/index.ts +1 -1
- package/src/proxyServer.ts +9 -9
- package/src/simple-stdio-server.ts +5 -5
- package/src/startHTTPStreamServer.test.ts +20 -17
- package/src/startHTTPStreamServer.ts +20 -19
- package/src/startSSEServer.test.ts +19 -15
- package/src/startSSEServer.ts +16 -8
- package/src/tapTransport.ts +11 -11
- package/dist/chunk-BLDWVJ5G.js.map +0 -1
- package/eslint.config.js +0 -10
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { JSONRPCMessage, ServerCapabilities } from '@modelcontextprotocol/sdk/types.js';
|
|
2
1
|
import { EventStore } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
3
|
-
import {
|
|
2
|
+
import { JSONRPCMessage, ServerCapabilities } from '@modelcontextprotocol/sdk/types.js';
|
|
4
3
|
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
4
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
5
5
|
import http from 'http';
|
|
6
6
|
import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
|
|
7
7
|
|
|
@@ -18,30 +18,30 @@ import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
|
|
|
18
18
|
declare class InMemoryEventStore implements EventStore {
|
|
19
19
|
private events;
|
|
20
20
|
/**
|
|
21
|
-
*
|
|
22
|
-
|
|
23
|
-
private generateEventId;
|
|
24
|
-
/**
|
|
25
|
-
* Extracts the stream ID from an event ID
|
|
21
|
+
* Replays events that occurred after a specific event ID
|
|
22
|
+
* Implements EventStore.replayEventsAfter
|
|
26
23
|
*/
|
|
27
|
-
|
|
24
|
+
replayEventsAfter(lastEventId: string, { send, }: {
|
|
25
|
+
send: (eventId: string, message: JSONRPCMessage) => Promise<void>;
|
|
26
|
+
}): Promise<string>;
|
|
28
27
|
/**
|
|
29
28
|
* Stores an event with a generated event ID
|
|
30
29
|
* Implements EventStore.storeEvent
|
|
31
30
|
*/
|
|
32
31
|
storeEvent(streamId: string, message: JSONRPCMessage): Promise<string>;
|
|
33
32
|
/**
|
|
34
|
-
*
|
|
35
|
-
* Implements EventStore.replayEventsAfter
|
|
33
|
+
* Generates a unique event ID for a given stream ID
|
|
36
34
|
*/
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
private generateEventId;
|
|
36
|
+
/**
|
|
37
|
+
* Extracts the stream ID from an event ID
|
|
38
|
+
*/
|
|
39
|
+
private getStreamIdFromEventId;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
declare const proxyServer: ({
|
|
43
|
-
server: Server;
|
|
42
|
+
declare const proxyServer: ({ client, server, serverCapabilities, }: {
|
|
44
43
|
client: Client;
|
|
44
|
+
server: Server;
|
|
45
45
|
serverCapabilities: ServerCapabilities;
|
|
46
46
|
}) => Promise<void>;
|
|
47
47
|
|
|
@@ -49,48 +49,48 @@ type SSEServer$1 = {
|
|
|
49
49
|
close: () => Promise<void>;
|
|
50
50
|
};
|
|
51
51
|
type ServerLike$1 = {
|
|
52
|
-
connect: Server["connect"];
|
|
53
52
|
close: Server["close"];
|
|
53
|
+
connect: Server["connect"];
|
|
54
54
|
};
|
|
55
|
-
declare const startHTTPStreamServer: <T extends ServerLike$1>({
|
|
56
|
-
port: number;
|
|
57
|
-
endpoint: string;
|
|
55
|
+
declare const startHTTPStreamServer: <T extends ServerLike$1>({ createServer, endpoint, eventStore, onClose, onConnect, onUnhandledRequest, port, }: {
|
|
58
56
|
createServer: (request: http.IncomingMessage) => Promise<T>;
|
|
57
|
+
endpoint: string;
|
|
59
58
|
eventStore?: EventStore;
|
|
60
|
-
onConnect?: (server: T) => void;
|
|
61
59
|
onClose?: (server: T) => void;
|
|
60
|
+
onConnect?: (server: T) => void;
|
|
62
61
|
onUnhandledRequest?: (req: http.IncomingMessage, res: http.ServerResponse) => Promise<void>;
|
|
62
|
+
port: number;
|
|
63
63
|
}) => Promise<SSEServer$1>;
|
|
64
64
|
|
|
65
65
|
type SSEServer = {
|
|
66
66
|
close: () => Promise<void>;
|
|
67
67
|
};
|
|
68
68
|
type ServerLike = {
|
|
69
|
-
connect: Server["connect"];
|
|
70
69
|
close: Server["close"];
|
|
70
|
+
connect: Server["connect"];
|
|
71
71
|
};
|
|
72
|
-
declare const startSSEServer: <T extends ServerLike>({
|
|
73
|
-
port: number;
|
|
74
|
-
endpoint: string;
|
|
72
|
+
declare const startSSEServer: <T extends ServerLike>({ createServer, endpoint, onClose, onConnect, onUnhandledRequest, port, }: {
|
|
75
73
|
createServer: (request: http.IncomingMessage) => Promise<T>;
|
|
76
|
-
|
|
74
|
+
endpoint: string;
|
|
77
75
|
onClose?: (server: T) => void;
|
|
76
|
+
onConnect?: (server: T) => void;
|
|
78
77
|
onUnhandledRequest?: (req: http.IncomingMessage, res: http.ServerResponse) => Promise<void>;
|
|
78
|
+
port: number;
|
|
79
79
|
}) => Promise<SSEServer>;
|
|
80
80
|
|
|
81
81
|
type TransportEvent = {
|
|
82
|
-
type: "close";
|
|
83
|
-
} | {
|
|
84
|
-
type: "onclose";
|
|
85
|
-
} | {
|
|
86
|
-
type: "onerror";
|
|
87
82
|
error: Error;
|
|
83
|
+
type: "onerror";
|
|
88
84
|
} | {
|
|
89
|
-
type: "onmessage";
|
|
90
85
|
message: JSONRPCMessage;
|
|
86
|
+
type: "onmessage";
|
|
91
87
|
} | {
|
|
92
|
-
type: "send";
|
|
93
88
|
message: JSONRPCMessage;
|
|
89
|
+
type: "send";
|
|
90
|
+
} | {
|
|
91
|
+
type: "close";
|
|
92
|
+
} | {
|
|
93
|
+
type: "onclose";
|
|
94
94
|
} | {
|
|
95
95
|
type: "start";
|
|
96
96
|
};
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
proxyServer,
|
|
4
4
|
startHTTPStreamServer,
|
|
5
5
|
startSSEServer
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-F2LFKNGG.js";
|
|
7
7
|
|
|
8
8
|
// src/tapTransport.ts
|
|
9
9
|
var tapTransport = (transport, eventHandler) => {
|
|
@@ -27,22 +27,22 @@ var tapTransport = (transport, eventHandler) => {
|
|
|
27
27
|
};
|
|
28
28
|
transport.onerror = async (error) => {
|
|
29
29
|
eventHandler({
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
error,
|
|
31
|
+
type: "onerror"
|
|
32
32
|
});
|
|
33
33
|
return originalOnError?.(error);
|
|
34
34
|
};
|
|
35
35
|
transport.onmessage = async (message) => {
|
|
36
36
|
eventHandler({
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
message,
|
|
38
|
+
type: "onmessage"
|
|
39
39
|
});
|
|
40
40
|
return originalOnMessage?.(message);
|
|
41
41
|
};
|
|
42
42
|
transport.send = async (message) => {
|
|
43
43
|
eventHandler({
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
message,
|
|
45
|
+
type: "send"
|
|
46
46
|
});
|
|
47
47
|
return originalSend?.(message);
|
|
48
48
|
};
|
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: \"
|
|
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":[]}
|
package/eslint.config.ts
ADDED
|
@@ -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.13.
|
|
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": {
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* https://github.com/modelcontextprotocol/typescript-sdk/blob/main/src/inMemoryEventStore.ts
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import type { JSONRPCMessage } from "@modelcontextprotocol/sdk/types.js";
|
|
7
6
|
import type { EventStore } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
7
|
+
import type { JSONRPCMessage } from "@modelcontextprotocol/sdk/types.js";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Simple in-memory implementation of the EventStore interface for resumability
|
|
@@ -12,34 +12,9 @@ import type { EventStore } from "@modelcontextprotocol/sdk/server/streamableHttp
|
|
|
12
12
|
* where a persistent storage solution would be more appropriate.
|
|
13
13
|
*/
|
|
14
14
|
export class InMemoryEventStore implements EventStore {
|
|
15
|
-
private events: Map<string, {
|
|
15
|
+
private events: Map<string, { message: JSONRPCMessage; streamId: string }> =
|
|
16
16
|
new Map();
|
|
17
17
|
|
|
18
|
-
/**
|
|
19
|
-
* Generates a unique event ID for a given stream ID
|
|
20
|
-
*/
|
|
21
|
-
private generateEventId(streamId: string): string {
|
|
22
|
-
return `${streamId}_${Date.now()}_${Math.random().toString(36).substring(2, 10)}`;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Extracts the stream ID from an event ID
|
|
27
|
-
*/
|
|
28
|
-
private getStreamIdFromEventId(eventId: string): string {
|
|
29
|
-
const parts = eventId.split("_");
|
|
30
|
-
return parts.length > 0 ? parts[0] : "";
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Stores an event with a generated event ID
|
|
35
|
-
* Implements EventStore.storeEvent
|
|
36
|
-
*/
|
|
37
|
-
async storeEvent(streamId: string, message: JSONRPCMessage): Promise<string> {
|
|
38
|
-
const eventId = this.generateEventId(streamId);
|
|
39
|
-
this.events.set(eventId, { streamId, message });
|
|
40
|
-
return eventId;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
18
|
/**
|
|
44
19
|
* Replays events that occurred after a specific event ID
|
|
45
20
|
* Implements EventStore.replayEventsAfter
|
|
@@ -48,7 +23,7 @@ export class InMemoryEventStore implements EventStore {
|
|
|
48
23
|
lastEventId: string,
|
|
49
24
|
{
|
|
50
25
|
send,
|
|
51
|
-
}: { send: (eventId: string, message: JSONRPCMessage) => Promise<void> }
|
|
26
|
+
}: { send: (eventId: string, message: JSONRPCMessage) => Promise<void> },
|
|
52
27
|
): Promise<string> {
|
|
53
28
|
if (!lastEventId || !this.events.has(lastEventId)) {
|
|
54
29
|
return "";
|
|
@@ -64,12 +39,12 @@ export class InMemoryEventStore implements EventStore {
|
|
|
64
39
|
|
|
65
40
|
// Sort events by eventId for chronological ordering
|
|
66
41
|
const sortedEvents = [...this.events.entries()].sort((a, b) =>
|
|
67
|
-
a[0].localeCompare(b[0])
|
|
42
|
+
a[0].localeCompare(b[0]),
|
|
68
43
|
);
|
|
69
44
|
|
|
70
45
|
for (const [
|
|
71
46
|
eventId,
|
|
72
|
-
{ streamId: eventStreamId
|
|
47
|
+
{ message, streamId: eventStreamId },
|
|
73
48
|
] of sortedEvents) {
|
|
74
49
|
// Only include events from the same stream
|
|
75
50
|
if (eventStreamId !== streamId) {
|
|
@@ -88,4 +63,29 @@ export class InMemoryEventStore implements EventStore {
|
|
|
88
63
|
}
|
|
89
64
|
return streamId;
|
|
90
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
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
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
42
|
+
onEvent?: (event: TransportEvent) => void;
|
|
55
43
|
|
|
56
44
|
/**
|
|
57
|
-
*
|
|
45
|
+
* How to handle stderr of the child process. This matches the semantics of Node's `child_process.spawn`.
|
|
58
46
|
*
|
|
59
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|