toolcraft 0.0.199 → 0.0.201
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/composition.json +3 -3
- package/dist/composition.json +3 -3
- package/node_modules/tiny-http-mcp-server/dist/composition.json +1 -1
- package/node_modules/tiny-http-mcp-server/dist/http-transport.js +1 -1
- package/node_modules/tiny-http-mcp-server/dist/parse-body.js +12 -5
- package/node_modules/tiny-http-mcp-server/package.json +1 -1
- package/node_modules/tiny-stdio-mcp-server/dist/composition.json +1 -1
- package/node_modules/tiny-stdio-mcp-server/dist/server.js +55 -19
- package/node_modules/tiny-stdio-mcp-server/dist/stdio-output.d.ts +24 -0
- package/node_modules/tiny-stdio-mcp-server/dist/stdio-output.js +131 -0
- package/node_modules/tiny-stdio-mcp-server/dist/types.d.ts +4 -0
- package/node_modules/toolcraft-schema/package.json +1 -1
- package/package.json +2 -2
package/composition.json
CHANGED
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
},
|
|
99
99
|
{
|
|
100
100
|
"name": "tiny-http-mcp-server",
|
|
101
|
-
"version": "0.1.
|
|
101
|
+
"version": "0.1.15",
|
|
102
102
|
"license": "MIT"
|
|
103
103
|
},
|
|
104
104
|
{
|
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
},
|
|
114
114
|
{
|
|
115
115
|
"name": "toolcraft",
|
|
116
|
-
"version": "0.0.
|
|
116
|
+
"version": "0.0.201",
|
|
117
117
|
"license": "MIT"
|
|
118
118
|
},
|
|
119
119
|
{
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
},
|
|
124
124
|
{
|
|
125
125
|
"name": "toolcraft-schema",
|
|
126
|
-
"version": "0.0.
|
|
126
|
+
"version": "0.0.201",
|
|
127
127
|
"license": "MIT"
|
|
128
128
|
},
|
|
129
129
|
{
|
package/dist/composition.json
CHANGED
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
},
|
|
99
99
|
{
|
|
100
100
|
"name": "tiny-http-mcp-server",
|
|
101
|
-
"version": "0.1.
|
|
101
|
+
"version": "0.1.15",
|
|
102
102
|
"license": "MIT"
|
|
103
103
|
},
|
|
104
104
|
{
|
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
},
|
|
114
114
|
{
|
|
115
115
|
"name": "toolcraft",
|
|
116
|
-
"version": "0.0.
|
|
116
|
+
"version": "0.0.201",
|
|
117
117
|
"license": "MIT"
|
|
118
118
|
},
|
|
119
119
|
{
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
},
|
|
124
124
|
{
|
|
125
125
|
"name": "toolcraft-schema",
|
|
126
|
-
"version": "0.0.
|
|
126
|
+
"version": "0.0.201",
|
|
127
127
|
"license": "MIT"
|
|
128
128
|
},
|
|
129
129
|
{
|
|
@@ -754,7 +754,7 @@ export class StreamableHttpTransport {
|
|
|
754
754
|
const history = this.sseEventHistory.get(sessionId) ?? [];
|
|
755
755
|
for (const event of history) {
|
|
756
756
|
if (event.id > lastEventId && !res.writableEnded) {
|
|
757
|
-
|
|
757
|
+
this.writeToLiveGetStream(res, formatSseEvent({ id: String(event.id), data: event.data }));
|
|
758
758
|
}
|
|
759
759
|
}
|
|
760
760
|
}
|
|
@@ -64,12 +64,19 @@ async function readStreamBody(req, maxBytes) {
|
|
|
64
64
|
return Buffer.concat(chunks).toString("utf8");
|
|
65
65
|
}
|
|
66
66
|
async function readRawBody(req, preParsed, options) {
|
|
67
|
-
if (preParsed !== undefined) {
|
|
68
|
-
return preParsed;
|
|
69
|
-
}
|
|
70
67
|
const reqWithBody = req;
|
|
71
|
-
|
|
72
|
-
|
|
68
|
+
const body = preParsed !== undefined ? preParsed : reqWithBody.body;
|
|
69
|
+
if (body !== undefined) {
|
|
70
|
+
if (options.maxBytes !== undefined) {
|
|
71
|
+
const serialized = JSON.stringify(body);
|
|
72
|
+
if (serialized === undefined) {
|
|
73
|
+
throw new Error("Invalid Request");
|
|
74
|
+
}
|
|
75
|
+
if (Buffer.byteLength(serialized, "utf8") > options.maxBytes) {
|
|
76
|
+
throw new Error("Payload too large");
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return body;
|
|
73
80
|
}
|
|
74
81
|
const raw = await readStreamBody(req, options.maxBytes);
|
|
75
82
|
try {
|
|
@@ -5,6 +5,7 @@ import { parseMessage, formatSuccessResponse, formatErrorResponse } from "./json
|
|
|
5
5
|
import { parseUriTemplate } from "./uri-template.js";
|
|
6
6
|
import { toContentBlocks } from "./content/convert.js";
|
|
7
7
|
import { ToolCallAdmission } from "./tool-call-admission.js";
|
|
8
|
+
import { StdioOutput } from "./stdio-output.js";
|
|
8
9
|
const PROTOCOL_VERSION = "2025-11-25";
|
|
9
10
|
const SUPPORTED_PROTOCOL_VERSIONS = new Set(["2025-03-26", "2025-06-18", PROTOCOL_VERSION]);
|
|
10
11
|
function compileToolSchema(schema) {
|
|
@@ -23,9 +24,13 @@ export function createServer(options) {
|
|
|
23
24
|
}
|
|
24
25
|
const maxConcurrentToolCalls = options.maxConcurrentToolCalls ?? 4;
|
|
25
26
|
const maxQueuedToolCalls = options.maxQueuedToolCalls ?? 64;
|
|
27
|
+
const maxStdioOutputBytes = options.maxStdioOutputBytes ?? 1024 * 1024;
|
|
28
|
+
const maxPendingStdioMessages = options.maxPendingStdioMessages ?? 128;
|
|
26
29
|
for (const [name, value, minimum] of [
|
|
27
30
|
["maxConcurrentToolCalls", maxConcurrentToolCalls, 1],
|
|
28
|
-
["maxQueuedToolCalls", maxQueuedToolCalls, 0]
|
|
31
|
+
["maxQueuedToolCalls", maxQueuedToolCalls, 0],
|
|
32
|
+
["maxStdioOutputBytes", maxStdioOutputBytes, 1],
|
|
33
|
+
["maxPendingStdioMessages", maxPendingStdioMessages, 1]
|
|
29
34
|
]) {
|
|
30
35
|
if (!Number.isSafeInteger(value) || value < minimum) {
|
|
31
36
|
throw new Error(`${name} must be a safe integer greater than or equal to ${minimum}.`);
|
|
@@ -361,7 +366,7 @@ export function createServer(options) {
|
|
|
361
366
|
const processLine = async (line, write, messageHandler) => {
|
|
362
367
|
const parsed = parseMessage(line);
|
|
363
368
|
if (!parsed.success) {
|
|
364
|
-
write(formatErrorResponse(parsed.id, parsed.error) + "\n");
|
|
369
|
+
await write(formatErrorResponse(parsed.id, parsed.error) + "\n");
|
|
365
370
|
return;
|
|
366
371
|
}
|
|
367
372
|
const { request, isNotification } = parsed;
|
|
@@ -370,7 +375,7 @@ export function createServer(options) {
|
|
|
370
375
|
}
|
|
371
376
|
if (!isNotification && request.method === "notifications/initialized") {
|
|
372
377
|
const requestWithId = request;
|
|
373
|
-
write(formatErrorResponse(requestWithId.id, {
|
|
378
|
+
await write(formatErrorResponse(requestWithId.id, {
|
|
374
379
|
code: JSON_RPC_ERROR_CODES.INVALID_REQUEST,
|
|
375
380
|
message: "Invalid Request"
|
|
376
381
|
}) + "\n");
|
|
@@ -383,7 +388,7 @@ export function createServer(options) {
|
|
|
383
388
|
catch {
|
|
384
389
|
if (!isNotification) {
|
|
385
390
|
const requestWithId = request;
|
|
386
|
-
write(formatErrorResponse(requestWithId.id, {
|
|
391
|
+
await write(formatErrorResponse(requestWithId.id, {
|
|
387
392
|
code: JSON_RPC_ERROR_CODES.INTERNAL_ERROR,
|
|
388
393
|
message: "Internal error"
|
|
389
394
|
}) + "\n");
|
|
@@ -396,10 +401,10 @@ export function createServer(options) {
|
|
|
396
401
|
}
|
|
397
402
|
const requestWithId = request;
|
|
398
403
|
if (error) {
|
|
399
|
-
write(formatErrorResponse(requestWithId.id, error) + "\n");
|
|
404
|
+
await write(formatErrorResponse(requestWithId.id, error) + "\n");
|
|
400
405
|
}
|
|
401
406
|
else if (result !== undefined) {
|
|
402
|
-
write(formatSuccessResponse(requestWithId.id, result) + "\n");
|
|
407
|
+
await write(formatSuccessResponse(requestWithId.id, result) + "\n");
|
|
403
408
|
}
|
|
404
409
|
};
|
|
405
410
|
const broadcastNotification = async (method, params, canSend = () => true) => {
|
|
@@ -541,27 +546,58 @@ export function createServer(options) {
|
|
|
541
546
|
});
|
|
542
547
|
},
|
|
543
548
|
async connect(transport) {
|
|
544
|
-
return new Promise((resolve) => {
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
549
|
+
return new Promise((resolve, reject) => {
|
|
550
|
+
let inputClosed = false;
|
|
551
|
+
let settled = false;
|
|
552
|
+
const pendingMessages = new Set();
|
|
553
|
+
const output = new StdioOutput(transport.writable, maxStdioOutputBytes, fail, finish);
|
|
554
|
+
const listener = (notification) => output.write(`${JSON.stringify(notification)}\n`);
|
|
548
555
|
const session = server.createMessageSession(listener);
|
|
549
556
|
const rl = readline.createInterface({
|
|
550
557
|
input: transport.readable,
|
|
551
558
|
crlfDelay: Infinity
|
|
552
559
|
});
|
|
553
|
-
|
|
560
|
+
function fail(error) {
|
|
561
|
+
if (settled)
|
|
562
|
+
return;
|
|
563
|
+
settled = true;
|
|
564
|
+
session.close();
|
|
565
|
+
rl.close();
|
|
566
|
+
transport.readable.pause();
|
|
567
|
+
transport.readable.off("error", fail);
|
|
568
|
+
output.abort(error);
|
|
569
|
+
pendingMessages.clear();
|
|
570
|
+
reject(error);
|
|
571
|
+
}
|
|
572
|
+
function finish() {
|
|
573
|
+
if (settled || !inputClosed || pendingMessages.size > 0 || output.pending > 0)
|
|
574
|
+
return;
|
|
575
|
+
settled = true;
|
|
576
|
+
session.close();
|
|
577
|
+
transport.readable.off("error", fail);
|
|
578
|
+
output.close();
|
|
579
|
+
resolve();
|
|
580
|
+
}
|
|
581
|
+
transport.readable.on("error", fail);
|
|
582
|
+
rl.on("error", fail);
|
|
554
583
|
rl.on("line", (line) => {
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
584
|
+
if (settled)
|
|
585
|
+
return;
|
|
586
|
+
if (pendingMessages.size >= maxPendingStdioMessages) {
|
|
587
|
+
fail(new Error("Stdio pending message limit exceeded"));
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
const message = processLine(line, data => output.write(data), session.handleMessage);
|
|
591
|
+
if (!settled)
|
|
592
|
+
pendingMessages.add(message);
|
|
593
|
+
void message.then(() => {
|
|
558
594
|
pendingMessages.delete(message);
|
|
559
|
-
|
|
595
|
+
finish();
|
|
596
|
+
}, fail);
|
|
560
597
|
});
|
|
561
|
-
rl.on("close",
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
resolve();
|
|
598
|
+
rl.on("close", () => {
|
|
599
|
+
inputClosed = true;
|
|
600
|
+
finish();
|
|
565
601
|
});
|
|
566
602
|
});
|
|
567
603
|
},
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** One connection's admitted frames, including its submitted, unsettled write. */
|
|
2
|
+
export declare class StdioOutput {
|
|
3
|
+
private readonly writable;
|
|
4
|
+
private readonly maxBytes;
|
|
5
|
+
private readonly onFailure;
|
|
6
|
+
private readonly onIdle;
|
|
7
|
+
private readonly queue;
|
|
8
|
+
private active;
|
|
9
|
+
private bytes;
|
|
10
|
+
private stopped;
|
|
11
|
+
private failure;
|
|
12
|
+
private awaitingError;
|
|
13
|
+
constructor(writable: NodeJS.WritableStream, maxBytes: number, onFailure: (error: unknown) => void, onIdle: () => void);
|
|
14
|
+
get pending(): number;
|
|
15
|
+
write(data: string): Promise<void>;
|
|
16
|
+
abort(error: unknown): void;
|
|
17
|
+
close(): void;
|
|
18
|
+
private pump;
|
|
19
|
+
private advance;
|
|
20
|
+
private readonly onDrain;
|
|
21
|
+
private readonly onError;
|
|
22
|
+
private readonly onClose;
|
|
23
|
+
private detach;
|
|
24
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/** One connection's admitted frames, including its submitted, unsettled write. */
|
|
2
|
+
export class StdioOutput {
|
|
3
|
+
writable;
|
|
4
|
+
maxBytes;
|
|
5
|
+
onFailure;
|
|
6
|
+
onIdle;
|
|
7
|
+
queue = [];
|
|
8
|
+
active;
|
|
9
|
+
bytes = 0;
|
|
10
|
+
stopped = false;
|
|
11
|
+
failure;
|
|
12
|
+
awaitingError = false;
|
|
13
|
+
constructor(writable, maxBytes, onFailure, onIdle) {
|
|
14
|
+
this.writable = writable;
|
|
15
|
+
this.maxBytes = maxBytes;
|
|
16
|
+
this.onFailure = onFailure;
|
|
17
|
+
this.onIdle = onIdle;
|
|
18
|
+
writable.on("drain", this.onDrain);
|
|
19
|
+
writable.on("error", this.onError);
|
|
20
|
+
writable.on("close", this.onClose);
|
|
21
|
+
}
|
|
22
|
+
get pending() { return this.queue.length + (this.active === undefined ? 0 : 1); }
|
|
23
|
+
write(data) {
|
|
24
|
+
if (this.stopped)
|
|
25
|
+
return Promise.reject(this.failure);
|
|
26
|
+
const bytes = Buffer.byteLength(data);
|
|
27
|
+
if (bytes > this.maxBytes - this.bytes) {
|
|
28
|
+
const error = new Error("Stdio output byte limit exceeded");
|
|
29
|
+
this.abort(error);
|
|
30
|
+
return Promise.reject(error);
|
|
31
|
+
}
|
|
32
|
+
this.bytes += bytes;
|
|
33
|
+
const promise = new Promise((resolve, reject) => {
|
|
34
|
+
this.queue.push({ data, bytes, resolve, reject });
|
|
35
|
+
});
|
|
36
|
+
this.pump();
|
|
37
|
+
return promise;
|
|
38
|
+
}
|
|
39
|
+
abort(error) {
|
|
40
|
+
if (!this.stopped) {
|
|
41
|
+
this.stopped = true;
|
|
42
|
+
this.failure = error;
|
|
43
|
+
this.active?.frame.reject(error);
|
|
44
|
+
if (this.active?.completed)
|
|
45
|
+
this.active = undefined;
|
|
46
|
+
for (const frame of this.queue.splice(0))
|
|
47
|
+
frame.reject(error);
|
|
48
|
+
this.bytes = 0;
|
|
49
|
+
this.onFailure(error);
|
|
50
|
+
}
|
|
51
|
+
// A submitted write can still fail after connection settlement. Keep its
|
|
52
|
+
// observation until callback/error/close; never destroy a caller's stream.
|
|
53
|
+
if (this.active === undefined && !this.awaitingError)
|
|
54
|
+
this.detach();
|
|
55
|
+
}
|
|
56
|
+
close() {
|
|
57
|
+
this.stopped = true;
|
|
58
|
+
this.failure = new Error("Stdio output is closed");
|
|
59
|
+
this.detach();
|
|
60
|
+
}
|
|
61
|
+
pump() {
|
|
62
|
+
if (this.stopped || this.active !== undefined)
|
|
63
|
+
return;
|
|
64
|
+
const frame = this.queue.shift();
|
|
65
|
+
if (frame === undefined) {
|
|
66
|
+
this.onIdle();
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const active = { frame, completed: false, returned: false, drained: false, needsDrain: false };
|
|
70
|
+
this.active = active;
|
|
71
|
+
try {
|
|
72
|
+
const accepted = this.writable.write(frame.data, "utf8", (error) => {
|
|
73
|
+
if (error != null) {
|
|
74
|
+
this.awaitingError = true;
|
|
75
|
+
this.active = undefined;
|
|
76
|
+
frame.reject(error);
|
|
77
|
+
this.abort(error);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
active.completed = true;
|
|
81
|
+
if (this.stopped) {
|
|
82
|
+
this.active = undefined;
|
|
83
|
+
this.detach();
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
this.advance();
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
active.returned = true;
|
|
90
|
+
active.needsDrain = !accepted;
|
|
91
|
+
this.advance();
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
this.active = undefined;
|
|
95
|
+
frame.reject(error);
|
|
96
|
+
this.abort(error);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
advance() {
|
|
100
|
+
const active = this.active;
|
|
101
|
+
if (this.stopped || active === undefined || !active.returned || !active.completed ||
|
|
102
|
+
(active.needsDrain && !active.drained))
|
|
103
|
+
return;
|
|
104
|
+
this.active = undefined;
|
|
105
|
+
this.bytes -= active.frame.bytes;
|
|
106
|
+
active.frame.resolve();
|
|
107
|
+
this.pump();
|
|
108
|
+
}
|
|
109
|
+
onDrain = () => {
|
|
110
|
+
if (this.active !== undefined)
|
|
111
|
+
this.active.drained = true;
|
|
112
|
+
this.advance();
|
|
113
|
+
};
|
|
114
|
+
onError = (error) => {
|
|
115
|
+
this.awaitingError = false;
|
|
116
|
+
this.abort(error);
|
|
117
|
+
this.active = undefined;
|
|
118
|
+
this.detach();
|
|
119
|
+
};
|
|
120
|
+
onClose = () => {
|
|
121
|
+
this.awaitingError = false;
|
|
122
|
+
this.abort(new Error("Stdio output closed before connection completion"));
|
|
123
|
+
this.active = undefined;
|
|
124
|
+
this.detach();
|
|
125
|
+
};
|
|
126
|
+
detach() {
|
|
127
|
+
this.writable.off("drain", this.onDrain);
|
|
128
|
+
this.writable.off("error", this.onError);
|
|
129
|
+
this.writable.off("close", this.onClose);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
@@ -213,6 +213,10 @@ export interface ServerOptions {
|
|
|
213
213
|
maxConcurrentToolCalls?: number;
|
|
214
214
|
/** Maximum tool calls waiting for capacity; defaults to 64. Zero disables waiting. */
|
|
215
215
|
maxQueuedToolCalls?: number;
|
|
216
|
+
/** Per-stdio-connection UTF-8 bytes queued or submitted but unsettled; defaults to 1 MiB. */
|
|
217
|
+
maxStdioOutputBytes?: number;
|
|
218
|
+
/** Per-stdio-connection messages awaiting handler/output settlement; defaults to 128. */
|
|
219
|
+
maxPendingStdioMessages?: number;
|
|
216
220
|
validateToolArguments?: boolean;
|
|
217
221
|
supportNotifications?: boolean;
|
|
218
222
|
supportResourceSubscriptions?: boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "toolcraft",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.201",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -158,7 +158,7 @@
|
|
|
158
158
|
"yaml"
|
|
159
159
|
],
|
|
160
160
|
"optionalDependencies": {
|
|
161
|
-
"toolcraft-schema": "0.0.
|
|
161
|
+
"toolcraft-schema": "0.0.201",
|
|
162
162
|
"toolcraft-design": "*",
|
|
163
163
|
"@poe-code/frontmatter": "*",
|
|
164
164
|
"@poe-code/agent-mcp-config": "*",
|