@fractaal/pi-mcp 0.86.9 → 1.6.0

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.
Files changed (91) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/README.md +60 -5
  3. package/dist/auth-provider.d.ts +4 -0
  4. package/dist/auth-provider.d.ts.map +1 -1
  5. package/dist/auth-provider.js.map +1 -1
  6. package/dist/client.d.ts.map +1 -1
  7. package/dist/client.js +9 -5
  8. package/dist/client.js.map +1 -1
  9. package/dist/config.d.ts +117 -0
  10. package/dist/config.d.ts.map +1 -0
  11. package/dist/config.js +153 -0
  12. package/dist/config.js.map +1 -0
  13. package/dist/index.d.ts +6 -1
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +5 -0
  16. package/dist/index.js.map +1 -1
  17. package/dist/input-schema.d.ts +7 -0
  18. package/dist/input-schema.d.ts.map +1 -0
  19. package/dist/input-schema.js +13 -0
  20. package/dist/input-schema.js.map +1 -0
  21. package/dist/naming.d.ts +18 -0
  22. package/dist/naming.d.ts.map +1 -0
  23. package/dist/naming.js +29 -0
  24. package/dist/naming.js.map +1 -0
  25. package/dist/oauth/auth-provider.d.ts +52 -0
  26. package/dist/oauth/auth-provider.d.ts.map +1 -0
  27. package/dist/oauth/auth-provider.js +97 -0
  28. package/dist/oauth/auth-provider.js.map +1 -0
  29. package/dist/oauth/callback.d.ts.map +1 -1
  30. package/dist/oauth/callback.js +5 -1
  31. package/dist/oauth/callback.js.map +1 -1
  32. package/dist/oauth/client-settings.d.ts +107 -0
  33. package/dist/oauth/client-settings.d.ts.map +1 -0
  34. package/dist/oauth/client-settings.js +114 -0
  35. package/dist/oauth/client-settings.js.map +1 -0
  36. package/dist/oauth/discovery.d.ts +3 -0
  37. package/dist/oauth/discovery.d.ts.map +1 -1
  38. package/dist/oauth/discovery.js +9 -6
  39. package/dist/oauth/discovery.js.map +1 -1
  40. package/dist/oauth/errors.d.ts +10 -2
  41. package/dist/oauth/errors.d.ts.map +1 -1
  42. package/dist/oauth/errors.js +13 -1
  43. package/dist/oauth/errors.js.map +1 -1
  44. package/dist/oauth/flow.d.ts +14 -0
  45. package/dist/oauth/flow.d.ts.map +1 -1
  46. package/dist/oauth/flow.js +52 -15
  47. package/dist/oauth/flow.js.map +1 -1
  48. package/dist/oauth/index.d.ts +5 -1
  49. package/dist/oauth/index.d.ts.map +1 -1
  50. package/dist/oauth/index.js +4 -1
  51. package/dist/oauth/index.js.map +1 -1
  52. package/dist/oauth/loopback.d.ts +51 -0
  53. package/dist/oauth/loopback.d.ts.map +1 -0
  54. package/dist/oauth/loopback.js +98 -0
  55. package/dist/oauth/loopback.js.map +1 -0
  56. package/dist/oauth/sign-in.d.ts +73 -0
  57. package/dist/oauth/sign-in.d.ts.map +1 -0
  58. package/dist/oauth/sign-in.js +182 -0
  59. package/dist/oauth/sign-in.js.map +1 -0
  60. package/dist/oauth/types.d.ts +1 -0
  61. package/dist/oauth/types.d.ts.map +1 -1
  62. package/dist/oauth/types.js +9 -5
  63. package/dist/oauth/types.js.map +1 -1
  64. package/dist/output-limit.d.ts +24 -0
  65. package/dist/output-limit.d.ts.map +1 -0
  66. package/dist/output-limit.js +58 -0
  67. package/dist/output-limit.js.map +1 -0
  68. package/dist/protocol/content.d.ts +2 -1
  69. package/dist/protocol/content.d.ts.map +1 -1
  70. package/dist/protocol/content.js.map +1 -1
  71. package/dist/transports/lines.d.ts +34 -0
  72. package/dist/transports/lines.d.ts.map +1 -0
  73. package/dist/transports/lines.js +85 -0
  74. package/dist/transports/lines.js.map +1 -0
  75. package/dist/transports/pipe.d.ts +49 -0
  76. package/dist/transports/pipe.d.ts.map +1 -0
  77. package/dist/transports/pipe.js +96 -0
  78. package/dist/transports/pipe.js.map +1 -0
  79. package/dist/transports/stdio.d.ts +1 -4
  80. package/dist/transports/stdio.d.ts.map +1 -1
  81. package/dist/transports/stdio.js +20 -51
  82. package/dist/transports/stdio.js.map +1 -1
  83. package/dist/transports/streamable-http.d.ts +9 -2
  84. package/dist/transports/streamable-http.d.ts.map +1 -1
  85. package/dist/transports/streamable-http.js +105 -30
  86. package/dist/transports/streamable-http.js.map +1 -1
  87. package/dist/transports/transport.d.ts +9 -1
  88. package/dist/transports/transport.d.ts.map +1 -1
  89. package/dist/transports/transport.js.map +1 -1
  90. package/package.json +4 -5
  91. /package/{LICENSES → THIRD_PARTY_NOTICES}/modelcontextprotocol-typescript-sdk.txt +0 -0
