@smthrs/mcp 0.0.0-stage → 1.0.0-rc.4
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/CHANGELOG.md +121 -0
- package/LICENSE +21 -0
- package/README.md +111 -2
- package/dist/cjs/Diagnostics.d.ts +46 -0
- package/dist/cjs/Diagnostics.d.ts.map +1 -0
- package/dist/cjs/Diagnostics.js +29 -0
- package/dist/cjs/Diagnostics.js.map +7 -0
- package/dist/cjs/McpClient.d.ts +304 -0
- package/dist/cjs/McpClient.d.ts.map +1 -0
- package/dist/cjs/McpClient.js +622 -0
- package/dist/cjs/McpClient.js.map +7 -0
- package/dist/cjs/McpError.d.ts +44 -0
- package/dist/cjs/McpError.d.ts.map +1 -0
- package/dist/cjs/McpError.js +41 -0
- package/dist/cjs/McpError.js.map +7 -0
- package/dist/cjs/McpFlows.d.ts +113 -0
- package/dist/cjs/McpFlows.d.ts.map +1 -0
- package/dist/cjs/McpFlows.js +127 -0
- package/dist/cjs/McpFlows.js.map +7 -0
- package/dist/cjs/index.d.ts +39 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +41 -0
- package/dist/cjs/index.js.map +7 -0
- package/dist/cjs/internal/DiagnosticReporter.d.ts +17 -0
- package/dist/cjs/internal/DiagnosticReporter.d.ts.map +1 -0
- package/dist/cjs/internal/DiagnosticReporter.js +56 -0
- package/dist/cjs/internal/DiagnosticReporter.js.map +7 -0
- package/dist/cjs/internal/HttpTransport.d.ts +67 -0
- package/dist/cjs/internal/HttpTransport.d.ts.map +1 -0
- package/dist/cjs/internal/HttpTransport.js +298 -0
- package/dist/cjs/internal/HttpTransport.js.map +7 -0
- package/dist/cjs/internal/JsonLimits.d.ts +29 -0
- package/dist/cjs/internal/JsonLimits.d.ts.map +1 -0
- package/dist/cjs/internal/JsonLimits.js +51 -0
- package/dist/cjs/internal/JsonLimits.js.map +7 -0
- package/dist/cjs/internal/Limits.d.ts +36 -0
- package/dist/cjs/internal/Limits.d.ts.map +1 -0
- package/dist/cjs/internal/Limits.js +34 -0
- package/dist/cjs/internal/Limits.js.map +7 -0
- package/dist/cjs/internal/Rpc.d.ts +141 -0
- package/dist/cjs/internal/Rpc.d.ts.map +1 -0
- package/dist/cjs/internal/Rpc.js +92 -0
- package/dist/cjs/internal/Rpc.js.map +7 -0
- package/dist/cjs/internal/StdioTransport.d.ts +78 -0
- package/dist/cjs/internal/StdioTransport.d.ts.map +1 -0
- package/dist/cjs/internal/StdioTransport.js +310 -0
- package/dist/cjs/internal/StdioTransport.js.map +7 -0
- package/dist/cjs/internal/Transport.d.ts +87 -0
- package/dist/cjs/internal/Transport.d.ts.map +1 -0
- package/dist/cjs/internal/Transport.js +116 -0
- package/dist/cjs/internal/Transport.js.map +7 -0
- package/dist/cjs/package.json +1 -0
- package/dist/esm/Diagnostics.d.ts +46 -0
- package/dist/esm/Diagnostics.d.ts.map +1 -0
- package/dist/esm/Diagnostics.js +26 -0
- package/dist/esm/Diagnostics.js.map +1 -0
- package/dist/esm/McpClient.d.ts +304 -0
- package/dist/esm/McpClient.d.ts.map +1 -0
- package/dist/esm/McpClient.js +671 -0
- package/dist/esm/McpClient.js.map +1 -0
- package/dist/esm/McpError.d.ts +44 -0
- package/dist/esm/McpError.d.ts.map +1 -0
- package/dist/esm/McpError.js +43 -0
- package/dist/esm/McpError.js.map +1 -0
- package/dist/esm/McpFlows.d.ts +113 -0
- package/dist/esm/McpFlows.d.ts.map +1 -0
- package/dist/esm/McpFlows.js +168 -0
- package/dist/esm/McpFlows.js.map +1 -0
- package/dist/esm/index.d.ts +39 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +39 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/internal/DiagnosticReporter.d.ts +17 -0
- package/dist/esm/internal/DiagnosticReporter.d.ts.map +1 -0
- package/dist/esm/internal/DiagnosticReporter.js +44 -0
- package/dist/esm/internal/DiagnosticReporter.js.map +1 -0
- package/dist/esm/internal/HttpTransport.d.ts +67 -0
- package/dist/esm/internal/HttpTransport.d.ts.map +1 -0
- package/dist/esm/internal/HttpTransport.js +266 -0
- package/dist/esm/internal/HttpTransport.js.map +1 -0
- package/dist/esm/internal/JsonLimits.d.ts +29 -0
- package/dist/esm/internal/JsonLimits.d.ts.map +1 -0
- package/dist/esm/internal/JsonLimits.js +55 -0
- package/dist/esm/internal/JsonLimits.js.map +1 -0
- package/dist/esm/internal/Limits.d.ts +36 -0
- package/dist/esm/internal/Limits.d.ts.map +1 -0
- package/dist/esm/internal/Limits.js +41 -0
- package/dist/esm/internal/Limits.js.map +1 -0
- package/dist/esm/internal/Rpc.d.ts +141 -0
- package/dist/esm/internal/Rpc.d.ts.map +1 -0
- package/dist/esm/internal/Rpc.js +129 -0
- package/dist/esm/internal/Rpc.js.map +1 -0
- package/dist/esm/internal/StdioTransport.d.ts +78 -0
- package/dist/esm/internal/StdioTransport.d.ts.map +1 -0
- package/dist/esm/internal/StdioTransport.js +332 -0
- package/dist/esm/internal/StdioTransport.js.map +1 -0
- package/dist/esm/internal/Transport.d.ts +87 -0
- package/dist/esm/internal/Transport.d.ts.map +1 -0
- package/dist/esm/internal/Transport.js +146 -0
- package/dist/esm/internal/Transport.js.map +1 -0
- package/docs/README.md +139 -0
- package/docs/api.md +469 -0
- package/docs/concepts/the-session.md +135 -0
- package/docs/concepts/tools-as-flows.md +116 -0
- package/docs/guides/bound-an-untrusted-server.md +158 -0
- package/docs/guides/configure-servers-for-the-cli.md +167 -0
- package/docs/guides/connect-a-server.md +161 -0
- package/docs/guides/grant-authority-to-mcp-tools.md +130 -0
- package/docs/guides/handle-a-failed-tool-call.md +125 -0
- package/docs/guides/select-the-tools-a-run-sees.md +92 -0
- package/docs/guides/testing.md +132 -0
- package/docs/guides/validate-structured-output.md +103 -0
- package/docs/installation.md +117 -0
- package/docs/quickstart.md +200 -0
- package/docs/troubleshooting.md +316 -0
- package/package.json +157 -3
- package/src/Diagnostics.ts +47 -0
- package/src/McpClient.ts +985 -0
- package/src/McpError.ts +52 -0
- package/src/McpFlows.ts +211 -0
- package/src/index.ts +42 -0
- package/src/internal/DiagnosticReporter.ts +47 -0
- package/src/internal/HttpTransport.ts +400 -0
- package/src/internal/JsonLimits.ts +53 -0
- package/src/internal/Limits.ts +48 -0
- package/src/internal/Rpc.ts +219 -0
- package/src/internal/StdioTransport.ts +491 -0
- package/src/internal/Transport.ts +178 -0
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var StdioTransport_exports = {};
|
|
30
|
+
__export(StdioTransport_exports, {
|
|
31
|
+
connect: () => connect,
|
|
32
|
+
defaultMaxStderrBytes: () => defaultMaxStderrBytes,
|
|
33
|
+
defaultQueueCapacity: () => defaultQueueCapacity
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(StdioTransport_exports);
|
|
36
|
+
var Redaction = __toESM(require("@smthrs/journal/Redaction"), 1);
|
|
37
|
+
var ChildProcessEnvironment = __toESM(require("@smthrs/kernel/ChildProcessEnvironment"), 1);
|
|
38
|
+
var import_effect = require("effect");
|
|
39
|
+
var ChildProcess = __toESM(require("effect/unstable/process/ChildProcess"), 1);
|
|
40
|
+
var import_McpError = require("../McpError.js");
|
|
41
|
+
var DiagnosticReporter = __toESM(require("./DiagnosticReporter.js"), 1);
|
|
42
|
+
var JsonLimits = __toESM(require("./JsonLimits.js"), 1);
|
|
43
|
+
var Limits = __toESM(require("./Limits.js"), 1);
|
|
44
|
+
var Rpc = __toESM(require("./Rpc.js"), 1);
|
|
45
|
+
var Transport = __toESM(require("./Transport.js"), 1);
|
|
46
|
+
const emptyStderrTail = () => ({
|
|
47
|
+
redactor: Redaction.lineRedactor(Redaction.diagnosticRules),
|
|
48
|
+
redacted: ""
|
|
49
|
+
});
|
|
50
|
+
const utf8 = new TextEncoder();
|
|
51
|
+
const tailBytes = (text, limit) => {
|
|
52
|
+
const bytes = utf8.encode(text);
|
|
53
|
+
if (bytes.byteLength <= limit) return text;
|
|
54
|
+
let start = bytes.byteLength - limit;
|
|
55
|
+
while (start < bytes.byteLength && (bytes[start] & 192) === 128) start += 1;
|
|
56
|
+
return new TextDecoder().decode(bytes.subarray(start));
|
|
57
|
+
};
|
|
58
|
+
const appendStderr = (current, text, limit) => {
|
|
59
|
+
const segments = text.split("\n");
|
|
60
|
+
const last = segments.pop();
|
|
61
|
+
let redacted = current.redacted;
|
|
62
|
+
for (const segment of segments) {
|
|
63
|
+
for (const clean of current.redactor.line(segment)) redacted += `${clean}
|
|
64
|
+
`;
|
|
65
|
+
}
|
|
66
|
+
if (last !== "") current.redactor.part(last);
|
|
67
|
+
return { redactor: current.redactor, redacted: tailBytes(redacted, limit) };
|
|
68
|
+
};
|
|
69
|
+
const renderStderr = (current, limit) => tailBytes(`${current.redacted}${current.redactor.peek().join("\n")}`.replace(/\s+/g, " ").trim(), limit);
|
|
70
|
+
const defaultQueueCapacity = 64;
|
|
71
|
+
const defaultMaxStderrBytes = 2048;
|
|
72
|
+
const diagnosticErrorCodes = /* @__PURE__ */ new Set(["spawn_failed", "timeout", "connection_closed"]);
|
|
73
|
+
const terminalStderrDrainMs = 250;
|
|
74
|
+
const frames = (server, maxFrameBytes, stream) => Transport.lines(server, maxFrameBytes, stream).pipe(import_effect.Stream.filter((line) => line.trim() !== ""));
|
|
75
|
+
const connect = (options) => import_effect.Effect.gen(function* () {
|
|
76
|
+
const diagnostic = yield* DiagnosticReporter.make(options.server);
|
|
77
|
+
const requestTimeoutMs = options.requestTimeoutMs ?? Transport.defaultRequestTimeoutMs;
|
|
78
|
+
const queueCapacity = options.queueCapacity ?? defaultQueueCapacity;
|
|
79
|
+
const maxFrameBytes = options.maxFrameBytes ?? Transport.defaultMaxFrameBytes;
|
|
80
|
+
const maxOutboundFrameBytes = options.maxOutboundFrameBytes ?? Transport.defaultMaxOutboundFrameBytes;
|
|
81
|
+
const maxStderrBytes = options.maxStderrBytes ?? defaultMaxStderrBytes;
|
|
82
|
+
yield* Limits.checkPositiveIntegers(options.server, [
|
|
83
|
+
["requestTimeoutMs", requestTimeoutMs],
|
|
84
|
+
["queueCapacity", queueCapacity],
|
|
85
|
+
["maxFrameBytes", maxFrameBytes],
|
|
86
|
+
["maxOutboundFrameBytes", maxOutboundFrameBytes],
|
|
87
|
+
["maxStderrBytes", maxStderrBytes]
|
|
88
|
+
]);
|
|
89
|
+
const handle = yield* ChildProcess.make(options.command, options.args, {
|
|
90
|
+
cwd: options.cwd,
|
|
91
|
+
env: ChildProcessEnvironment.make(process.env, options.env),
|
|
92
|
+
extendEnv: false,
|
|
93
|
+
stdin: "pipe",
|
|
94
|
+
stdout: "pipe",
|
|
95
|
+
// Stderr is diagnostic only: a scoped drainer below retains at most the
|
|
96
|
+
// configured byte tail, and stderr failure never fails the connection.
|
|
97
|
+
stderr: "pipe"
|
|
98
|
+
}).pipe(
|
|
99
|
+
import_effect.Effect.mapError((error) => {
|
|
100
|
+
diagnostic("spawn", error.message);
|
|
101
|
+
return new import_McpError.McpError({
|
|
102
|
+
code: "spawn_failed",
|
|
103
|
+
message: `Failed to start MCP server "${options.server}"; process details withheld`,
|
|
104
|
+
server: options.server
|
|
105
|
+
});
|
|
106
|
+
})
|
|
107
|
+
);
|
|
108
|
+
const stderrState = yield* import_effect.Ref.make(emptyStderrTail());
|
|
109
|
+
const stderrDecoder = new TextDecoder();
|
|
110
|
+
const stderrDrainer = yield* handle.stderr.pipe(
|
|
111
|
+
import_effect.Stream.runForEach(
|
|
112
|
+
(chunk) => import_effect.Ref.update(
|
|
113
|
+
stderrState,
|
|
114
|
+
(current) => appendStderr(current, stderrDecoder.decode(chunk, { stream: true }), maxStderrBytes)
|
|
115
|
+
)
|
|
116
|
+
),
|
|
117
|
+
import_effect.Effect.ignore,
|
|
118
|
+
import_effect.Effect.forkScoped
|
|
119
|
+
);
|
|
120
|
+
const withStderr = (error) => {
|
|
121
|
+
if (!diagnosticErrorCodes.has(error.code)) return import_effect.Effect.succeed(error);
|
|
122
|
+
return import_effect.Effect.map(import_effect.Ref.get(stderrState), (current) => {
|
|
123
|
+
const rendered = renderStderr(current, maxStderrBytes);
|
|
124
|
+
if (rendered !== "") diagnostic("stderr", rendered);
|
|
125
|
+
return rendered === "" ? error : new import_McpError.McpError({
|
|
126
|
+
code: error.code,
|
|
127
|
+
message: `${error.message} (stderr diagnostic withheld)`,
|
|
128
|
+
server: error.server
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
};
|
|
132
|
+
const nextId = yield* import_effect.Ref.make(0);
|
|
133
|
+
const outbound = yield* import_effect.Queue.bounded(queueCapacity);
|
|
134
|
+
const terminalError = yield* import_effect.Deferred.make();
|
|
135
|
+
const state = yield* import_effect.Ref.make({ _tag: "Open", pending: import_effect.HashMap.empty() });
|
|
136
|
+
const enqueue = (frame) => import_effect.Effect.flatMap(import_effect.Queue.offer(outbound, frame), (offered) => offered ? import_effect.Effect.void : import_effect.Effect.flatMap(import_effect.Deferred.await(terminalError), import_effect.Effect.fail));
|
|
137
|
+
const frameOf = (method, message) => import_effect.Effect.try({
|
|
138
|
+
try: () => Rpc.encode(message),
|
|
139
|
+
catch: () => Limits.protocolError(options.server, `MCP server "${options.server}" could not encode a ${method} frame`)
|
|
140
|
+
}).pipe(
|
|
141
|
+
import_effect.Effect.flatMap(
|
|
142
|
+
(frame) => frame.byteLength <= maxOutboundFrameBytes ? import_effect.Effect.succeed(frame) : import_effect.Effect.fail(
|
|
143
|
+
Limits.protocolError(
|
|
144
|
+
options.server,
|
|
145
|
+
`MCP server "${options.server}" tried to send a ${method} frame larger than ${maxOutboundFrameBytes} bytes`
|
|
146
|
+
)
|
|
147
|
+
)
|
|
148
|
+
)
|
|
149
|
+
);
|
|
150
|
+
const closeWith = (baseError, drainStderr = true) => import_effect.Effect.uninterruptible(import_effect.Effect.gen(function* () {
|
|
151
|
+
const waiters = yield* import_effect.Ref.modify(state, (current) => current._tag === "Closed" ? [void 0, current] : [Array.from(import_effect.HashMap.values(current.pending)), { _tag: "Closed", error: terminalError }]);
|
|
152
|
+
if (waiters === void 0) return;
|
|
153
|
+
yield* import_effect.Queue.shutdown(outbound);
|
|
154
|
+
yield* (drainStderr && diagnosticErrorCodes.has(baseError.code) ? import_effect.Fiber.await(stderrDrainer).pipe(
|
|
155
|
+
import_effect.Effect.asVoid,
|
|
156
|
+
import_effect.Effect.timeoutOrElse({ duration: terminalStderrDrainMs, orElse: () => import_effect.Effect.void }),
|
|
157
|
+
import_effect.Effect.interruptible
|
|
158
|
+
) : import_effect.Effect.void).pipe(import_effect.Effect.ensuring(import_effect.Effect.gen(function* () {
|
|
159
|
+
const error = yield* withStderr(baseError);
|
|
160
|
+
yield* import_effect.Deferred.succeed(terminalError, error);
|
|
161
|
+
yield* import_effect.Effect.forEach(waiters, (pending) => import_effect.Deferred.fail(pending.deferred, error), {
|
|
162
|
+
discard: true
|
|
163
|
+
});
|
|
164
|
+
})));
|
|
165
|
+
}));
|
|
166
|
+
yield* import_effect.Stream.fromEffectRepeat(import_effect.Queue.take(outbound)).pipe(
|
|
167
|
+
import_effect.Stream.filter((record) => {
|
|
168
|
+
if (record.request === void 0) return true;
|
|
169
|
+
if (record.request.cancelled) return false;
|
|
170
|
+
record.request.dispatched = true;
|
|
171
|
+
return true;
|
|
172
|
+
}),
|
|
173
|
+
import_effect.Stream.map((record) => record.frame),
|
|
174
|
+
import_effect.Stream.run(handle.stdin),
|
|
175
|
+
import_effect.Effect.onExit((exit) => closeWith(Transport.closed(options.server, "stdin closed"), !import_effect.Exit.hasInterrupts(exit))),
|
|
176
|
+
import_effect.Effect.forkScoped
|
|
177
|
+
);
|
|
178
|
+
yield* frames(options.server, maxFrameBytes, handle.stdout).pipe(
|
|
179
|
+
import_effect.Stream.runForEach(
|
|
180
|
+
(line) => import_effect.Effect.gen(function* () {
|
|
181
|
+
const message = Rpc.parse(line);
|
|
182
|
+
if (message === void 0) return;
|
|
183
|
+
const jsonIssue = JsonLimits.checkParsed(message);
|
|
184
|
+
if (jsonIssue !== void 0) {
|
|
185
|
+
return yield* import_effect.Effect.fail(
|
|
186
|
+
Limits.protocolError(options.server, `MCP server "${options.server}" sent invalid JSON: ${jsonIssue}`)
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
const reply = Rpc.classify(message);
|
|
190
|
+
if (reply._tag === "Notification") return;
|
|
191
|
+
if (reply._tag === "Request") {
|
|
192
|
+
const response = reply.method === "ping" ? { jsonrpc: "2.0", id: reply.id, result: {} } : { jsonrpc: "2.0", id: reply.id, error: { code: -32601, message: "Method not found" } };
|
|
193
|
+
return yield* frameOf("server-response", response).pipe(
|
|
194
|
+
import_effect.Effect.flatMap((frame) => enqueue({ frame })),
|
|
195
|
+
import_effect.Effect.timeoutOrElse({
|
|
196
|
+
duration: requestTimeoutMs,
|
|
197
|
+
orElse: () => import_effect.Effect.fail(Transport.timeout(options.server, "server-response admission", requestTimeoutMs))
|
|
198
|
+
})
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
if (reply._tag === "Malformed") {
|
|
202
|
+
return yield* import_effect.Effect.fail(
|
|
203
|
+
Limits.protocolError(
|
|
204
|
+
options.server,
|
|
205
|
+
`MCP server "${options.server}" sent a malformed JSON-RPC reply: ${reply.reason}`
|
|
206
|
+
)
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
if (reply._tag === "UncorrelatedError") {
|
|
210
|
+
diagnostic("remote-error", { code: reply.code, message: reply.message, data: reply.data });
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
const pending = yield* import_effect.Ref.modify(state, (current) => {
|
|
214
|
+
if (current._tag === "Closed") return [import_effect.Option.none(), current];
|
|
215
|
+
return [
|
|
216
|
+
import_effect.HashMap.get(current.pending, reply.id),
|
|
217
|
+
{ ...current, pending: import_effect.HashMap.remove(current.pending, reply.id) }
|
|
218
|
+
];
|
|
219
|
+
});
|
|
220
|
+
if (import_effect.Option.isNone(pending)) return;
|
|
221
|
+
if (reply._tag === "Error") {
|
|
222
|
+
diagnostic("remote-error", { code: reply.code, message: reply.message, data: reply.data });
|
|
223
|
+
yield* import_effect.Deferred.fail(
|
|
224
|
+
pending.value.deferred,
|
|
225
|
+
Transport.replyError(options.server, pending.value.method, reply)
|
|
226
|
+
);
|
|
227
|
+
} else {
|
|
228
|
+
yield* import_effect.Deferred.succeed(pending.value.deferred, reply.result);
|
|
229
|
+
}
|
|
230
|
+
})
|
|
231
|
+
),
|
|
232
|
+
import_effect.Effect.matchEffect({
|
|
233
|
+
onFailure: (error) => closeWith(
|
|
234
|
+
error instanceof import_McpError.McpError ? error : Transport.closed(options.server, "stdout failed")
|
|
235
|
+
),
|
|
236
|
+
// A clean EOF is still a closed MCP connection. Node reports an
|
|
237
|
+
// ordinary child exit by ending stdout successfully.
|
|
238
|
+
onSuccess: () => closeWith(Transport.closed(options.server, "stdout closed"))
|
|
239
|
+
}),
|
|
240
|
+
import_effect.Effect.forkScoped
|
|
241
|
+
);
|
|
242
|
+
yield* handle.exitCode.pipe(
|
|
243
|
+
import_effect.Effect.flatMap((exitCode) => closeWith(Transport.closed(options.server, `exited with code ${exitCode}`))),
|
|
244
|
+
import_effect.Effect.catch(() => closeWith(Transport.closed(options.server, "process exited"))),
|
|
245
|
+
import_effect.Effect.forkScoped
|
|
246
|
+
);
|
|
247
|
+
yield* import_effect.Effect.addFinalizer(() => closeWith(Transport.closed(options.server, "connection scope closed"), false));
|
|
248
|
+
const takePending = (id) => import_effect.Ref.modify(state, (current) => {
|
|
249
|
+
if (current._tag === "Closed") return [false, current];
|
|
250
|
+
const present = import_effect.HashMap.has(current.pending, id);
|
|
251
|
+
return [present, { ...current, pending: import_effect.HashMap.remove(current.pending, id) }];
|
|
252
|
+
});
|
|
253
|
+
const request = (method, params, timeoutMs = requestTimeoutMs) => import_effect.Effect.gen(function* () {
|
|
254
|
+
if (!Limits.isPositiveInteger(timeoutMs)) {
|
|
255
|
+
return yield* import_effect.Effect.fail(
|
|
256
|
+
Limits.protocolError(options.server, "MCP request timeout must be a positive integer")
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
const id = yield* import_effect.Ref.updateAndGet(nextId, (n) => n + 1);
|
|
260
|
+
const deferred = yield* import_effect.Deferred.make();
|
|
261
|
+
const frame = yield* frameOf(method, { jsonrpc: "2.0", id, method, params });
|
|
262
|
+
const requestState = { id, cancelled: false, dispatched: false };
|
|
263
|
+
return yield* import_effect.Effect.gen(function* () {
|
|
264
|
+
const registration = yield* import_effect.Ref.modify(state, (current) => current._tag === "Closed" ? [current.error, current] : [void 0, {
|
|
265
|
+
...current,
|
|
266
|
+
pending: import_effect.HashMap.set(current.pending, id, { deferred, method })
|
|
267
|
+
}]);
|
|
268
|
+
if (registration !== void 0) return yield* import_effect.Effect.flatMap(import_effect.Deferred.await(registration), import_effect.Effect.fail);
|
|
269
|
+
yield* enqueue({ frame, request: requestState });
|
|
270
|
+
return yield* import_effect.Deferred.await(deferred);
|
|
271
|
+
}).pipe(
|
|
272
|
+
import_effect.Effect.ensuring(import_effect.Effect.gen(function* () {
|
|
273
|
+
requestState.cancelled = true;
|
|
274
|
+
const pending = yield* takePending(id);
|
|
275
|
+
if (!requestState.dispatched || !pending || method === "initialize") return;
|
|
276
|
+
yield* frameOf("notifications/cancelled", {
|
|
277
|
+
jsonrpc: "2.0",
|
|
278
|
+
method: "notifications/cancelled",
|
|
279
|
+
params: { requestId: requestState.id, reason: Transport.cancellationReason }
|
|
280
|
+
}).pipe(
|
|
281
|
+
// Best-effort cancellation cannot delay the deadline it reports.
|
|
282
|
+
import_effect.Effect.flatMap((frame2) => import_effect.Effect.sync(() => import_effect.Queue.offerUnsafe(outbound, { frame: frame2 }))),
|
|
283
|
+
import_effect.Effect.ignore
|
|
284
|
+
);
|
|
285
|
+
})),
|
|
286
|
+
import_effect.Effect.timeoutOrElse({
|
|
287
|
+
duration: timeoutMs,
|
|
288
|
+
orElse: () => import_effect.Effect.flatMap(withStderr(Transport.timeout(options.server, method, timeoutMs)), import_effect.Effect.fail)
|
|
289
|
+
})
|
|
290
|
+
);
|
|
291
|
+
});
|
|
292
|
+
const notify = (method, params, timeoutMs = requestTimeoutMs) => {
|
|
293
|
+
if (!Limits.isPositiveInteger(timeoutMs)) {
|
|
294
|
+
return import_effect.Effect.fail(Limits.protocolError(options.server, "MCP notification timeout must be a positive integer"));
|
|
295
|
+
}
|
|
296
|
+
return import_effect.Effect.gen(function* () {
|
|
297
|
+
const current = yield* import_effect.Ref.get(state);
|
|
298
|
+
if (current._tag === "Closed") return yield* import_effect.Effect.flatMap(import_effect.Deferred.await(current.error), import_effect.Effect.fail);
|
|
299
|
+
const frame = yield* frameOf(method, { jsonrpc: "2.0", method, params });
|
|
300
|
+
yield* enqueue({ frame });
|
|
301
|
+
}).pipe(
|
|
302
|
+
import_effect.Effect.timeoutOrElse({
|
|
303
|
+
duration: timeoutMs,
|
|
304
|
+
orElse: () => import_effect.Effect.flatMap(withStderr(Transport.timeout(options.server, method, timeoutMs)), import_effect.Effect.fail)
|
|
305
|
+
})
|
|
306
|
+
);
|
|
307
|
+
};
|
|
308
|
+
return { request, notify };
|
|
309
|
+
});
|
|
310
|
+
//# sourceMappingURL=StdioTransport.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/internal/StdioTransport.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Newline-delimited JSON-RPC transport over a spawned MCP server's stdio.\n *\n * This module owns exactly the connection lifecycle and request/reply\n * correlation an MCP session needs: spawn once, write frames in, read frames\n * out, match replies to the request that asked for them. It knows nothing\n * about `initialize`, `tools/list`, or `tools/call`. It answers the protocol's\n * liveness `ping` with an empty result, and unsupported server requests with\n * method-not-found. {@link McpClient} owns feature negotiation and tool calls.\n *\n * Server-initiated notifications are received and dropped. A future caller\n * that needs `notifications/*` (for example a progress stream) is the reason\n * to add a subscription surface here rather than threading one more parameter\n * through every constructor now.\n *\n * @since 1.0.0-rc.0\n */\n\nimport * as Redaction from \"@smthrs/journal/Redaction\"\nimport * as ChildProcessEnvironment from \"@smthrs/kernel/ChildProcessEnvironment\"\nimport { Deferred, Effect, Exit, Fiber, HashMap, Option, Queue, Ref, Stream } from \"effect\"\nimport type { Scope } from \"effect\"\nimport * as ChildProcess from \"effect/unstable/process/ChildProcess\"\nimport type { ChildProcessSpawner } from \"effect/unstable/process/ChildProcessSpawner\"\nimport { McpError } from \"../McpError.ts\"\nimport * as DiagnosticReporter from \"./DiagnosticReporter.ts\"\nimport * as JsonLimits from \"./JsonLimits.ts\"\nimport * as Limits from \"./Limits.ts\"\nimport * as Rpc from \"./Rpc.ts\"\nimport * as Transport from \"./Transport.ts\"\n\n/**\n * The retained stderr diagnostic: the line redactor, which owns every line\n * still open or arriving, and the redacted lines it has released, capped to\n * the configured byte tail.\n */\ninterface StderrTail {\n readonly redactor: Redaction.LineRedactor\n readonly redacted: string\n}\n\nconst emptyStderrTail = (): StderrTail => ({\n redactor: Redaction.lineRedactor(Redaction.diagnosticRules),\n redacted: \"\"\n})\n\nconst utf8 = new TextEncoder()\n\n/** The last `limit` bytes of `text`, cut on a UTF-8 character boundary. */\nconst tailBytes = (text: string, limit: number): string => {\n const bytes = utf8.encode(text)\n if (bytes.byteLength <= limit) return text\n let start = bytes.byteLength - limit\n // Skip continuation bytes so the tail starts on a whole character.\n while (start < bytes.byteLength && (bytes[start]! & 0xc0) === 0x80) start += 1\n return new TextDecoder().decode(bytes.subarray(start))\n}\n\n/**\n * Adds decoded stderr text. The redactor takes each complete line and the\n * part of a line still arriving, holds a value that spans lines (a private\n * key block, a string `util.inspect` split with `+`) until it closes, bounds\n * an overlong line itself, and returns only what is safe to show.\n */\nconst appendStderr = (current: StderrTail, text: string, limit: number): StderrTail => {\n const segments = text.split(\"\\n\")\n const last = segments.pop()!\n let redacted = current.redacted\n for (const segment of segments) {\n for (const clean of current.redactor.line(segment)) redacted += `${clean}\\n`\n }\n if (last !== \"\") current.redactor.part(last)\n return { redactor: current.redactor, redacted: tailBytes(redacted, limit) }\n}\n\n/**\n * The diagnostic an observer receives: the released tail and what the\n * redactor would release now, whitespace flattened, capped to the last\n * `limit` bytes. The cap runs after redaction, so it can only cut a\n * placeholder, never a credential.\n */\nconst renderStderr = (current: StderrTail, limit: number): string =>\n tailBytes(`${current.redacted}${current.redactor.peek().join(\"\\n\")}`.replace(/\\s+/g, \" \").trim(), limit)\n\n/**\n * Options accepted by {@link connect}.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport interface ConnectOptions {\n /** The name this server is known by, for error messages only. */\n readonly server: string\n readonly command: string\n readonly args: ReadonlyArray<string>\n readonly cwd?: string | undefined\n /** Values merged into the bootstrap allowlist rather than the full host environment. */\n readonly env?: Record<string, string | undefined> | undefined\n /** Default deadline for a request/reply exchange. See {@link Transport.defaultRequestTimeoutMs}. */\n readonly requestTimeoutMs?: number | undefined\n /** Maximum number of frames waiting to be written. See {@link defaultQueueCapacity}. */\n readonly queueCapacity?: number | undefined\n /** Maximum UTF-8 bytes accepted in one inbound JSON-RPC frame. See {@link Transport.defaultMaxFrameBytes}. */\n readonly maxFrameBytes?: number | undefined\n /** Maximum UTF-8 bytes emitted in one JSON-RPC frame. See {@link Transport.defaultMaxOutboundFrameBytes}. */\n readonly maxOutboundFrameBytes?: number | undefined\n /** Maximum diagnostic stderr bytes retained in memory. See {@link defaultMaxStderrBytes}. */\n readonly maxStderrBytes?: number | undefined\n}\n\n/**\n * Default number of outbound frames allowed to wait in memory.\n *\n * @category constants\n * @since 1.0.0-rc.0\n */\nexport const defaultQueueCapacity = 64\n\n/**\n * Default maximum child-stderr tail retained for connection diagnostics.\n *\n * @category constants\n * @since 1.0.0-rc.0\n */\nexport const defaultMaxStderrBytes = 2048\n\nconst diagnosticErrorCodes: ReadonlySet<string> = new Set([\"spawn_failed\", \"timeout\", \"connection_closed\"])\n// Exit and stdout EOF can precede the parent's final stderr read. Await the\n// actual drainer, with a finite fallback for a child/descendant holding its\n// stderr pipe open. Caller interruption never has to wait out this budget.\nconst terminalStderrDrainMs = 250\n\ntype Pending = {\n readonly deferred: Deferred.Deferred<unknown, McpError>\n readonly method: string\n}\n\ntype OutboundFrame = {\n readonly frame: Uint8Array\n readonly request?: {\n readonly id: number\n cancelled: boolean\n dispatched: boolean\n }\n}\n\ntype ConnectionState = {\n readonly _tag: \"Open\"\n readonly pending: HashMap.HashMap<number, Pending>\n} | {\n readonly _tag: \"Closed\"\n readonly error: Deferred.Deferred<McpError>\n}\n\n/** Stdout lines, without the blank lines servers commonly emit between frames. */\nconst frames = (\n server: string,\n maxFrameBytes: number,\n stream: Stream.Stream<Uint8Array, unknown>\n): Stream.Stream<string, unknown | McpError> =>\n Transport.lines(server, maxFrameBytes, stream).pipe(Stream.filter((line) => line.trim() !== \"\"))\n\n/**\n * Spawns an MCP server over stdio and returns a live {@link Transport}.\n *\n * The connection is scoped: the writer and reader loops are daemon fibers\n * forked into the calling scope, and closing that scope tears the process\n * down with it. Every request pending when the connection closes fails with\n * `connection_closed` instead of hanging forever.\n *\n * Stdout that does not claim JSON-RPC is ignored because servers commonly log\n * there. Once an object carries its own `jsonrpc` property, a malformed version\n * or reply closes the connection with `protocol_error`.\n *\n * @category constructors\n * @since 1.0.0-rc.0\n */\nexport const connect = (\n options: ConnectOptions\n): Effect.Effect<Transport.Transport, McpError, ChildProcessSpawner | Scope.Scope> =>\n Effect.gen(function*() {\n const diagnostic = yield* DiagnosticReporter.make(options.server)\n const requestTimeoutMs = options.requestTimeoutMs ?? Transport.defaultRequestTimeoutMs\n const queueCapacity = options.queueCapacity ?? defaultQueueCapacity\n const maxFrameBytes = options.maxFrameBytes ?? Transport.defaultMaxFrameBytes\n const maxOutboundFrameBytes = options.maxOutboundFrameBytes ?? Transport.defaultMaxOutboundFrameBytes\n const maxStderrBytes = options.maxStderrBytes ?? defaultMaxStderrBytes\n yield* Limits.checkPositiveIntegers(options.server, [\n [\"requestTimeoutMs\", requestTimeoutMs],\n [\"queueCapacity\", queueCapacity],\n [\"maxFrameBytes\", maxFrameBytes],\n [\"maxOutboundFrameBytes\", maxOutboundFrameBytes],\n [\"maxStderrBytes\", maxStderrBytes]\n ])\n\n const handle = yield* ChildProcess.make(options.command, options.args, {\n cwd: options.cwd,\n env: ChildProcessEnvironment.make(process.env, options.env),\n extendEnv: false,\n stdin: \"pipe\",\n stdout: \"pipe\",\n // Stderr is diagnostic only: a scoped drainer below retains at most the\n // configured byte tail, and stderr failure never fails the connection.\n stderr: \"pipe\"\n }).pipe(\n Effect.mapError((error) => {\n diagnostic(\"spawn\", error.message)\n return new McpError({\n code: \"spawn_failed\",\n message: `Failed to start MCP server \"${options.server}\"; process details withheld`,\n server: options.server\n })\n })\n )\n\n // Stderr is redacted a whole line at a time as it arrives, and only the\n // redacted text is capped. Capping raw bytes first could cut a\n // credential's recognizable prefix and leave its remainder unredacted.\n const stderrState = yield* Ref.make<StderrTail>(emptyStderrTail())\n const stderrDecoder = new TextDecoder()\n const stderrDrainer = yield* handle.stderr.pipe(\n Stream.runForEach((chunk) =>\n Ref.update(\n stderrState,\n (current) => appendStderr(current, stderrDecoder.decode(chunk, { stream: true }), maxStderrBytes)\n )\n ),\n Effect.ignore,\n Effect.forkScoped\n )\n\n const withStderr = (error: McpError): Effect.Effect<McpError> => {\n if (!diagnosticErrorCodes.has(error.code)) return Effect.succeed(error)\n return Effect.map(Ref.get(stderrState), (current) => {\n const rendered = renderStderr(current, maxStderrBytes)\n if (rendered !== \"\") diagnostic(\"stderr\", rendered)\n return rendered === \"\"\n ? error\n : new McpError({\n code: error.code,\n message: `${error.message} (stderr diagnostic withheld)`,\n server: error.server\n })\n })\n }\n\n const nextId = yield* Ref.make(0)\n const outbound = yield* Queue.bounded<OutboundFrame>(queueCapacity)\n const terminalError = yield* Deferred.make<McpError>()\n const state = yield* Ref.make<ConnectionState>({ _tag: \"Open\", pending: HashMap.empty() })\n\n // Define these before starting the reader: a server can send a request\n // immediately on connection, before our first outbound request exists.\n const enqueue = (frame: OutboundFrame): Effect.Effect<void, McpError> =>\n Effect.flatMap(Queue.offer(outbound, frame), (offered) =>\n offered\n ? Effect.void\n : Effect.flatMap(Deferred.await(terminalError), Effect.fail))\n\n const frameOf = (method: string, message: Rpc.OutboundMessage): Effect.Effect<Uint8Array, McpError> =>\n Effect.try({\n try: () => Rpc.encode(message),\n catch: () =>\n Limits.protocolError(options.server, `MCP server \"${options.server}\" could not encode a ${method} frame`)\n }).pipe(\n Effect.flatMap((frame) =>\n frame.byteLength <= maxOutboundFrameBytes\n ? Effect.succeed(frame)\n : Effect.fail(\n Limits.protocolError(\n options.server,\n `MCP server \"${options.server}\" tried to send a ${method} frame larger than ${maxOutboundFrameBytes} bytes`\n )\n )\n )\n )\n\n /** Closes once, fails every waiter, and rejects all future traffic. */\n const closeWith = (baseError: McpError, drainStderr = true) =>\n Effect.uninterruptible(Effect.gen(function*() {\n const waiters = yield* Ref.modify(state, (current) =>\n current._tag === \"Closed\"\n ? [undefined, current] as const\n : [Array.from(HashMap.values(current.pending)), { _tag: \"Closed\", error: terminalError }] as const)\n if (waiters === undefined) return\n // Stop admission immediately. Pending requests and blocked/new offers\n // share the terminal result, so none can tear down the stderr reader\n // merely because a different process signal won the close race.\n yield* Queue.shutdown(outbound)\n yield* (drainStderr && diagnosticErrorCodes.has(baseError.code)\n ? Fiber.await(stderrDrainer).pipe(\n Effect.asVoid,\n Effect.timeoutOrElse({ duration: terminalStderrDrainMs, orElse: () => Effect.void }),\n Effect.interruptible\n )\n : Effect.void).pipe(Effect.ensuring(Effect.gen(function*() {\n // Even interruption while draining settles every waiter. Awaiting\n // the drainer's fiber does not interrupt that fiber on timeout.\n const error = yield* withStderr(baseError)\n yield* Deferred.succeed(terminalError, error)\n yield* Effect.forEach(waiters, (pending) => Deferred.fail(pending.deferred, error), {\n discard: true\n })\n })))\n }))\n\n // Writer: drains outbound frames into the process's stdin for the life of\n // the connection scope. A write failure is the same \"connection is gone\"\n // fact the reader loop reports, so it collapses pending requests too.\n // Pull one record at a time: batching would mark later records dispatched\n // while an earlier stdin write is still blocked. Check and mark without a\n // yield so request cleanup cannot interleave with the dispatch decision.\n yield* Stream.fromEffectRepeat(Queue.take(outbound)).pipe(\n Stream.filter((record) => {\n if (record.request === undefined) return true\n if (record.request.cancelled) return false\n record.request.dispatched = true\n return true\n }),\n Stream.map((record) => record.frame),\n Stream.run(handle.stdin),\n Effect.onExit((exit) => closeWith(Transport.closed(options.server, \"stdin closed\"), !Exit.hasInterrupts(exit))),\n Effect.forkScoped\n )\n\n // Reader: one line of stdout is one JSON-RPC message. A validated reply\n // resolves its pending request by id; malformed tagged messages close the\n // whole connection, while stdout noise, notifications, and unknown ids drop.\n yield* frames(options.server, maxFrameBytes, handle.stdout).pipe(\n Stream.runForEach((line) =>\n Effect.gen(function*() {\n const message = Rpc.parse(line)\n if (message === undefined) return\n const jsonIssue = JsonLimits.checkParsed(message)\n if (jsonIssue !== undefined) {\n return yield* Effect.fail(\n Limits.protocolError(options.server, `MCP server \"${options.server}\" sent invalid JSON: ${jsonIssue}`)\n )\n }\n const reply = Rpc.classify(message)\n if (reply._tag === \"Notification\") return\n if (reply._tag === \"Request\") {\n const response: Rpc.OutboundMessage = reply.method === \"ping\"\n ? { jsonrpc: \"2.0\", id: reply.id, result: {} }\n : { jsonrpc: \"2.0\", id: reply.id, error: { code: -32_601, message: \"Method not found\" } }\n // Opposite-direction ids are independent, even when they equal an\n // active tool request's id. Responses share the bounded writer and\n // size guard, never creating another pending request of our own.\n return yield* frameOf(\"server-response\", response).pipe(\n Effect.flatMap((frame) => enqueue({ frame })),\n Effect.timeoutOrElse({\n duration: requestTimeoutMs,\n orElse: () =>\n Effect.fail(Transport.timeout(options.server, \"server-response admission\", requestTimeoutMs))\n })\n )\n }\n if (reply._tag === \"Malformed\") {\n return yield* Effect.fail(\n Limits.protocolError(\n options.server,\n `MCP server \"${options.server}\" sent a malformed JSON-RPC reply: ${reply.reason}`\n )\n )\n }\n if (reply._tag === \"UncorrelatedError\") {\n diagnostic(\"remote-error\", { code: reply.code, message: reply.message, data: reply.data })\n return\n }\n const pending = yield* Ref.modify(state, (current) => {\n if (current._tag === \"Closed\") return [Option.none<Pending>(), current] as const\n return [\n HashMap.get(current.pending, reply.id),\n { ...current, pending: HashMap.remove(current.pending, reply.id) }\n ] as const\n })\n if (Option.isNone(pending)) return\n if (reply._tag === \"Error\") {\n diagnostic(\"remote-error\", { code: reply.code, message: reply.message, data: reply.data })\n yield* Deferred.fail(\n pending.value.deferred,\n Transport.replyError(options.server, pending.value.method, reply)\n )\n } else {\n yield* Deferred.succeed(pending.value.deferred, reply.result)\n }\n })\n ),\n Effect.matchEffect({\n onFailure: (error) =>\n closeWith(\n error instanceof McpError ? error : Transport.closed(options.server, \"stdout failed\")\n ),\n // A clean EOF is still a closed MCP connection. Node reports an\n // ordinary child exit by ending stdout successfully.\n onSuccess: () => closeWith(Transport.closed(options.server, \"stdout closed\"))\n }),\n Effect.forkScoped\n )\n\n // Some process implementations expose exit before stdout observes EOF.\n // Treat either signal as the same terminal transition.\n yield* handle.exitCode.pipe(\n Effect.flatMap((exitCode) => closeWith(Transport.closed(options.server, `exited with code ${exitCode}`))),\n Effect.catch(() => closeWith(Transport.closed(options.server, \"process exited\"))),\n Effect.forkScoped\n )\n\n // Finalizers run in reverse order. Record scope closure before interrupting\n // the I/O fibers, whose cleanup must not replace it with \"stdin closed\".\n // Scope closure also tears down the child, so no cancellation is needed.\n yield* Effect.addFinalizer(() => closeWith(Transport.closed(options.server, \"connection scope closed\"), false))\n\n const takePending = (id: number): Effect.Effect<boolean> =>\n Ref.modify(state, (current) => {\n if (current._tag === \"Closed\") return [false, current]\n const present = HashMap.has(current.pending, id)\n return [present, { ...current, pending: HashMap.remove(current.pending, id) }]\n })\n\n const request = (\n method: string,\n params?: unknown,\n timeoutMs = requestTimeoutMs\n ): Effect.Effect<unknown, McpError> =>\n Effect.gen(function*() {\n if (!Limits.isPositiveInteger(timeoutMs)) {\n return yield* Effect.fail(\n Limits.protocolError(options.server, \"MCP request timeout must be a positive integer\")\n )\n }\n const id = yield* Ref.updateAndGet(nextId, (n) => n + 1)\n const deferred = yield* Deferred.make<unknown, McpError>()\n const frame = yield* frameOf(method, { jsonrpc: \"2.0\", id, method, params })\n const requestState = { id, cancelled: false, dispatched: false }\n return yield* Effect.gen(function*() {\n const registration = yield* Ref.modify(state, (current) =>\n current._tag === \"Closed\"\n ? [current.error, current] as const\n : [undefined, {\n ...current,\n pending: HashMap.set(current.pending, id, { deferred, method })\n }] as const)\n if (registration !== undefined) return yield* Effect.flatMap(Deferred.await(registration), Effect.fail)\n yield* enqueue({ frame, request: requestState })\n return yield* Deferred.await(deferred)\n }).pipe(\n Effect.ensuring(Effect.gen(function*() {\n requestState.cancelled = true\n const pending = yield* takePending(id)\n if (!requestState.dispatched || !pending || method === \"initialize\") return\n yield* frameOf(\"notifications/cancelled\", {\n jsonrpc: \"2.0\",\n method: \"notifications/cancelled\",\n params: { requestId: requestState.id, reason: Transport.cancellationReason }\n }).pipe(\n // Best-effort cancellation cannot delay the deadline it reports.\n Effect.flatMap((frame) => Effect.sync(() => Queue.offerUnsafe(outbound, { frame }))),\n Effect.ignore\n )\n })),\n Effect.timeoutOrElse({\n duration: timeoutMs,\n orElse: () => Effect.flatMap(withStderr(Transport.timeout(options.server, method, timeoutMs)), Effect.fail)\n })\n )\n })\n\n const notify = (\n method: string,\n params?: unknown,\n timeoutMs = requestTimeoutMs\n ): Effect.Effect<void, McpError> => {\n if (!Limits.isPositiveInteger(timeoutMs)) {\n return Effect.fail(Limits.protocolError(options.server, \"MCP notification timeout must be a positive integer\"))\n }\n return Effect.gen(function*() {\n const current = yield* Ref.get(state)\n if (current._tag === \"Closed\") return yield* Effect.flatMap(Deferred.await(current.error), Effect.fail)\n const frame = yield* frameOf(method, { jsonrpc: \"2.0\", method, params })\n yield* enqueue({ frame })\n }).pipe(\n Effect.timeoutOrElse({\n duration: timeoutMs,\n orElse: () => Effect.flatMap(withStderr(Transport.timeout(options.server, method, timeoutMs)), Effect.fail)\n })\n )\n }\n\n return { request, notify }\n })\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["frame"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The request/notify surface both MCP transports provide, and the failure
|
|
3
|
+
* wording they share. {@link McpClient} speaks to this interface only; stdio
|
|
4
|
+
* and Streamable HTTP differ in how a frame travels, not in what it means.
|
|
5
|
+
*
|
|
6
|
+
* @since 1.0.0-rc.1
|
|
7
|
+
*/
|
|
8
|
+
import { Effect, Stream } from "effect";
|
|
9
|
+
import { McpError } from "../McpError.ts";
|
|
10
|
+
import type * as Rpc from "./Rpc.ts";
|
|
11
|
+
/**
|
|
12
|
+
* One live connection to an MCP server.
|
|
13
|
+
*
|
|
14
|
+
* @category models
|
|
15
|
+
* @since 1.0.0-rc.0
|
|
16
|
+
*/
|
|
17
|
+
export interface Transport {
|
|
18
|
+
/** Sends a request and resolves with its `result`, or fails with the server's `error`. */
|
|
19
|
+
readonly request: (method: string, params?: unknown, timeoutMs?: number) => Effect.Effect<unknown, McpError>;
|
|
20
|
+
/** Sends a notification, bounding delivery by the optional positive-integer deadline. */
|
|
21
|
+
readonly notify: (method: string, params?: unknown, timeoutMs?: number) => Effect.Effect<void, McpError>;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Default request deadline.
|
|
25
|
+
*
|
|
26
|
+
* @category constants
|
|
27
|
+
* @since 1.0.0-rc.0
|
|
28
|
+
*/
|
|
29
|
+
export declare const defaultRequestTimeoutMs = 120000;
|
|
30
|
+
/**
|
|
31
|
+
* Default maximum inbound JSON-RPC frame size (one MiB).
|
|
32
|
+
*
|
|
33
|
+
* @category constants
|
|
34
|
+
* @since 1.0.0-rc.0
|
|
35
|
+
*/
|
|
36
|
+
export declare const defaultMaxFrameBytes: number;
|
|
37
|
+
/**
|
|
38
|
+
* Default maximum outbound JSON-RPC frame size (one MiB).
|
|
39
|
+
*
|
|
40
|
+
* @category constants
|
|
41
|
+
* @since 1.0.0-rc.0
|
|
42
|
+
*/
|
|
43
|
+
export declare const defaultMaxOutboundFrameBytes: number;
|
|
44
|
+
/**
|
|
45
|
+
* The reason sent with a best-effort `notifications/cancelled`.
|
|
46
|
+
*
|
|
47
|
+
* @category constants
|
|
48
|
+
* @since 1.0.0-rc.1
|
|
49
|
+
*/
|
|
50
|
+
export declare const cancellationReason = "request no longer awaited";
|
|
51
|
+
/**
|
|
52
|
+
* A `connection_closed` failure naming the server.
|
|
53
|
+
*
|
|
54
|
+
* @category errors
|
|
55
|
+
* @since 1.0.0-rc.1
|
|
56
|
+
*/
|
|
57
|
+
export declare const closed: (server: string, reason: string) => McpError;
|
|
58
|
+
/**
|
|
59
|
+
* A `timeout` failure naming the server, method and deadline.
|
|
60
|
+
*
|
|
61
|
+
* @category errors
|
|
62
|
+
* @since 1.0.0-rc.1
|
|
63
|
+
*/
|
|
64
|
+
export declare const timeout: (server: string, method: string, timeoutMs: number) => McpError;
|
|
65
|
+
/**
|
|
66
|
+
* The model-facing failure for a correlated JSON-RPC error reply. Remote text
|
|
67
|
+
* is withheld; the caller reports it to Diagnostics.
|
|
68
|
+
*
|
|
69
|
+
* @category errors
|
|
70
|
+
* @since 1.0.0-rc.1
|
|
71
|
+
*/
|
|
72
|
+
export declare const replyError: (server: string, method: string, reply: Extract<Rpc.Reply, {
|
|
73
|
+
readonly _tag: "Error";
|
|
74
|
+
}>) => McpError;
|
|
75
|
+
/**
|
|
76
|
+
* Splits a byte stream into lines in linear time, retaining one bounded
|
|
77
|
+
* partial line. Lines end at LF, with a CR before the LF dropped; with
|
|
78
|
+
* `crTerminates`, as server-sent events require, a lone CR also ends a line.
|
|
79
|
+
* Blank lines are kept: server-sent events use them as delimiters.
|
|
80
|
+
*
|
|
81
|
+
* @category constructors
|
|
82
|
+
* @since 1.0.0-rc.1
|
|
83
|
+
*/
|
|
84
|
+
export declare const lines: <E>(server: string, maxLineBytes: number, stream: Stream.Stream<Uint8Array, E>, options?: {
|
|
85
|
+
readonly crTerminates?: boolean;
|
|
86
|
+
}) => Stream.Stream<string, E | McpError>;
|
|
87
|
+
//# sourceMappingURL=Transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Transport.d.ts","sourceRoot":"","sources":["../../../src/internal/Transport.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAEzC,OAAO,KAAK,KAAK,GAAG,MAAM,UAAU,CAAA;AAEpC;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACxB,0FAA0F;IAC1F,QAAQ,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,SAAS,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAA;IAC5G,yFAAyF;IACzF,QAAQ,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,SAAS,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;CACzG;AAED;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,SAAU,CAAA;AAE9C;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,QAAc,CAAA;AAE/C;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,QAAc,CAAA;AAEvD;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,8BAA8B,CAAA;AAE7D;;;;;GAKG;AACH,eAAO,MAAM,MAAM,GAAI,QAAQ,MAAM,EAAE,QAAQ,MAAM,KAAG,QAC0C,CAAA;AAElG;;;;;GAKG;AACH,eAAO,MAAM,OAAO,GAAI,QAAQ,MAAM,EAAE,QAAQ,MAAM,EAAE,WAAW,MAAM,KAAG,QAKxE,CAAA;AAEJ;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,GACrB,QAAQ,MAAM,EACd,QAAQ,MAAM,EACd,OAAO,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC,KACpD,QAaF,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,KAAK,GAAI,CAAC,EACrB,QAAQ,MAAM,EACd,cAAc,MAAM,EACpB,QAAQ,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,EACpC,UAAS;IAAE,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAA;CAAO,KAChD,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,QAAQ,CAyDpC,CAAA"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var Transport_exports = {};
|
|
30
|
+
__export(Transport_exports, {
|
|
31
|
+
cancellationReason: () => cancellationReason,
|
|
32
|
+
closed: () => closed,
|
|
33
|
+
defaultMaxFrameBytes: () => defaultMaxFrameBytes,
|
|
34
|
+
defaultMaxOutboundFrameBytes: () => defaultMaxOutboundFrameBytes,
|
|
35
|
+
defaultRequestTimeoutMs: () => defaultRequestTimeoutMs,
|
|
36
|
+
lines: () => lines,
|
|
37
|
+
replyError: () => replyError,
|
|
38
|
+
timeout: () => timeout
|
|
39
|
+
});
|
|
40
|
+
module.exports = __toCommonJS(Transport_exports);
|
|
41
|
+
var import_effect = require("effect");
|
|
42
|
+
var import_McpError = require("../McpError.js");
|
|
43
|
+
var Limits = __toESM(require("./Limits.js"), 1);
|
|
44
|
+
const defaultRequestTimeoutMs = 12e4;
|
|
45
|
+
const defaultMaxFrameBytes = 1024 * 1024;
|
|
46
|
+
const defaultMaxOutboundFrameBytes = 1024 * 1024;
|
|
47
|
+
const cancellationReason = "request no longer awaited";
|
|
48
|
+
const closed = (server, reason) => new import_McpError.McpError({ code: "connection_closed", message: `MCP server "${server}" ${reason}`, server });
|
|
49
|
+
const timeout = (server, method, timeoutMs) => new import_McpError.McpError({
|
|
50
|
+
code: "timeout",
|
|
51
|
+
message: `MCP server "${server}" did not answer ${method} within ${timeoutMs}ms`,
|
|
52
|
+
server
|
|
53
|
+
});
|
|
54
|
+
const replyError = (server, method, reply) => {
|
|
55
|
+
const remoteUnknownTool = (reply.code === -32601 || reply.code === -32602) && /\btool\b/i.test(reply.message) && /\b(?:unknown|unrecognized|no such|not found)\b/i.test(reply.message);
|
|
56
|
+
return new import_McpError.McpError({
|
|
57
|
+
code: method === "tools/call" ? remoteUnknownTool ? "tool_not_found" : "tool_failed" : "protocol_error",
|
|
58
|
+
message: `MCP server "${server}" failed ${method} (${reply.code}); remote details withheld`,
|
|
59
|
+
server
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
const lines = (server, maxLineBytes, stream, options = {}) => {
|
|
63
|
+
const crTerminates = options.crTerminates === true;
|
|
64
|
+
const decoder = new TextDecoder();
|
|
65
|
+
const decode = (partial) => {
|
|
66
|
+
const joined = new Uint8Array(partial.bytes);
|
|
67
|
+
let offset = 0;
|
|
68
|
+
for (const piece of partial.pieces) {
|
|
69
|
+
joined.set(piece, offset);
|
|
70
|
+
offset += piece.byteLength;
|
|
71
|
+
}
|
|
72
|
+
const end = joined[partial.bytes - 1] === 13 ? partial.bytes - 1 : partial.bytes;
|
|
73
|
+
return decoder.decode(joined.subarray(0, end));
|
|
74
|
+
};
|
|
75
|
+
const terminator = (chunk, from) => {
|
|
76
|
+
if (!crTerminates) return chunk.indexOf(10, from);
|
|
77
|
+
for (let index = from; index < chunk.byteLength; index += 1) {
|
|
78
|
+
if (chunk[index] === 10 || chunk[index] === 13) return index;
|
|
79
|
+
}
|
|
80
|
+
return -1;
|
|
81
|
+
};
|
|
82
|
+
const tooLong = () => import_effect.Effect.fail(Limits.protocolError(server, `MCP frame exceeded ${maxLineBytes} bytes`));
|
|
83
|
+
return stream.pipe(
|
|
84
|
+
import_effect.Stream.mapAccumEffect(
|
|
85
|
+
() => ({ pieces: [], bytes: 0, skipLf: false }),
|
|
86
|
+
(partial, chunk) => {
|
|
87
|
+
const complete = [];
|
|
88
|
+
const append = (piece) => {
|
|
89
|
+
if (piece.byteLength === 0) return true;
|
|
90
|
+
const bytes = partial.bytes + piece.byteLength;
|
|
91
|
+
const contentBytes = bytes - (piece[piece.byteLength - 1] === 13 ? 1 : 0);
|
|
92
|
+
if (contentBytes > maxLineBytes) return false;
|
|
93
|
+
partial.pieces.push(piece);
|
|
94
|
+
partial.bytes = bytes;
|
|
95
|
+
return true;
|
|
96
|
+
};
|
|
97
|
+
let start = partial.skipLf && chunk[0] === 10 ? 1 : 0;
|
|
98
|
+
partial.skipLf = false;
|
|
99
|
+
for (let end = terminator(chunk, start); end !== -1; end = terminator(chunk, start)) {
|
|
100
|
+
if (!append(chunk.subarray(start, end))) return tooLong();
|
|
101
|
+
complete.push(decode(partial));
|
|
102
|
+
partial = { pieces: [], bytes: 0, skipLf: false };
|
|
103
|
+
start = end + 1;
|
|
104
|
+
if (chunk[end] === 13) {
|
|
105
|
+
if (start === chunk.byteLength) partial.skipLf = true;
|
|
106
|
+
else if (chunk[start] === 10) start += 1;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (!append(chunk.subarray(start))) return tooLong();
|
|
110
|
+
return import_effect.Effect.succeed([partial, complete]);
|
|
111
|
+
},
|
|
112
|
+
{ onHalt: (partial) => partial.bytes === 0 ? [] : [decode(partial)] }
|
|
113
|
+
)
|
|
114
|
+
);
|
|
115
|
+
};
|
|
116
|
+
//# sourceMappingURL=Transport.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/internal/Transport.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * The request/notify surface both MCP transports provide, and the failure\n * wording they share. {@link McpClient} speaks to this interface only; stdio\n * and Streamable HTTP differ in how a frame travels, not in what it means.\n *\n * @since 1.0.0-rc.1\n */\n\nimport { Effect, Stream } from \"effect\"\nimport { McpError } from \"../McpError.ts\"\nimport * as Limits from \"./Limits.ts\"\nimport type * as Rpc from \"./Rpc.ts\"\n\n/**\n * One live connection to an MCP server.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport interface Transport {\n /** Sends a request and resolves with its `result`, or fails with the server's `error`. */\n readonly request: (method: string, params?: unknown, timeoutMs?: number) => Effect.Effect<unknown, McpError>\n /** Sends a notification, bounding delivery by the optional positive-integer deadline. */\n readonly notify: (method: string, params?: unknown, timeoutMs?: number) => Effect.Effect<void, McpError>\n}\n\n/**\n * Default request deadline.\n *\n * @category constants\n * @since 1.0.0-rc.0\n */\nexport const defaultRequestTimeoutMs = 120_000\n\n/**\n * Default maximum inbound JSON-RPC frame size (one MiB).\n *\n * @category constants\n * @since 1.0.0-rc.0\n */\nexport const defaultMaxFrameBytes = 1024 * 1024\n\n/**\n * Default maximum outbound JSON-RPC frame size (one MiB).\n *\n * @category constants\n * @since 1.0.0-rc.0\n */\nexport const defaultMaxOutboundFrameBytes = 1024 * 1024\n\n/**\n * The reason sent with a best-effort `notifications/cancelled`.\n *\n * @category constants\n * @since 1.0.0-rc.1\n */\nexport const cancellationReason = \"request no longer awaited\"\n\n/**\n * A `connection_closed` failure naming the server.\n *\n * @category errors\n * @since 1.0.0-rc.1\n */\nexport const closed = (server: string, reason: string): McpError =>\n new McpError({ code: \"connection_closed\", message: `MCP server \"${server}\" ${reason}`, server })\n\n/**\n * A `timeout` failure naming the server, method and deadline.\n *\n * @category errors\n * @since 1.0.0-rc.1\n */\nexport const timeout = (server: string, method: string, timeoutMs: number): McpError =>\n new McpError({\n code: \"timeout\",\n message: `MCP server \"${server}\" did not answer ${method} within ${timeoutMs}ms`,\n server\n })\n\n/**\n * The model-facing failure for a correlated JSON-RPC error reply. Remote text\n * is withheld; the caller reports it to Diagnostics.\n *\n * @category errors\n * @since 1.0.0-rc.1\n */\nexport const replyError = (\n server: string,\n method: string,\n reply: Extract<Rpc.Reply, { readonly _tag: \"Error\" }>\n): McpError => {\n // Servers do not standardize unknown-tool prose, so this heuristic stays\n // limited to the two MCP error codes and an explicit tool plus absence phrase.\n const remoteUnknownTool = (reply.code === -32_601 || reply.code === -32_602) &&\n /\\btool\\b/i.test(reply.message) &&\n /\\b(?:unknown|unrecognized|no such|not found)\\b/i.test(reply.message)\n return new McpError({\n code: method === \"tools/call\"\n ? remoteUnknownTool ? \"tool_not_found\" : \"tool_failed\"\n : \"protocol_error\",\n message: `MCP server \"${server}\" failed ${method} (${reply.code}); remote details withheld`,\n server\n })\n}\n\n/**\n * Splits a byte stream into lines in linear time, retaining one bounded\n * partial line. Lines end at LF, with a CR before the LF dropped; with\n * `crTerminates`, as server-sent events require, a lone CR also ends a line.\n * Blank lines are kept: server-sent events use them as delimiters.\n *\n * @category constructors\n * @since 1.0.0-rc.1\n */\nexport const lines = <E>(\n server: string,\n maxLineBytes: number,\n stream: Stream.Stream<Uint8Array, E>,\n options: { readonly crTerminates?: boolean } = {}\n): Stream.Stream<string, E | McpError> => {\n type PartialLine = { pieces: Array<Uint8Array>; bytes: number; skipLf: boolean }\n const crTerminates = options.crTerminates === true\n const decoder = new TextDecoder()\n const decode = (partial: PartialLine): string => {\n const joined = new Uint8Array(partial.bytes)\n let offset = 0\n for (const piece of partial.pieces) {\n joined.set(piece, offset)\n offset += piece.byteLength\n }\n const end = joined[partial.bytes - 1] === 0x0d ? partial.bytes - 1 : partial.bytes\n return decoder.decode(joined.subarray(0, end))\n }\n const terminator = (chunk: Uint8Array, from: number): number => {\n if (!crTerminates) return chunk.indexOf(0x0a, from)\n for (let index = from; index < chunk.byteLength; index += 1) {\n if (chunk[index] === 0x0a || chunk[index] === 0x0d) return index\n }\n return -1\n }\n const tooLong = () => Effect.fail(Limits.protocolError(server, `MCP frame exceeded ${maxLineBytes} bytes`))\n return stream.pipe(\n Stream.mapAccumEffect(\n (): PartialLine => ({ pieces: [], bytes: 0, skipLf: false }),\n (partial, chunk) => {\n const complete: Array<string> = []\n const append = (piece: Uint8Array): boolean => {\n if (piece.byteLength === 0) return true\n const bytes = partial.bytes + piece.byteLength\n // A final CR may be the first half of CRLF. Allow that one byte\n // beyond the cap, but count it if more frame content follows.\n const contentBytes = bytes - (piece[piece.byteLength - 1] === 0x0d ? 1 : 0)\n if (contentBytes > maxLineBytes) return false\n partial.pieces.push(piece)\n partial.bytes = bytes\n return true\n }\n // The LF of a CRLF split across chunks ends nothing new.\n let start = partial.skipLf && chunk[0] === 0x0a ? 1 : 0\n partial.skipLf = false\n for (let end = terminator(chunk, start); end !== -1; end = terminator(chunk, start)) {\n if (!append(chunk.subarray(start, end))) return tooLong()\n complete.push(decode(partial))\n partial = { pieces: [], bytes: 0, skipLf: false }\n start = end + 1\n if (chunk[end] === 0x0d) {\n if (start === chunk.byteLength) partial.skipLf = true\n else if (chunk[start] === 0x0a) start += 1\n }\n }\n if (!append(chunk.subarray(start))) return tooLong()\n return Effect.succeed([partial, complete] as const)\n },\n { onHalt: (partial) => partial.bytes === 0 ? [] : [decode(partial)] }\n )\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"commonjs"}
|