toolcraft 0.0.198 → 0.0.199

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 CHANGED
@@ -98,7 +98,7 @@
98
98
  },
99
99
  {
100
100
  "name": "tiny-http-mcp-server",
101
- "version": "0.1.12",
101
+ "version": "0.1.13",
102
102
  "license": "MIT"
103
103
  },
104
104
  {
@@ -113,7 +113,7 @@
113
113
  },
114
114
  {
115
115
  "name": "toolcraft",
116
- "version": "0.0.198",
116
+ "version": "0.0.199",
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.198",
126
+ "version": "0.0.199",
127
127
  "license": "MIT"
128
128
  },
129
129
  {
@@ -98,7 +98,7 @@
98
98
  },
99
99
  {
100
100
  "name": "tiny-http-mcp-server",
101
- "version": "0.1.12",
101
+ "version": "0.1.13",
102
102
  "license": "MIT"
103
103
  },
104
104
  {
@@ -113,7 +113,7 @@
113
113
  },
114
114
  {
115
115
  "name": "toolcraft",
116
- "version": "0.0.198",
116
+ "version": "0.0.199",
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.198",
126
+ "version": "0.0.199",
127
127
  "license": "MIT"
128
128
  },
129
129
  {
@@ -436,7 +436,7 @@ Returned `HttpServer` instances support:
436
436
  | `maxStreamBufferBytes` | `number` | `1048576` | End a GET SSE stream before a live write when its buffered bytes exceed this limit. |
437
437
  | `maxSseEventHistory` | `number` | `100` | Number of server-sent events retained for `Last-Event-ID` replay. |
438
438
  | `sseKeepAliveMs` | `number` | `30000` | GET SSE keepalive interval in milliseconds. Set to `0` to disable keepalive comments. |
439
- | `maxConcurrentToolCalls` | `number` | unlimited | Maximum concurrent tool calls across sessions. |
439
+ | `maxConcurrentToolCalls` | `number` | `4` | Maximum concurrent tool calls across sessions. |
440
440
  | `sessionStore` | `SessionStore` | in-memory store | Pluggable session-record storage; SSE streams and replay history remain instance-local. |
441
441
  | `requestIdGenerator` | `() => string` | incrementing ids | Generates request ids when `X-Request-Id` is absent. |
442
442
  | `observability` | `HttpObservabilityOptions` | none | Emits request, auth, session, stream, and tool lifecycle events. |
@@ -622,7 +622,7 @@ tiny-http-mcp-server [options]
622
622
  | `--max-stream-buffer-bytes <bytes>` | `1048576` | End a GET SSE stream before a live notification or keepalive write when buffered bytes exceed this limit. |
623
623
  | `--max-sse-event-history <count>` | `100` | Number of SSE events retained for `Last-Event-ID` replay. |
624
624
  | `--sse-keep-alive-ms <ms>` | `30000` | GET SSE keepalive interval in milliseconds. Set to `0` to disable keepalive comments. |
625
- | `--max-concurrent-tool-calls <count>` | unlimited | Maximum concurrent tool calls across sessions. |
625
+ | `--max-concurrent-tool-calls <count>` | `4` | Maximum concurrent tool calls across sessions. |
626
626
  | `--trusted-proxy` | off | Trust `X-Forwarded-Proto` and `X-Forwarded-Host` for metadata challenge URLs. |
627
627
  | `--request-timeout-ms <ms>` | Node default | Node HTTP request timeout. |
628
628
  | `--headers-timeout-ms <ms>` | Node default | Node HTTP headers timeout. |
@@ -44,7 +44,9 @@ const HELP_TEXT = [
44
44
  " --sse-keep-alive-ms <ms>",
45
45
  " GET SSE keepalive interval (default: 30000; 0 disables)",
46
46
  " --max-concurrent-tool-calls <count>",
47
- " Maximum concurrent tool calls across sessions",
47
+ " Maximum concurrent tool calls across sessions (default: 4)",
48
+ " --max-queued-tool-calls <count>",
49
+ " Maximum waiting tool calls (default: 64; 0 disables waiting)",
48
50
  " --trusted-proxy Trust X-Forwarded-Proto and X-Forwarded-Host",
49
51
  " --request-timeout-ms <ms>",
50
52
  " Node HTTP request timeout",
@@ -201,6 +203,7 @@ function parseCliOptions(args) {
201
203
  "max-sse-event-history": { type: "string" },
202
204
  "sse-keep-alive-ms": { type: "string" },
203
205
  "max-concurrent-tool-calls": { type: "string" },
206
+ "max-queued-tool-calls": { type: "string" },
204
207
  "trusted-proxy": { type: "boolean" },
205
208
  "request-timeout-ms": { type: "string" },
206
209
  "headers-timeout-ms": { type: "string" },
@@ -227,6 +230,7 @@ function parseCliOptions(args) {
227
230
  const maxSseEventHistory = parseOptionalInteger(values["max-sse-event-history"], "--max-sse-event-history", 0);
228
231
  const sseKeepAliveMs = parseOptionalInteger(values["sse-keep-alive-ms"], "--sse-keep-alive-ms", 0);
229
232
  const maxConcurrentToolCalls = parseOptionalInteger(values["max-concurrent-tool-calls"], "--max-concurrent-tool-calls", 1);
233
+ const maxQueuedToolCalls = parseOptionalInteger(values["max-queued-tool-calls"], "--max-queued-tool-calls", 0);
230
234
  const requestTimeoutMs = parseOptionalInteger(values["request-timeout-ms"], "--request-timeout-ms", 0);
231
235
  const headersTimeoutMs = parseOptionalInteger(values["headers-timeout-ms"], "--headers-timeout-ms", 0);
232
236
  const keepAliveTimeoutMs = parseOptionalInteger(values["keep-alive-timeout-ms"], "--keep-alive-timeout-ms", 0);
@@ -257,6 +261,7 @@ function parseCliOptions(args) {
257
261
  ...(maxSseEventHistory === undefined ? {} : { maxSseEventHistory }),
258
262
  ...(sseKeepAliveMs === undefined ? {} : { sseKeepAliveMs }),
259
263
  ...(maxConcurrentToolCalls === undefined ? {} : { maxConcurrentToolCalls }),
264
+ ...(maxQueuedToolCalls === undefined ? {} : { maxQueuedToolCalls }),
260
265
  trustedProxy: values["trusted-proxy"] ?? false,
261
266
  ...(requestTimeoutMs === undefined ? {} : { requestTimeoutMs }),
262
267
  ...(headersTimeoutMs === undefined ? {} : { headersTimeoutMs }),
@@ -412,6 +417,7 @@ export async function runCli(args = process.argv.slice(2), dependencies = {}) {
412
417
  ...(options.maxConcurrentToolCalls === undefined
413
418
  ? {}
414
419
  : { maxConcurrentToolCalls: options.maxConcurrentToolCalls }),
420
+ ...(options.maxQueuedToolCalls === undefined ? {} : { maxQueuedToolCalls: options.maxQueuedToolCalls }),
415
421
  ...(options.trustedProxy ? { trustedProxy: true } : {}),
416
422
  ...(oauth === undefined ? {} : { oauth })
417
423
  });
@@ -18,7 +18,7 @@
18
18
  },
19
19
  {
20
20
  "name": "tiny-http-mcp-server",
21
- "version": "0.1.12",
21
+ "version": "0.1.13",
22
22
  "license": "MIT"
23
23
  },
24
24
  {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tiny-http-mcp-server",
3
- "version": "0.1.12",
3
+ "version": "0.1.13",
4
4
  "description": "Minimal MCP server over HTTP built on tiny-stdio-mcp-server",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -8,7 +8,7 @@
8
8
  },
9
9
  {
10
10
  "name": "toolcraft-schema",
11
- "version": "0.0.198",
11
+ "version": "0.0.199",
12
12
  "license": "MIT"
13
13
  }
14
14
  ]
@@ -4,6 +4,7 @@ import { JSON_RPC_ERROR_CODES, ToolError } from "./types.js";
4
4
  import { parseMessage, formatSuccessResponse, formatErrorResponse } from "./jsonrpc.js";
5
5
  import { parseUriTemplate } from "./uri-template.js";
6
6
  import { toContentBlocks } from "./content/convert.js";
7
+ import { ToolCallAdmission } from "./tool-call-admission.js";
7
8
  const PROTOCOL_VERSION = "2025-11-25";
8
9
  const SUPPORTED_PROTOCOL_VERSIONS = new Set(["2025-03-26", "2025-06-18", PROTOCOL_VERSION]);
9
10
  function compileToolSchema(schema) {
@@ -20,6 +21,17 @@ export function createServer(options) {
20
21
  (!Number.isInteger(options.toolCallTimeoutMs) || options.toolCallTimeoutMs <= 0)) {
21
22
  throw new Error("toolCallTimeoutMs must be a positive integer.");
22
23
  }
24
+ const maxConcurrentToolCalls = options.maxConcurrentToolCalls ?? 4;
25
+ const maxQueuedToolCalls = options.maxQueuedToolCalls ?? 64;
26
+ for (const [name, value, minimum] of [
27
+ ["maxConcurrentToolCalls", maxConcurrentToolCalls, 1],
28
+ ["maxQueuedToolCalls", maxQueuedToolCalls, 0]
29
+ ]) {
30
+ if (!Number.isSafeInteger(value) || value < minimum) {
31
+ throw new Error(`${name} must be a safe integer greater than or equal to ${minimum}.`);
32
+ }
33
+ }
34
+ const toolAdmission = new ToolCallAdmission(maxConcurrentToolCalls, maxQueuedToolCalls);
23
35
  const supportNotifications = options.supportNotifications !== false;
24
36
  const supportResourceSubscriptions = options.supportResourceSubscriptions !== false;
25
37
  const tools = new Map();
@@ -142,17 +154,33 @@ export function createServer(options) {
142
154
  }
143
155
  try {
144
156
  let handlerResult;
157
+ const admissionTimeout = options.toolCallTimeoutMs === undefined ? undefined : new AbortController();
158
+ const admissionSignal = AbortSignal.any([
159
+ lifecycle.abortController.signal,
160
+ ...(admissionTimeout === undefined ? [] : [admissionTimeout.signal])
161
+ ]);
162
+ const handlerPromise = (async () => {
163
+ const release = await toolAdmission.acquire(admissionSignal);
164
+ try {
165
+ admissionSignal.throwIfAborted();
166
+ return await tool.handler(toolArgs);
167
+ }
168
+ finally {
169
+ release();
170
+ }
171
+ })();
145
172
  if (options.toolCallTimeoutMs === undefined) {
146
- handlerResult = await tool.handler(toolArgs);
173
+ handlerResult = await handlerPromise;
147
174
  }
148
175
  else {
149
176
  let timeout;
150
- const handlerPromise = Promise.resolve().then(() => tool.handler(toolArgs));
151
177
  handlerResult = await Promise.race([
152
178
  handlerPromise,
153
179
  new Promise((_resolve, reject) => {
154
180
  timeout = setTimeout(() => {
155
- reject(new ToolError(JSON_RPC_ERROR_CODES.INTERNAL_ERROR, `Tool call timed out: ${toolName}`));
181
+ const error = new ToolError(JSON_RPC_ERROR_CODES.INTERNAL_ERROR, `Tool call timed out: ${toolName}`);
182
+ admissionTimeout.abort(error);
183
+ reject(error);
156
184
  }, options.toolCallTimeoutMs);
157
185
  })
158
186
  ]).finally(() => {
@@ -0,0 +1,9 @@
1
+ export declare class ToolCallAdmission {
2
+ private readonly maximumActive;
3
+ private readonly maximumQueued;
4
+ private active;
5
+ private readonly waiting;
6
+ constructor(maximumActive: number, maximumQueued: number);
7
+ acquire(signal: AbortSignal): Promise<() => void>;
8
+ private release;
9
+ }
@@ -0,0 +1,51 @@
1
+ import { ToolError } from "./types.js";
2
+ export class ToolCallAdmission {
3
+ maximumActive;
4
+ maximumQueued;
5
+ active = 0;
6
+ waiting = [];
7
+ constructor(maximumActive, maximumQueued) {
8
+ this.maximumActive = maximumActive;
9
+ this.maximumQueued = maximumQueued;
10
+ }
11
+ acquire(signal) {
12
+ signal.throwIfAborted();
13
+ if (this.active < this.maximumActive) {
14
+ this.active++;
15
+ return Promise.resolve(this.release());
16
+ }
17
+ if (this.waiting.length >= this.maximumQueued) {
18
+ throw new ToolError(-32000, "Too many queued tool calls");
19
+ }
20
+ return new Promise((resolve, reject) => {
21
+ const waiting = {
22
+ signal,
23
+ resolve,
24
+ abort: () => {
25
+ const index = this.waiting.indexOf(waiting);
26
+ if (index !== -1)
27
+ this.waiting.splice(index, 1);
28
+ reject(signal.reason);
29
+ }
30
+ };
31
+ this.waiting.push(waiting);
32
+ signal.addEventListener("abort", waiting.abort, { once: true });
33
+ });
34
+ }
35
+ release() {
36
+ let released = false;
37
+ return () => {
38
+ if (released)
39
+ return;
40
+ released = true;
41
+ const next = this.waiting.shift();
42
+ if (next === undefined) {
43
+ this.active--;
44
+ }
45
+ else {
46
+ next.signal.removeEventListener("abort", next.abort);
47
+ next.resolve(this.release());
48
+ }
49
+ };
50
+ }
51
+ }
@@ -209,6 +209,10 @@ export interface ServerOptions {
209
209
  name: string;
210
210
  version: string;
211
211
  toolCallTimeoutMs?: number;
212
+ /** Shared active tool-handler capacity across sessions; defaults to four. */
213
+ maxConcurrentToolCalls?: number;
214
+ /** Maximum tool calls waiting for capacity; defaults to 64. Zero disables waiting. */
215
+ maxQueuedToolCalls?: number;
212
216
  validateToolArguments?: boolean;
213
217
  supportNotifications?: boolean;
214
218
  supportResourceSubscriptions?: boolean;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toolcraft-schema",
3
- "version": "0.0.198",
3
+ "version": "0.0.199",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toolcraft",
3
- "version": "0.0.198",
3
+ "version": "0.0.199",
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.198",
161
+ "toolcraft-schema": "0.0.199",
162
162
  "toolcraft-design": "*",
163
163
  "@poe-code/frontmatter": "*",
164
164
  "@poe-code/agent-mcp-config": "*",