@@ -0,0 +1,85 @@
1
+ import { parseJsonRpcMessage } from "../protocol/jsonrpc.js";
2
+ import { DEFAULT_MAX_MESSAGE_BYTES } from "./transport.js";
3
+ const DEFAULT_MAX_STDERR_BYTES = 64 * 1024;
4
+ /**
5
+ * Reads a program's output as MCP's stdio framing: newline-delimited JSON-RPC messages on stdout, and a bounded
6
+ * tail of stderr for diagnostics.
7
+ */
8
+ export class JsonRpcLineReader {
9
+ options;
10
+ /** The bytes of the incomplete message received so far. */
11
+ stdoutChunks = [];
12
+ stdoutPendingBytes = 0;
13
+ stderrBuffer = Buffer.alloc(0);
14
+ constructor(options) {
15
+ this.options = options;
16
+ }
17
+ /** The last `maxStderrBytes` of stderr. */
18
+ get stderr() {
19
+ return this.stderrBuffer.toString("utf8");
20
+ }
21
+ /**
22
+ * Takes the next stdout bytes and reports every complete message, or the error parsing it. Bytes of an
23
+ * incomplete message are kept as chunks and joined once when its newline arrives, so a large message that
24
+ * arrives in many chunks is copied and scanned only once.
25
+ */
26
+ readStdout(chunk) {
27
+ const data = Buffer.from(chunk.buffer, chunk.byteOffset, chunk.byteLength);
28
+ const maxMessageBytes = this.options.maxMessageBytes ?? DEFAULT_MAX_MESSAGE_BYTES;
29
+ let start = 0;
30
+ while (true) {
31
+ const newline = data.indexOf(0x0a, start);
32
+ if (newline < 0) {
33
+ if (start < data.length) {
34
+ // Copied, since the caller may reuse the chunk's memory.
35
+ this.stdoutChunks.push(Buffer.from(data.subarray(start)));
36
+ this.stdoutPendingBytes += data.length - start;
37
+ }
38
+ if (this.stdoutPendingBytes > maxMessageBytes) {
39
+ this.stdoutChunks = [];
40
+ this.stdoutPendingBytes = 0;
41
+ this.options.onError(new Error(`MCP stdio message exceeds ${maxMessageBytes} bytes`));
42
+ }
43
+ return;
44
+ }
45
+ let line = data.subarray(start, newline);
46
+ if (this.stdoutChunks.length > 0) {
47
+ this.stdoutChunks.push(line);
48
+ line = Buffer.concat(this.stdoutChunks, this.stdoutPendingBytes + line.length);
49
+ this.stdoutChunks = [];
50
+ this.stdoutPendingBytes = 0;
51
+ }
52
+ start = newline + 1;
53
+ if (line.length > maxMessageBytes) {
54
+ this.options.onError(new Error(`MCP stdio message exceeds ${maxMessageBytes} bytes`));
55
+ continue;
56
+ }
57
+ const text = line.toString("utf8").replace(/\r$/, "");
58
+ if (!text.trim())
59
+ continue;
60
+ try {
61
+ this.options.onMessage(parseJsonRpcMessage(JSON.parse(text)));
62
+ }
63
+ catch (error) {
64
+ this.options.onError(error);
65
+ }
66
+ }
67
+ }
68
+ /** Takes the next stderr bytes into the tail and passes them to `onStderr`. */
69
+ readStderr(chunk) {
70
+ const maxStderrBytes = this.options.maxStderrBytes ?? DEFAULT_MAX_STDERR_BYTES;
71
+ this.stderrBuffer = Buffer.concat([this.stderrBuffer, chunk]);
72
+ if (this.stderrBuffer.length > maxStderrBytes)
73
+ this.stderrBuffer = this.stderrBuffer.subarray(-maxStderrBytes);
74
+ this.options.onStderr?.(Buffer.from(chunk.buffer, chunk.byteOffset, chunk.byteLength).toString("utf8"));
75
+ }
76
+ /** Ends stdout: a message without its newline is reported as an error and dropped. */
77
+ end() {
78
+ if (Buffer.concat(this.stdoutChunks).toString("utf8").trim()) {
79
+ this.options.onError(new Error("MCP stdio server closed with an incomplete JSON-RPC message"));
80
+ }
81
+ this.stdoutChunks = [];
82
+ this.stdoutPendingBytes = 0;
83
+ }
84
+ }
85
+ //# sourceMappingURL=lines.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lines.js","sourceRoot":"","sources":["../../src/transports/lines.ts"],"names":[],"mappings":"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","sourcesContent":["import { type JsonRpcMessage, parseJsonRpcMessage } from \"../protocol/jsonrpc.ts\";\nimport { DEFAULT_MAX_MESSAGE_BYTES } from \"./transport.ts\";\n\nconst DEFAULT_MAX_STDERR_BYTES = 64 * 1024;\n\nexport interface JsonRpcLineReaderOptions {\n\tmaxMessageBytes?: number;\n\tmaxStderrBytes?: number;\n\t/** Every stderr chunk, decoded as UTF-8. */\n\tonStderr?: (chunk: string) => void;\n\tonMessage(message: JsonRpcMessage): void;\n\tonError(error: unknown): void;\n}\n\n/**\n * Reads a program's output as MCP's stdio framing: newline-delimited JSON-RPC messages on stdout, and a bounded\n * tail of stderr for diagnostics.\n */\nexport class JsonRpcLineReader {\n\tprivate readonly options: JsonRpcLineReaderOptions;\n\t/** The bytes of the incomplete message received so far. */\n\tprivate stdoutChunks: Buffer[] = [];\n\tprivate stdoutPendingBytes = 0;\n\tprivate stderrBuffer = Buffer.alloc(0);\n\n\tconstructor(options: JsonRpcLineReaderOptions) {\n\t\tthis.options = options;\n\t}\n\n\t/** The last `maxStderrBytes` of stderr. */\n\tget stderr(): string {\n\t\treturn this.stderrBuffer.toString(\"utf8\");\n\t}\n\n\t/**\n\t * Takes the next stdout bytes and reports every complete message, or the error parsing it. Bytes of an\n\t * incomplete message are kept as chunks and joined once when its newline arrives, so a large message that\n\t * arrives in many chunks is copied and scanned only once.\n\t */\n\treadStdout(chunk: Uint8Array): void {\n\t\tconst data = Buffer.from(chunk.buffer, chunk.byteOffset, chunk.byteLength);\n\t\tconst maxMessageBytes = this.options.maxMessageBytes ?? DEFAULT_MAX_MESSAGE_BYTES;\n\t\tlet start = 0;\n\t\twhile (true) {\n\t\t\tconst newline = data.indexOf(0x0a, start);\n\t\t\tif (newline < 0) {\n\t\t\t\tif (start < data.length) {\n\t\t\t\t\t// Copied, since the caller may reuse the chunk's memory.\n\t\t\t\t\tthis.stdoutChunks.push(Buffer.from(data.subarray(start)));\n\t\t\t\t\tthis.stdoutPendingBytes += data.length - start;\n\t\t\t\t}\n\t\t\t\tif (this.stdoutPendingBytes > maxMessageBytes) {\n\t\t\t\t\tthis.stdoutChunks = [];\n\t\t\t\t\tthis.stdoutPendingBytes = 0;\n\t\t\t\t\tthis.options.onError(new Error(`MCP stdio message exceeds ${maxMessageBytes} bytes`));\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tlet line = data.subarray(start, newline);\n\t\t\tif (this.stdoutChunks.length > 0) {\n\t\t\t\tthis.stdoutChunks.push(line);\n\t\t\t\tline = Buffer.concat(this.stdoutChunks, this.stdoutPendingBytes + line.length);\n\t\t\t\tthis.stdoutChunks = [];\n\t\t\t\tthis.stdoutPendingBytes = 0;\n\t\t\t}\n\t\t\tstart = newline + 1;\n\t\t\tif (line.length > maxMessageBytes) {\n\t\t\t\tthis.options.onError(new Error(`MCP stdio message exceeds ${maxMessageBytes} bytes`));\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst text = line.toString(\"utf8\").replace(/\\r$/, \"\");\n\t\t\tif (!text.trim()) continue;\n\t\t\ttry {\n\t\t\t\tthis.options.onMessage(parseJsonRpcMessage(JSON.parse(text)));\n\t\t\t} catch (error) {\n\t\t\t\tthis.options.onError(error);\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Takes the next stderr bytes into the tail and passes them to `onStderr`. */\n\treadStderr(chunk: Uint8Array): void {\n\t\tconst maxStderrBytes = this.options.maxStderrBytes ?? DEFAULT_MAX_STDERR_BYTES;\n\t\tthis.stderrBuffer = Buffer.concat([this.stderrBuffer, chunk]);\n\t\tif (this.stderrBuffer.length > maxStderrBytes) this.stderrBuffer = this.stderrBuffer.subarray(-maxStderrBytes);\n\t\tthis.options.onStderr?.(Buffer.from(chunk.buffer, chunk.byteOffset, chunk.byteLength).toString(\"utf8\"));\n\t}\n\n\t/** Ends stdout: a message without its newline is reported as an error and dropped. */\n\tend(): void {\n\t\tif (Buffer.concat(this.stdoutChunks).toString(\"utf8\").trim()) {\n\t\t\tthis.options.onError(new Error(\"MCP stdio server closed with an incomplete JSON-RPC message\"));\n\t\t}\n\t\tthis.stdoutChunks = [];\n\t\tthis.stdoutPendingBytes = 0;\n\t}\n}\n"]}
@@ -0,0 +1,49 @@
1
+ import { type JsonRpcMessage } from "../protocol/jsonrpc.ts";
2
+ import { type McpTransport, TransportEvents } from "./transport.ts";
3
+ /** A running program's stdio, as `@earendil-works/pi-env`'s `RemoteProcess` provides it. */
4
+ export interface McpPipe {
5
+ write(data: Uint8Array | string): void;
6
+ closeStdin(): void;
7
+ /** Kill the program (and what it started). The pipe owns cleaning up what the program started. */
8
+ close(): void;
9
+ /** Settles when the program has ended, whatever the reason; it must also settle after `close()`. */
10
+ readonly exited: Promise<unknown>;
11
+ }
12
+ /** Handlers `PipeTransportOptions.open` connects to the program's output. */
13
+ export interface McpPipeHandlers {
14
+ onStdout(bytes: Uint8Array): void;
15
+ onStderr(bytes: Uint8Array): void;
16
+ }
17
+ export interface PipeTransportOptions {
18
+ /** Start the program; `onStdout`/`onStderr` receive its output from then on. */
19
+ open(handlers: McpPipeHandlers): Promise<McpPipe>;
20
+ maxMessageBytes?: number;
21
+ maxStderrBytes?: number;
22
+ onStderr?: (chunk: string) => void;
23
+ /** After closing stdin, how long the program may take to exit before `close()` kills it. Default 2000. */
24
+ closeTimeoutMs?: number;
25
+ }
26
+ /**
27
+ * MCP's stdio transport over a program that something else runs, for example on another machine through pi-env.
28
+ * The transport closes when the program ends; a rejected `exited` that `close()` did not cause is reported as an
29
+ * error first.
30
+ */
31
+ export declare class PipeTransport extends TransportEvents implements McpTransport {
32
+ readonly options: Readonly<PipeTransportOptions>;
33
+ private readonly lines;
34
+ private pipe;
35
+ private started;
36
+ private closed;
37
+ private ended;
38
+ constructor(options: PipeTransportOptions);
39
+ /** The last `maxStderrBytes` (default 64 KiB) of the program's stderr. */
40
+ get stderr(): string;
41
+ start(): Promise<void>;
42
+ send(message: JsonRpcMessage): Promise<void>;
43
+ close(): Promise<void>;
44
+ /** The program ended: report an unfinished message, then close. */
45
+ private exit;
46
+ /** `exited` rejected: an error unless `close()` caused it, then the same as an exit. */
47
+ private fail;
48
+ }
49
+ //# sourceMappingURL=pipe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipe.d.ts","sourceRoot":"","sources":["../../src/transports/pipe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAA4B,MAAM,wBAAwB,CAAC;AAEvF,OAAO,EAAE,KAAK,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAIpE,4FAA4F;AAC5F,MAAM,WAAW,OAAO;IACvB,KAAK,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAAC;IACvC,UAAU,IAAI,IAAI,CAAC;IACnB,kGAAkG;IAClG,KAAK,IAAI,IAAI,CAAC;IACd,oGAAoG;IACpG,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;CAClC;AAED,6EAA6E;AAC7E,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;CAClC;AAED,MAAM,WAAW,oBAAoB;IACpC,gFAAgF;IAChF,IAAI,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAClD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,0GAA0G;IAC1G,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;GAIG;AACH,qBAAa,aAAc,SAAQ,eAAgB,YAAW,YAAY;IACzE,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,oBAAoB,CAAC,CAAC;IACjD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAoB;IAC1C,OAAO,CAAC,IAAI,CAAsB;IAClC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,KAAK,CAAS;IAEtB,YAAY,OAAO,EAAE,oBAAoB,EAUxC;IAED,0EAA0E;IAC1E,IAAI,MAAM,IAAI,MAAM,CAEnB;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAoB3B;IAEK,IAAI,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAGjD;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAyB3B;IAED,mEAAmE;IACnE,OAAO,CAAC,IAAI;IAMZ,wFAAwF;IACxF,OAAO,CAAC,IAAI;CAIZ"}
@@ -0,0 +1,96 @@
1
+ import { McpConnectionClosedError } from "../protocol/jsonrpc.js";
2
+ import { JsonRpcLineReader } from "./lines.js";
3
+ import { TransportEvents } from "./transport.js";
4
+ const DEFAULT_CLOSE_TIMEOUT_MS = 2_000;
5
+ /**
6
+ * MCP's stdio transport over a program that something else runs, for example on another machine through pi-env.
7
+ * The transport closes when the program ends; a rejected `exited` that `close()` did not cause is reported as an
8
+ * error first.
9
+ */
10
+ export class PipeTransport extends TransportEvents {
11
+ options;
12
+ lines;
13
+ pipe;
14
+ started = false;
15
+ closed = false;
16
+ ended = false;
17
+ constructor(options) {
18
+ super();
19
+ this.options = Object.freeze({ ...options });
20
+ this.lines = new JsonRpcLineReader({
21
+ maxMessageBytes: this.options.maxMessageBytes,
22
+ maxStderrBytes: this.options.maxStderrBytes,
23
+ onStderr: this.options.onStderr,
24
+ onMessage: (message) => this.emitMessage(message),
25
+ onError: (error) => this.emitError(error),
26
+ });
27
+ }
28
+ /** The last `maxStderrBytes` (default 64 KiB) of the program's stderr. */
29
+ get stderr() {
30
+ return this.lines.stderr;
31
+ }
32
+ async start() {
33
+ if (this.started)
34
+ throw new Error("MCP pipe transport already started");
35
+ if (this.closed)
36
+ throw new McpConnectionClosedError();
37
+ this.started = true;
38
+ const pipe = await this.options.open({
39
+ onStdout: (bytes) => this.lines.readStdout(bytes),
40
+ onStderr: (bytes) => this.lines.readStderr(bytes),
41
+ });
42
+ if (this.closed) {
43
+ // close() ran while the program was starting and found nothing to shut down. Nobody else awaits
44
+ // `exited`, and a pipe may reject it after a kill.
45
+ pipe.exited.catch(() => { });
46
+ pipe.close();
47
+ throw new McpConnectionClosedError();
48
+ }
49
+ this.pipe = pipe;
50
+ pipe.exited.then(() => this.exit(), (error) => this.fail(error));
51
+ }
52
+ async send(message) {
53
+ if (!this.pipe || this.closed || this.ended)
54
+ throw new McpConnectionClosedError();
55
+ this.pipe.write(`${JSON.stringify(message)}\n`);
56
+ }
57
+ async close() {
58
+ if (this.closed)
59
+ return;
60
+ this.closed = true;
61
+ const pipe = this.pipe;
62
+ if (!pipe) {
63
+ this.emitClose();
64
+ return;
65
+ }
66
+ if (this.ended)
67
+ return;
68
+ // Shutdown per the spec: close stdin and let the program exit, then kill it.
69
+ pipe.closeStdin();
70
+ let timer;
71
+ const exitedInTime = await Promise.race([
72
+ pipe.exited.then(() => true, () => true),
73
+ new Promise((resolve) => {
74
+ timer = setTimeout(() => resolve(false), this.options.closeTimeoutMs ?? DEFAULT_CLOSE_TIMEOUT_MS);
75
+ }),
76
+ ]);
77
+ clearTimeout(timer);
78
+ if (exitedInTime)
79
+ return;
80
+ pipe.close();
81
+ await pipe.exited.catch(() => { });
82
+ }
83
+ /** The program ended: report an unfinished message, then close. */
84
+ exit() {
85
+ this.ended = true;
86
+ this.lines.end();
87
+ this.emitClose();
88
+ }
89
+ /** `exited` rejected: an error unless `close()` caused it, then the same as an exit. */
90
+ fail(error) {
91
+ if (!this.closed)
92
+ this.emitError(error);
93
+ this.exit();
94
+ }
95
+ }
96
+ //# sourceMappingURL=pipe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipe.js","sourceRoot":"","sources":["../../src/transports/pipe.ts"],"names":[],"mappings":"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","sourcesContent":["import { type JsonRpcMessage, McpConnectionClosedError } from \"../protocol/jsonrpc.ts\";\nimport { JsonRpcLineReader } from \"./lines.ts\";\nimport { type McpTransport, TransportEvents } from \"./transport.ts\";\n\nconst DEFAULT_CLOSE_TIMEOUT_MS = 2_000;\n\n/** A running program's stdio, as `@earendil-works/pi-env`'s `RemoteProcess` provides it. */\nexport interface McpPipe {\n\twrite(data: Uint8Array | string): void;\n\tcloseStdin(): void;\n\t/** Kill the program (and what it started). The pipe owns cleaning up what the program started. */\n\tclose(): void;\n\t/** Settles when the program has ended, whatever the reason; it must also settle after `close()`. */\n\treadonly exited: Promise<unknown>;\n}\n\n/** Handlers `PipeTransportOptions.open` connects to the program's output. */\nexport interface McpPipeHandlers {\n\tonStdout(bytes: Uint8Array): void;\n\tonStderr(bytes: Uint8Array): void;\n}\n\nexport interface PipeTransportOptions {\n\t/** Start the program; `onStdout`/`onStderr` receive its output from then on. */\n\topen(handlers: McpPipeHandlers): Promise<McpPipe>;\n\tmaxMessageBytes?: number;\n\tmaxStderrBytes?: number;\n\tonStderr?: (chunk: string) => void;\n\t/** After closing stdin, how long the program may take to exit before `close()` kills it. Default 2000. */\n\tcloseTimeoutMs?: number;\n}\n\n/**\n * MCP's stdio transport over a program that something else runs, for example on another machine through pi-env.\n * The transport closes when the program ends; a rejected `exited` that `close()` did not cause is reported as an\n * error first.\n */\nexport class PipeTransport extends TransportEvents implements McpTransport {\n\treadonly options: Readonly<PipeTransportOptions>;\n\tprivate readonly lines: JsonRpcLineReader;\n\tprivate pipe: McpPipe | undefined;\n\tprivate started = false;\n\tprivate closed = false;\n\tprivate ended = false;\n\n\tconstructor(options: PipeTransportOptions) {\n\t\tsuper();\n\t\tthis.options = Object.freeze({ ...options });\n\t\tthis.lines = new JsonRpcLineReader({\n\t\t\tmaxMessageBytes: this.options.maxMessageBytes,\n\t\t\tmaxStderrBytes: this.options.maxStderrBytes,\n\t\t\tonStderr: this.options.onStderr,\n\t\t\tonMessage: (message) => this.emitMessage(message),\n\t\t\tonError: (error) => this.emitError(error),\n\t\t});\n\t}\n\n\t/** The last `maxStderrBytes` (default 64 KiB) of the program's stderr. */\n\tget stderr(): string {\n\t\treturn this.lines.stderr;\n\t}\n\n\tasync start(): Promise<void> {\n\t\tif (this.started) throw new Error(\"MCP pipe transport already started\");\n\t\tif (this.closed) throw new McpConnectionClosedError();\n\t\tthis.started = true;\n\t\tconst pipe = await this.options.open({\n\t\t\tonStdout: (bytes) => this.lines.readStdout(bytes),\n\t\t\tonStderr: (bytes) => this.lines.readStderr(bytes),\n\t\t});\n\t\tif (this.closed) {\n\t\t\t// close() ran while the program was starting and found nothing to shut down. Nobody else awaits\n\t\t\t// `exited`, and a pipe may reject it after a kill.\n\t\t\tpipe.exited.catch(() => {});\n\t\t\tpipe.close();\n\t\t\tthrow new McpConnectionClosedError();\n\t\t}\n\t\tthis.pipe = pipe;\n\t\tpipe.exited.then(\n\t\t\t() => this.exit(),\n\t\t\t(error: unknown) => this.fail(error),\n\t\t);\n\t}\n\n\tasync send(message: JsonRpcMessage): Promise<void> {\n\t\tif (!this.pipe || this.closed || this.ended) throw new McpConnectionClosedError();\n\t\tthis.pipe.write(`${JSON.stringify(message)}\\n`);\n\t}\n\n\tasync close(): Promise<void> {\n\t\tif (this.closed) return;\n\t\tthis.closed = true;\n\t\tconst pipe = this.pipe;\n\t\tif (!pipe) {\n\t\t\tthis.emitClose();\n\t\t\treturn;\n\t\t}\n\t\tif (this.ended) return;\n\t\t// Shutdown per the spec: close stdin and let the program exit, then kill it.\n\t\tpipe.closeStdin();\n\t\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\t\tconst exitedInTime = await Promise.race([\n\t\t\tpipe.exited.then(\n\t\t\t\t() => true,\n\t\t\t\t() => true,\n\t\t\t),\n\t\t\tnew Promise<false>((resolve) => {\n\t\t\t\ttimer = setTimeout(() => resolve(false), this.options.closeTimeoutMs ?? DEFAULT_CLOSE_TIMEOUT_MS);\n\t\t\t}),\n\t\t]);\n\t\tclearTimeout(timer);\n\t\tif (exitedInTime) return;\n\t\tpipe.close();\n\t\tawait pipe.exited.catch(() => {});\n\t}\n\n\t/** The program ended: report an unfinished message, then close. */\n\tprivate exit(): void {\n\t\tthis.ended = true;\n\t\tthis.lines.end();\n\t\tthis.emitClose();\n\t}\n\n\t/** `exited` rejected: an error unless `close()` caused it, then the same as an exit. */\n\tprivate fail(error: unknown): void {\n\t\tif (!this.closed) this.emitError(error);\n\t\tthis.exit();\n\t}\n}\n"]}
@@ -16,8 +16,7 @@ export interface StdioTransportOptions {
16
16
  export declare class StdioTransport extends TransportEvents implements McpTransport {
17
17
  readonly options: Readonly<StdioTransportOptions>;
18
18
  private child;
19
- private stdoutBuffer;
20
- private stderrBuffer;
19
+ private readonly lines;
21
20
  private started;
22
21
  private closed;
23
22
  constructor(options: StdioTransportOptions);
@@ -26,7 +25,5 @@ export declare class StdioTransport extends TransportEvents implements McpTransp
26
25
  start(): Promise<void>;
27
26
  send(message: JsonRpcMessage): Promise<void>;
28
27
  close(): Promise<void>;
29
- private handleStdout;
30
- private handleStderr;
31
28
  }
32
29
  //# sourceMappingURL=stdio.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../../src/transports/stdio.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,cAAc,EAAiD,MAAM,wBAAwB,CAAC;AAC5G,OAAO,EAA6B,KAAK,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAkD/F,MAAM,WAAW,qBAAqB;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,+FAA+F;IAC/F,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,qBAAa,cAAe,SAAQ,eAAgB,YAAW,YAAY;IAC1E,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,qBAAqB,CAAC,CAAC;IAClD,OAAO,CAAC,KAAK,CAA2B;IACxC,OAAO,CAAC,YAAY,CAAmB;IACvC,OAAO,CAAC,YAAY,CAAmB;IACvC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAS;IAEvB,YAAY,OAAO,EAAE,qBAAqB,EAGzC;IAED,IAAI,GAAG,IAAI,MAAM,GAAG,SAAS,CAE5B;IAED,IAAI,MAAM,IAAI,MAAM,CAEnB;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAmD3B;IAEK,IAAI,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAOjD;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CA2B3B;IAED,OAAO,CAAC,YAAY;IA4BpB,OAAO,CAAC,YAAY;CAOpB"}
1
+ {"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../../src/transports/stdio.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,cAAc,EAA4B,MAAM,wBAAwB,CAAC;AAEvF,OAAO,EAAE,KAAK,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAiDpE,MAAM,WAAW,qBAAqB;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,+FAA+F;IAC/F,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,qBAAa,cAAe,SAAQ,eAAgB,YAAW,YAAY;IAC1E,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,qBAAqB,CAAC,CAAC;IAClD,OAAO,CAAC,KAAK,CAA2B;IACxC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAoB;IAC1C,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAS;IAEvB,YAAY,OAAO,EAAE,qBAAqB,EAUzC;IAED,IAAI,GAAG,IAAI,MAAM,GAAG,SAAS,CAE5B;IAED,IAAI,MAAM,IAAI,MAAM,CAEnB;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAiD3B;IAEK,IAAI,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAOjD;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CA6B3B;CACD"}
@@ -1,9 +1,9 @@
1
1
  import { spawn } from "node:child_process";
2
2
  import process from "node:process";
3
3
  import crossSpawn from "cross-spawn";
4
- import { McpConnectionClosedError, parseJsonRpcMessage } from "../protocol/jsonrpc.js";
5
- import { DEFAULT_MAX_MESSAGE_BYTES, TransportEvents } from "./transport.js";
6
- const DEFAULT_MAX_STDERR_BYTES = 64 * 1024;
4
+ import { McpConnectionClosedError } from "../protocol/jsonrpc.js";
5
+ import { JsonRpcLineReader } from "./lines.js";
6
+ import { TransportEvents } from "./transport.js";
7
7
  const DEFAULT_CLOSE_TIMEOUT_MS = 2_000;
8
8
  /** How long a server gets to exit on its own after stdin closes, before it is sent SIGTERM. */
9
9
  const STDIN_CLOSE_GRACE_MS = 500;
@@ -52,19 +52,25 @@ function installExitHook() {
52
52
  export class StdioTransport extends TransportEvents {
53
53
  options;
54
54
  child;
55
- stdoutBuffer = Buffer.alloc(0);
56
- stderrBuffer = Buffer.alloc(0);
55
+ lines;
57
56
  started = false;
58
57
  closed = false;
59
58
  constructor(options) {
60
59
  super();
61
60
  this.options = Object.freeze({ ...options, args: options.args ? [...options.args] : undefined });
61
+ this.lines = new JsonRpcLineReader({
62
+ maxMessageBytes: this.options.maxMessageBytes,
63
+ maxStderrBytes: this.options.maxStderrBytes,
64
+ onStderr: this.options.onStderr,
65
+ onMessage: (message) => this.emitMessage(message),
66
+ onError: (error) => this.emitError(error),
67
+ });
62
68
  }
63
69
  get pid() {
64
70
  return this.child?.pid;
65
71
  }
66
72
  get stderr() {
67
- return this.stderrBuffer.toString("utf8");
73
+ return this.lines.stderr;
68
74
  }
69
75
  async start() {
70
76
  if (this.started)
@@ -86,22 +92,20 @@ export class StdioTransport extends TransportEvents {
86
92
  if (USE_PROCESS_GROUPS && pid !== undefined) {
87
93
  installExitHook();
88
94
  liveProcessGroups.add(pid);
89
- child.once("exit", () => liveProcessGroups.delete(pid));
95
+ // Not on "exit": children of the server can outlive it while still holding its pipes.
96
+ child.once("close", () => liveProcessGroups.delete(pid));
90
97
  }
91
- child.stdout?.on("data", (chunk) => this.handleStdout(chunk));
98
+ child.stdout?.on("data", (chunk) => this.lines.readStdout(chunk));
92
99
  child.stdout?.on("error", (error) => this.emitError(error));
93
100
  child.stdin?.on("error", (error) => {
94
101
  if (!this.closed)
95
102
  this.emitError(error);
96
103
  });
97
- child.stderr?.on("data", (chunk) => this.handleStderr(chunk));
104
+ child.stderr?.on("data", (chunk) => this.lines.readStderr(chunk));
98
105
  child.stderr?.on("error", (error) => this.emitError(error));
99
106
  child.on("close", () => {
100
107
  this.child = undefined;
101
- if (this.stdoutBuffer.toString("utf8").trim()) {
102
- this.emitError(new Error("MCP stdio server closed with an incomplete JSON-RPC message"));
103
- }
104
- this.stdoutBuffer = Buffer.alloc(0);
108
+ this.lines.end();
105
109
  this.emitClose();
106
110
  });
107
111
  await new Promise((resolve, reject) => {
@@ -139,7 +143,9 @@ export class StdioTransport extends TransportEvents {
139
143
  this.emitClose();
140
144
  return;
141
145
  }
142
- if (child.exitCode !== null || child.signalCode !== null) {
146
+ // With process groups, a server that already exited can leave children holding its pipes; the
147
+ // group cleanup below still has to run for them.
148
+ if (!USE_PROCESS_GROUPS && (child.exitCode !== null || child.signalCode !== null)) {
143
149
  child.stdin?.end();
144
150
  return;
145
151
  }
@@ -160,42 +166,5 @@ export class StdioTransport extends TransportEvents {
160
166
  child.stdin?.end();
161
167
  });
162
168
  }
163
- handleStdout(chunk) {
164
- this.stdoutBuffer = Buffer.concat([this.stdoutBuffer, Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)]);
165
- const maxMessageBytes = this.options.maxMessageBytes ?? DEFAULT_MAX_MESSAGE_BYTES;
166
- while (true) {
167
- const newline = this.stdoutBuffer.indexOf(0x0a);
168
- if (newline < 0) {
169
- if (this.stdoutBuffer.length > maxMessageBytes) {
170
- this.stdoutBuffer = Buffer.alloc(0);
171
- this.emitError(new Error(`MCP stdio message exceeds ${maxMessageBytes} bytes`));
172
- }
173
- return;
174
- }
175
- const line = this.stdoutBuffer.subarray(0, newline);
176
- this.stdoutBuffer = this.stdoutBuffer.subarray(newline + 1);
177
- if (line.length > maxMessageBytes) {
178
- this.emitError(new Error(`MCP stdio message exceeds ${maxMessageBytes} bytes`));
179
- continue;
180
- }
181
- const text = line.toString("utf8").replace(/\r$/, "");
182
- if (!text.trim())
183
- continue;
184
- try {
185
- this.emitMessage(parseJsonRpcMessage(JSON.parse(text)));
186
- }
187
- catch (error) {
188
- this.emitError(error);
189
- }
190
- }
191
- }
192
- handleStderr(chunk) {
193
- const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
194
- const maxStderrBytes = this.options.maxStderrBytes ?? DEFAULT_MAX_STDERR_BYTES;
195
- this.stderrBuffer = Buffer.concat([this.stderrBuffer, buffer]);
196
- if (this.stderrBuffer.length > maxStderrBytes)
197
- this.stderrBuffer = this.stderrBuffer.subarray(-maxStderrBytes);
198
- this.options.onStderr?.(buffer.toString("utf8"));
199
- }
200
169
  }
201
170
  //# sourceMappingURL=stdio.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"stdio.js","sourceRoot":"","sources":["../../src/transports/stdio.ts"],"names":[],"mappings":"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","sourcesContent":["import { type ChildProcess, spawn } from \"node:child_process\";\nimport process from \"node:process\";\nimport crossSpawn from \"cross-spawn\";\nimport { type JsonRpcMessage, McpConnectionClosedError, parseJsonRpcMessage } from \"../protocol/jsonrpc.ts\";\nimport { DEFAULT_MAX_MESSAGE_BYTES, type McpTransport, TransportEvents } from \"./transport.ts\";\n\nconst DEFAULT_MAX_STDERR_BYTES = 64 * 1024;\nconst DEFAULT_CLOSE_TIMEOUT_MS = 2_000;\n/** How long a server gets to exit on its own after stdin closes, before it is sent SIGTERM. */\nconst STDIN_CLOSE_GRACE_MS = 500;\nconst USE_PROCESS_GROUPS = process.platform !== \"win32\";\n\n/** Process groups of running servers, killed if the host exits without closing them. */\nconst liveProcessGroups = new Set<number>();\nlet exitHookInstalled = false;\n\nfunction killProcessTree(child: ChildProcess, signal: NodeJS.Signals): void {\n\tconst pid = child.pid;\n\tif (process.platform === \"win32\" && pid !== undefined) {\n\t\tif (child.exitCode !== null) return;\n\t\t// Windows has no graceful signals, and cross-spawn runs `.cmd` shims through cmd.exe; killing only\n\t\t// that would leave the server running.\n\t\tspawn(\"taskkill\", [\"/pid\", String(pid), \"/T\", \"/F\"], { stdio: \"ignore\", windowsHide: true }).on(\n\t\t\t\"error\",\n\t\t\t() => {},\n\t\t);\n\t\treturn;\n\t}\n\tif (USE_PROCESS_GROUPS && pid !== undefined) {\n\t\ttry {\n\t\t\t// Negative pid: the whole group, so wrappers like `npx` or `uvx` do not leave the server behind.\n\t\t\tprocess.kill(-pid, signal);\n\t\t\treturn;\n\t\t} catch {\n\t\t\t// The group is gone or was never created; fall back to the direct child.\n\t\t}\n\t}\n\ttry {\n\t\tchild.kill(signal);\n\t} catch {}\n}\n\nfunction installExitHook(): void {\n\tif (exitHookInstalled) return;\n\texitHookInstalled = true;\n\tprocess.once(\"exit\", () => {\n\t\tfor (const pid of liveProcessGroups) {\n\t\t\ttry {\n\t\t\t\tprocess.kill(-pid, \"SIGTERM\");\n\t\t\t} catch {}\n\t\t}\n\t});\n}\n\nexport interface StdioTransportOptions {\n\tcommand: string;\n\targs?: readonly string[];\n\tcwd?: string;\n\tenv?: Record<string, string>;\n\tinheritEnv?: boolean;\n\tstderr?: \"pipe\" | \"inherit\";\n\tonStderr?: (chunk: string) => void;\n\tmaxMessageBytes?: number;\n\tmaxStderrBytes?: number;\n\t/** Time to wait for the server to exit after SIGTERM before sending SIGKILL. Default: 2000. */\n\tcloseTimeoutMs?: number;\n}\n\nexport class StdioTransport extends TransportEvents implements McpTransport {\n\treadonly options: Readonly<StdioTransportOptions>;\n\tprivate child: ChildProcess | undefined;\n\tprivate stdoutBuffer = Buffer.alloc(0);\n\tprivate stderrBuffer = Buffer.alloc(0);\n\tprivate started = false;\n\tprivate closed = false;\n\n\tconstructor(options: StdioTransportOptions) {\n\t\tsuper();\n\t\tthis.options = Object.freeze({ ...options, args: options.args ? [...options.args] : undefined });\n\t}\n\n\tget pid(): number | undefined {\n\t\treturn this.child?.pid;\n\t}\n\n\tget stderr(): string {\n\t\treturn this.stderrBuffer.toString(\"utf8\");\n\t}\n\n\tasync start(): Promise<void> {\n\t\tif (this.started) throw new Error(\"MCP stdio transport already started\");\n\t\tif (this.closed) throw new McpConnectionClosedError();\n\t\tthis.started = true;\n\t\tconst env = this.options.inheritEnv === false ? { ...this.options.env } : { ...process.env, ...this.options.env };\n\t\tconst child = crossSpawn(this.options.command, this.options.args ?? [], {\n\t\t\tcwd: this.options.cwd,\n\t\t\tenv,\n\t\t\tstdio: [\"pipe\", \"pipe\", this.options.stderr === \"inherit\" ? \"inherit\" : \"pipe\"],\n\t\t\twindowsHide: true,\n\t\t\t// Own process group, so closing the transport can terminate the server's children too.\n\t\t\tdetached: USE_PROCESS_GROUPS,\n\t\t});\n\t\tthis.child = child;\n\t\tconst pid = child.pid;\n\t\tif (USE_PROCESS_GROUPS && pid !== undefined) {\n\t\t\tinstallExitHook();\n\t\t\tliveProcessGroups.add(pid);\n\t\t\tchild.once(\"exit\", () => liveProcessGroups.delete(pid));\n\t\t}\n\t\tchild.stdout?.on(\"data\", (chunk: Buffer | string) => this.handleStdout(chunk));\n\t\tchild.stdout?.on(\"error\", (error) => this.emitError(error));\n\t\tchild.stdin?.on(\"error\", (error) => {\n\t\t\tif (!this.closed) this.emitError(error);\n\t\t});\n\t\tchild.stderr?.on(\"data\", (chunk: Buffer | string) => this.handleStderr(chunk));\n\t\tchild.stderr?.on(\"error\", (error) => this.emitError(error));\n\t\tchild.on(\"close\", () => {\n\t\t\tthis.child = undefined;\n\t\t\tif (this.stdoutBuffer.toString(\"utf8\").trim()) {\n\t\t\t\tthis.emitError(new Error(\"MCP stdio server closed with an incomplete JSON-RPC message\"));\n\t\t\t}\n\t\t\tthis.stdoutBuffer = Buffer.alloc(0);\n\t\t\tthis.emitClose();\n\t\t});\n\n\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\tconst onSpawn = () => {\n\t\t\t\tchild.off(\"error\", onError);\n\t\t\t\tresolve();\n\t\t\t};\n\t\t\tconst onError = (error: Error) => {\n\t\t\t\tchild.off(\"spawn\", onSpawn);\n\t\t\t\treject(error);\n\t\t\t};\n\t\t\tchild.once(\"spawn\", onSpawn);\n\t\t\tchild.once(\"error\", onError);\n\t\t});\n\t\tchild.on(\"error\", (error) => {\n\t\t\tif (!this.closed) this.emitError(error);\n\t\t});\n\t}\n\n\tasync send(message: JsonRpcMessage): Promise<void> {\n\t\tconst stdin = this.child?.stdin;\n\t\tif (!this.started || this.closed || !stdin?.writable) throw new McpConnectionClosedError();\n\t\tconst payload = `${JSON.stringify(message)}\\n`;\n\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\tstdin.write(payload, (error) => (error ? reject(error) : resolve()));\n\t\t});\n\t}\n\n\tasync close(): Promise<void> {\n\t\tif (this.closed) return;\n\t\tthis.closed = true;\n\t\tconst child = this.child;\n\t\tif (!child) {\n\t\t\tthis.emitClose();\n\t\t\treturn;\n\t\t}\n\t\tif (child.exitCode !== null || child.signalCode !== null) {\n\t\t\tchild.stdin?.end();\n\t\t\treturn;\n\t\t}\n\t\t// Shutdown per the spec: close stdin and let the server exit, then SIGTERM, then SIGKILL.\n\t\tconst closeTimeoutMs = this.options.closeTimeoutMs ?? DEFAULT_CLOSE_TIMEOUT_MS;\n\t\tawait new Promise<void>((resolve) => {\n\t\t\tconst timers: ReturnType<typeof setTimeout>[] = [];\n\t\t\tchild.once(\"close\", () => {\n\t\t\t\tfor (const timer of timers) clearTimeout(timer);\n\t\t\t\t// Children of the server that ignored stdin closing would otherwise outlive it.\n\t\t\t\tkillProcessTree(child, \"SIGTERM\");\n\t\t\t\tresolve();\n\t\t\t});\n\t\t\tconst grace = Math.min(STDIN_CLOSE_GRACE_MS, closeTimeoutMs);\n\t\t\ttimers.push(setTimeout(() => killProcessTree(child, \"SIGTERM\"), grace));\n\t\t\ttimers.push(setTimeout(() => killProcessTree(child, \"SIGKILL\"), grace + closeTimeoutMs));\n\t\t\tchild.stdin?.end();\n\t\t});\n\t}\n\n\tprivate handleStdout(chunk: Buffer | string): void {\n\t\tthis.stdoutBuffer = Buffer.concat([this.stdoutBuffer, Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)]);\n\t\tconst maxMessageBytes = this.options.maxMessageBytes ?? DEFAULT_MAX_MESSAGE_BYTES;\n\t\twhile (true) {\n\t\t\tconst newline = this.stdoutBuffer.indexOf(0x0a);\n\t\t\tif (newline < 0) {\n\t\t\t\tif (this.stdoutBuffer.length > maxMessageBytes) {\n\t\t\t\t\tthis.stdoutBuffer = Buffer.alloc(0);\n\t\t\t\t\tthis.emitError(new Error(`MCP stdio message exceeds ${maxMessageBytes} bytes`));\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst line = this.stdoutBuffer.subarray(0, newline);\n\t\t\tthis.stdoutBuffer = this.stdoutBuffer.subarray(newline + 1);\n\t\t\tif (line.length > maxMessageBytes) {\n\t\t\t\tthis.emitError(new Error(`MCP stdio message exceeds ${maxMessageBytes} bytes`));\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst text = line.toString(\"utf8\").replace(/\\r$/, \"\");\n\t\t\tif (!text.trim()) continue;\n\t\t\ttry {\n\t\t\t\tthis.emitMessage(parseJsonRpcMessage(JSON.parse(text)));\n\t\t\t} catch (error) {\n\t\t\t\tthis.emitError(error);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate handleStderr(chunk: Buffer | string): void {\n\t\tconst buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\n\t\tconst maxStderrBytes = this.options.maxStderrBytes ?? DEFAULT_MAX_STDERR_BYTES;\n\t\tthis.stderrBuffer = Buffer.concat([this.stderrBuffer, buffer]);\n\t\tif (this.stderrBuffer.length > maxStderrBytes) this.stderrBuffer = this.stderrBuffer.subarray(-maxStderrBytes);\n\t\tthis.options.onStderr?.(buffer.toString(\"utf8\"));\n\t}\n}\n"]}
1
+ {"version":3,"file":"stdio.js","sourceRoot":"","sources":["../../src/transports/stdio.ts"],"names":[],"mappings":"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","sourcesContent":["import { type ChildProcess, spawn } from \"node:child_process\";\nimport process from \"node:process\";\nimport crossSpawn from \"cross-spawn\";\nimport { type JsonRpcMessage, McpConnectionClosedError } from \"../protocol/jsonrpc.ts\";\nimport { JsonRpcLineReader } from \"./lines.ts\";\nimport { type McpTransport, TransportEvents } from \"./transport.ts\";\n\nconst DEFAULT_CLOSE_TIMEOUT_MS = 2_000;\n/** How long a server gets to exit on its own after stdin closes, before it is sent SIGTERM. */\nconst STDIN_CLOSE_GRACE_MS = 500;\nconst USE_PROCESS_GROUPS = process.platform !== \"win32\";\n\n/** Process groups of running servers, killed if the host exits without closing them. */\nconst liveProcessGroups = new Set<number>();\nlet exitHookInstalled = false;\n\nfunction killProcessTree(child: ChildProcess, signal: NodeJS.Signals): void {\n\tconst pid = child.pid;\n\tif (process.platform === \"win32\" && pid !== undefined) {\n\t\tif (child.exitCode !== null) return;\n\t\t// Windows has no graceful signals, and cross-spawn runs `.cmd` shims through cmd.exe; killing only\n\t\t// that would leave the server running.\n\t\tspawn(\"taskkill\", [\"/pid\", String(pid), \"/T\", \"/F\"], { stdio: \"ignore\", windowsHide: true }).on(\n\t\t\t\"error\",\n\t\t\t() => {},\n\t\t);\n\t\treturn;\n\t}\n\tif (USE_PROCESS_GROUPS && pid !== undefined) {\n\t\ttry {\n\t\t\t// Negative pid: the whole group, so wrappers like `npx` or `uvx` do not leave the server behind.\n\t\t\tprocess.kill(-pid, signal);\n\t\t\treturn;\n\t\t} catch {\n\t\t\t// The group is gone or was never created; fall back to the direct child.\n\t\t}\n\t}\n\ttry {\n\t\tchild.kill(signal);\n\t} catch {}\n}\n\nfunction installExitHook(): void {\n\tif (exitHookInstalled) return;\n\texitHookInstalled = true;\n\tprocess.once(\"exit\", () => {\n\t\tfor (const pid of liveProcessGroups) {\n\t\t\ttry {\n\t\t\t\tprocess.kill(-pid, \"SIGTERM\");\n\t\t\t} catch {}\n\t\t}\n\t});\n}\n\nexport interface StdioTransportOptions {\n\tcommand: string;\n\targs?: readonly string[];\n\tcwd?: string;\n\tenv?: Record<string, string>;\n\tinheritEnv?: boolean;\n\tstderr?: \"pipe\" | \"inherit\";\n\tonStderr?: (chunk: string) => void;\n\tmaxMessageBytes?: number;\n\tmaxStderrBytes?: number;\n\t/** Time to wait for the server to exit after SIGTERM before sending SIGKILL. Default: 2000. */\n\tcloseTimeoutMs?: number;\n}\n\nexport class StdioTransport extends TransportEvents implements McpTransport {\n\treadonly options: Readonly<StdioTransportOptions>;\n\tprivate child: ChildProcess | undefined;\n\tprivate readonly lines: JsonRpcLineReader;\n\tprivate started = false;\n\tprivate closed = false;\n\n\tconstructor(options: StdioTransportOptions) {\n\t\tsuper();\n\t\tthis.options = Object.freeze({ ...options, args: options.args ? [...options.args] : undefined });\n\t\tthis.lines = new JsonRpcLineReader({\n\t\t\tmaxMessageBytes: this.options.maxMessageBytes,\n\t\t\tmaxStderrBytes: this.options.maxStderrBytes,\n\t\t\tonStderr: this.options.onStderr,\n\t\t\tonMessage: (message) => this.emitMessage(message),\n\t\t\tonError: (error) => this.emitError(error),\n\t\t});\n\t}\n\n\tget pid(): number | undefined {\n\t\treturn this.child?.pid;\n\t}\n\n\tget stderr(): string {\n\t\treturn this.lines.stderr;\n\t}\n\n\tasync start(): Promise<void> {\n\t\tif (this.started) throw new Error(\"MCP stdio transport already started\");\n\t\tif (this.closed) throw new McpConnectionClosedError();\n\t\tthis.started = true;\n\t\tconst env = this.options.inheritEnv === false ? { ...this.options.env } : { ...process.env, ...this.options.env };\n\t\tconst child = crossSpawn(this.options.command, this.options.args ?? [], {\n\t\t\tcwd: this.options.cwd,\n\t\t\tenv,\n\t\t\tstdio: [\"pipe\", \"pipe\", this.options.stderr === \"inherit\" ? \"inherit\" : \"pipe\"],\n\t\t\twindowsHide: true,\n\t\t\t// Own process group, so closing the transport can terminate the server's children too.\n\t\t\tdetached: USE_PROCESS_GROUPS,\n\t\t});\n\t\tthis.child = child;\n\t\tconst pid = child.pid;\n\t\tif (USE_PROCESS_GROUPS && pid !== undefined) {\n\t\t\tinstallExitHook();\n\t\t\tliveProcessGroups.add(pid);\n\t\t\t// Not on \"exit\": children of the server can outlive it while still holding its pipes.\n\t\t\tchild.once(\"close\", () => liveProcessGroups.delete(pid));\n\t\t}\n\t\tchild.stdout?.on(\"data\", (chunk: Buffer) => this.lines.readStdout(chunk));\n\t\tchild.stdout?.on(\"error\", (error) => this.emitError(error));\n\t\tchild.stdin?.on(\"error\", (error) => {\n\t\t\tif (!this.closed) this.emitError(error);\n\t\t});\n\t\tchild.stderr?.on(\"data\", (chunk: Buffer) => this.lines.readStderr(chunk));\n\t\tchild.stderr?.on(\"error\", (error) => this.emitError(error));\n\t\tchild.on(\"close\", () => {\n\t\t\tthis.child = undefined;\n\t\t\tthis.lines.end();\n\t\t\tthis.emitClose();\n\t\t});\n\n\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\tconst onSpawn = () => {\n\t\t\t\tchild.off(\"error\", onError);\n\t\t\t\tresolve();\n\t\t\t};\n\t\t\tconst onError = (error: Error) => {\n\t\t\t\tchild.off(\"spawn\", onSpawn);\n\t\t\t\treject(error);\n\t\t\t};\n\t\t\tchild.once(\"spawn\", onSpawn);\n\t\t\tchild.once(\"error\", onError);\n\t\t});\n\t\tchild.on(\"error\", (error) => {\n\t\t\tif (!this.closed) this.emitError(error);\n\t\t});\n\t}\n\n\tasync send(message: JsonRpcMessage): Promise<void> {\n\t\tconst stdin = this.child?.stdin;\n\t\tif (!this.started || this.closed || !stdin?.writable) throw new McpConnectionClosedError();\n\t\tconst payload = `${JSON.stringify(message)}\\n`;\n\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\tstdin.write(payload, (error) => (error ? reject(error) : resolve()));\n\t\t});\n\t}\n\n\tasync close(): Promise<void> {\n\t\tif (this.closed) return;\n\t\tthis.closed = true;\n\t\tconst child = this.child;\n\t\tif (!child) {\n\t\t\tthis.emitClose();\n\t\t\treturn;\n\t\t}\n\t\t// With process groups, a server that already exited can leave children holding its pipes; the\n\t\t// group cleanup below still has to run for them.\n\t\tif (!USE_PROCESS_GROUPS && (child.exitCode !== null || child.signalCode !== null)) {\n\t\t\tchild.stdin?.end();\n\t\t\treturn;\n\t\t}\n\t\t// Shutdown per the spec: close stdin and let the server exit, then SIGTERM, then SIGKILL.\n\t\tconst closeTimeoutMs = this.options.closeTimeoutMs ?? DEFAULT_CLOSE_TIMEOUT_MS;\n\t\tawait new Promise<void>((resolve) => {\n\t\t\tconst timers: ReturnType<typeof setTimeout>[] = [];\n\t\t\tchild.once(\"close\", () => {\n\t\t\t\tfor (const timer of timers) clearTimeout(timer);\n\t\t\t\t// Children of the server that ignored stdin closing would otherwise outlive it.\n\t\t\t\tkillProcessTree(child, \"SIGTERM\");\n\t\t\t\tresolve();\n\t\t\t});\n\t\t\tconst grace = Math.min(STDIN_CLOSE_GRACE_MS, closeTimeoutMs);\n\t\t\ttimers.push(setTimeout(() => killProcessTree(child, \"SIGTERM\"), grace));\n\t\t\ttimers.push(setTimeout(() => killProcessTree(child, \"SIGKILL\"), grace + closeTimeoutMs));\n\t\t\tchild.stdin?.end();\n\t\t});\n\t}\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  import type { AuthProvider, McpFetch } from "../auth-provider.ts";
2
2
  import { type JsonRpcMessage } from "../protocol/jsonrpc.ts";
3
- import { type McpTransport, TransportEvents } from "./transport.ts";
3
+ import { type McpTransport, type McpTransportSendOptions, TransportEvents } from "./transport.ts";
4
4
  export interface SseEvent {
5
5
  event?: string;
6
6
  data: string;
@@ -56,18 +56,25 @@ export declare class StreamableHttpTransport extends TransportEvents implements
56
56
  private sessionIdValue;
57
57
  private protocolVersion;
58
58
  private getStreamStarted;
59
+ /** Access token of the latest request, which `close()` reuses instead of asking the auth provider. */
60
+ private lastToken;
59
61
  constructor(options: StreamableHttpTransportOptions);
60
62
  get sessionId(): string | undefined;
61
63
  start(): Promise<void>;
62
64
  setProtocolVersion(version: string): void;
63
- send(message: JsonRpcMessage): Promise<void>;
65
+ send(message: JsonRpcMessage, { signal }?: McpTransportSendOptions): Promise<void>;
64
66
  close(): Promise<void>;
65
67
  /**
66
68
  * Fetch with auth headers. A 401 (or a 403 asking for more scope) is handed to the auth provider
67
69
  * once, and the request is retried with whatever credentials it left behind.
70
+ *
71
+ * `signal` only stops dispatch: getting a token or the authorization recovery can take a while, and a
72
+ * request its sender gave up on in the meantime must not reach the server. It does not abort the shared
73
+ * recovery, which other requests may wait for.
68
74
  */
69
75
  private authorizedFetch;
70
76
  private headers;
77
+ private buildHeaders;
71
78
  private captureSession;
72
79
  private checkResponse;
73
80
  private consumeSse;
@@ -1 +1 @@
1
- {"version":3,"file":"streamable-http.d.ts","sourceRoot":"","sources":["../../src/transports/streamable-http.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"streamable-http.d.ts","sourceRoot":"","sources":["../../src/transports/streamable-http.ts"],"names":[],"mappings":"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"}