@animalabs/agent-framework 0.26.1 → 0.27.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.
- package/README.md +57 -7
- package/dist/src/agent.d.ts +263 -22
- package/dist/src/agent.d.ts.map +1 -1
- package/dist/src/agent.js +531 -54
- package/dist/src/agent.js.map +1 -1
- package/dist/src/api/server.d.ts.map +1 -1
- package/dist/src/api/server.js +1 -2
- package/dist/src/api/server.js.map +1 -1
- package/dist/src/auto-continue.d.ts +33 -0
- package/dist/src/auto-continue.d.ts.map +1 -0
- package/dist/src/auto-continue.js +35 -0
- package/dist/src/auto-continue.js.map +1 -0
- package/dist/src/code-execution/isolate/scripts.d.ts +11 -0
- package/dist/src/code-execution/isolate/scripts.d.ts.map +1 -1
- package/dist/src/code-execution/isolate/scripts.js +5 -1
- package/dist/src/code-execution/isolate/scripts.js.map +1 -1
- package/dist/src/framework.d.ts +829 -156
- package/dist/src/framework.d.ts.map +1 -1
- package/dist/src/framework.js +3906 -1008
- package/dist/src/framework.js.map +1 -1
- package/dist/src/gate/event-gate.d.ts +309 -46
- package/dist/src/gate/event-gate.d.ts.map +1 -1
- package/dist/src/gate/event-gate.js +2117 -395
- package/dist/src/gate/event-gate.js.map +1 -1
- package/dist/src/gate/index.d.ts +2 -2
- package/dist/src/gate/index.d.ts.map +1 -1
- package/dist/src/gate/index.js +1 -1
- package/dist/src/gate/index.js.map +1 -1
- package/dist/src/gate/recipe-reconcile.d.ts +103 -0
- package/dist/src/gate/recipe-reconcile.d.ts.map +1 -0
- package/dist/src/gate/recipe-reconcile.js +325 -0
- package/dist/src/gate/recipe-reconcile.js.map +1 -0
- package/dist/src/gate/types.d.ts +68 -8
- package/dist/src/gate/types.d.ts.map +1 -1
- package/dist/src/index.d.ts +6 -4
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +5 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcpl/channel-registry.d.ts +433 -113
- package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
- package/dist/src/mcpl/channel-registry.js +1152 -434
- package/dist/src/mcpl/channel-registry.js.map +1 -1
- package/dist/src/mcpl/conversation-router.d.ts +16 -8
- package/dist/src/mcpl/conversation-router.d.ts.map +1 -1
- package/dist/src/mcpl/conversation-router.js +29 -15
- package/dist/src/mcpl/conversation-router.js.map +1 -1
- package/dist/src/mcpl/convert-block.d.ts.map +1 -1
- package/dist/src/mcpl/convert-block.js +14 -2
- package/dist/src/mcpl/convert-block.js.map +1 -1
- package/dist/src/mcpl/hook-orchestrator.d.ts +7 -1
- package/dist/src/mcpl/hook-orchestrator.d.ts.map +1 -1
- package/dist/src/mcpl/hook-orchestrator.js +32 -7
- package/dist/src/mcpl/hook-orchestrator.js.map +1 -1
- package/dist/src/mcpl/index.d.ts +5 -1
- package/dist/src/mcpl/index.d.ts.map +1 -1
- package/dist/src/mcpl/index.js +4 -0
- package/dist/src/mcpl/index.js.map +1 -1
- package/dist/src/mcpl/inference-router.d.ts +14 -2
- package/dist/src/mcpl/inference-router.d.ts.map +1 -1
- package/dist/src/mcpl/inference-router.js +73 -28
- package/dist/src/mcpl/inference-router.js.map +1 -1
- package/dist/src/mcpl/prose-grammar.d.ts +4 -0
- package/dist/src/mcpl/prose-grammar.d.ts.map +1 -1
- package/dist/src/mcpl/prose-grammar.js +6 -0
- package/dist/src/mcpl/prose-grammar.js.map +1 -1
- package/dist/src/mcpl/protocol-family.d.ts.map +1 -1
- package/dist/src/mcpl/protocol-family.js +3 -0
- package/dist/src/mcpl/protocol-family.js.map +1 -1
- package/dist/src/mcpl/push-coalescer.d.ts +20 -2
- package/dist/src/mcpl/push-coalescer.d.ts.map +1 -1
- package/dist/src/mcpl/push-coalescer.js +33 -11
- package/dist/src/mcpl/push-coalescer.js.map +1 -1
- package/dist/src/mcpl/reference-fetcher.d.ts +63 -42
- package/dist/src/mcpl/reference-fetcher.d.ts.map +1 -1
- package/dist/src/mcpl/reference-fetcher.js +177 -145
- package/dist/src/mcpl/reference-fetcher.js.map +1 -1
- package/dist/src/mcpl/references.d.ts +380 -46
- package/dist/src/mcpl/references.d.ts.map +1 -1
- package/dist/src/mcpl/references.js +737 -78
- package/dist/src/mcpl/references.js.map +1 -1
- package/dist/src/mcpl/server-connection.d.ts +82 -3
- package/dist/src/mcpl/server-connection.d.ts.map +1 -1
- package/dist/src/mcpl/server-connection.js +132 -7
- package/dist/src/mcpl/server-connection.js.map +1 -1
- package/dist/src/mcpl/tool-lifecycle.d.ts +11 -0
- package/dist/src/mcpl/tool-lifecycle.d.ts.map +1 -1
- package/dist/src/mcpl/tool-lifecycle.js +21 -0
- package/dist/src/mcpl/tool-lifecycle.js.map +1 -1
- package/dist/src/mcpl/tool-presence.d.ts +259 -0
- package/dist/src/mcpl/tool-presence.d.ts.map +1 -0
- package/dist/src/mcpl/tool-presence.js +628 -0
- package/dist/src/mcpl/tool-presence.js.map +1 -0
- package/dist/src/mcpl/tool-result-normalize.d.ts +9 -3
- package/dist/src/mcpl/tool-result-normalize.d.ts.map +1 -1
- package/dist/src/mcpl/tool-result-normalize.js +14 -6
- package/dist/src/mcpl/tool-result-normalize.js.map +1 -1
- package/dist/src/mcpl/tool-signature.d.ts +39 -0
- package/dist/src/mcpl/tool-signature.d.ts.map +1 -0
- package/dist/src/mcpl/tool-signature.js +292 -0
- package/dist/src/mcpl/tool-signature.js.map +1 -0
- package/dist/src/mcpl/toolset-host.d.ts +200 -0
- package/dist/src/mcpl/toolset-host.d.ts.map +1 -0
- package/dist/src/mcpl/toolset-host.js +621 -0
- package/dist/src/mcpl/toolset-host.js.map +1 -0
- package/dist/src/mcpl/toolsets-module.d.ts +80 -0
- package/dist/src/mcpl/toolsets-module.d.ts.map +1 -0
- package/dist/src/mcpl/toolsets-module.js +431 -0
- package/dist/src/mcpl/toolsets-module.js.map +1 -0
- package/dist/src/mcpl/transport.d.ts +2 -2
- package/dist/src/mcpl/transport.d.ts.map +1 -1
- package/dist/src/mcpl/transport.js +3 -2
- package/dist/src/mcpl/transport.js.map +1 -1
- package/dist/src/mcpl/types.d.ts +21 -1
- package/dist/src/mcpl/types.d.ts.map +1 -1
- package/dist/src/mcpl/types.js.map +1 -1
- package/dist/src/mcpl/visible-content.d.ts +7 -9
- package/dist/src/mcpl/visible-content.d.ts.map +1 -1
- package/dist/src/mcpl/visible-content.js +21 -9
- package/dist/src/mcpl/visible-content.js.map +1 -1
- package/dist/src/module-registry.d.ts +23 -8
- package/dist/src/module-registry.d.ts.map +1 -1
- package/dist/src/module-registry.js +68 -15
- package/dist/src/module-registry.js.map +1 -1
- package/dist/src/modules/discord/index.js +1 -1
- package/dist/src/modules/discord/index.js.map +1 -1
- package/dist/src/modules/history/index.d.ts +2 -1
- package/dist/src/modules/history/index.d.ts.map +1 -1
- package/dist/src/modules/history/index.js +51 -17
- package/dist/src/modules/history/index.js.map +1 -1
- package/dist/src/modules/history/semantic.d.ts +108 -6
- package/dist/src/modules/history/semantic.d.ts.map +1 -1
- package/dist/src/modules/history/semantic.js +273 -57
- package/dist/src/modules/history/semantic.js.map +1 -1
- package/dist/src/modules/workspace/direct-write.d.ts +181 -0
- package/dist/src/modules/workspace/direct-write.d.ts.map +1 -0
- package/dist/src/modules/workspace/direct-write.js +384 -0
- package/dist/src/modules/workspace/direct-write.js.map +1 -0
- package/dist/src/modules/workspace/edit-text.d.ts +26 -12
- package/dist/src/modules/workspace/edit-text.d.ts.map +1 -1
- package/dist/src/modules/workspace/edit-text.js +58 -28
- package/dist/src/modules/workspace/edit-text.js.map +1 -1
- package/dist/src/modules/workspace/effects.d.ts +10 -0
- package/dist/src/modules/workspace/effects.d.ts.map +1 -1
- package/dist/src/modules/workspace/effects.js +1 -1
- package/dist/src/modules/workspace/effects.js.map +1 -1
- package/dist/src/modules/workspace/index.d.ts +246 -27
- package/dist/src/modules/workspace/index.d.ts.map +1 -1
- package/dist/src/modules/workspace/index.js +1216 -236
- package/dist/src/modules/workspace/index.js.map +1 -1
- package/dist/src/modules/workspace/read-page.d.ts +41 -0
- package/dist/src/modules/workspace/read-page.d.ts.map +1 -0
- package/dist/src/modules/workspace/read-page.js +213 -0
- package/dist/src/modules/workspace/read-page.js.map +1 -0
- package/dist/src/modules/workspace/result-text.d.ts +67 -0
- package/dist/src/modules/workspace/result-text.d.ts.map +1 -0
- package/dist/src/modules/workspace/result-text.js +94 -0
- package/dist/src/modules/workspace/result-text.js.map +1 -0
- package/dist/src/modules/workspace/text.d.ts +29 -12
- package/dist/src/modules/workspace/text.d.ts.map +1 -1
- package/dist/src/modules/workspace/text.js +35 -33
- package/dist/src/modules/workspace/text.js.map +1 -1
- package/dist/src/modules/workspace/types.d.ts +41 -17
- package/dist/src/modules/workspace/types.d.ts.map +1 -1
- package/dist/src/modules/workspace/types.js.map +1 -1
- package/dist/src/sent-rows.d.ts +59 -0
- package/dist/src/sent-rows.d.ts.map +1 -0
- package/dist/src/sent-rows.js +145 -0
- package/dist/src/sent-rows.js.map +1 -0
- package/dist/src/silent-heartbeat.d.ts +17 -10
- package/dist/src/silent-heartbeat.d.ts.map +1 -1
- package/dist/src/silent-heartbeat.js.map +1 -1
- package/dist/src/stable-hash.d.ts +3 -0
- package/dist/src/stable-hash.d.ts.map +1 -0
- package/dist/src/stable-hash.js +15 -0
- package/dist/src/stable-hash.js.map +1 -0
- package/dist/src/stored-injections.d.ts +99 -0
- package/dist/src/stored-injections.d.ts.map +1 -0
- package/dist/src/stored-injections.js +106 -0
- package/dist/src/stored-injections.js.map +1 -0
- package/dist/src/text-as-stored.d.ts +4 -3
- package/dist/src/text-as-stored.d.ts.map +1 -1
- package/dist/src/text-as-stored.js +4 -3
- package/dist/src/text-as-stored.js.map +1 -1
- package/dist/src/thinking-drops.d.ts +81 -0
- package/dist/src/thinking-drops.d.ts.map +1 -0
- package/dist/src/thinking-drops.js +103 -0
- package/dist/src/thinking-drops.js.map +1 -0
- package/dist/src/tool-image-ledger.d.ts +9 -8
- package/dist/src/tool-image-ledger.d.ts.map +1 -1
- package/dist/src/tool-image-ledger.js +9 -8
- package/dist/src/tool-image-ledger.js.map +1 -1
- package/dist/src/tool-presentation.d.ts.map +1 -1
- package/dist/src/tool-presentation.js +3 -1
- package/dist/src/tool-presentation.js.map +1 -1
- package/dist/src/tool-result-guard.d.ts +4 -2
- package/dist/src/tool-result-guard.d.ts.map +1 -1
- package/dist/src/tool-result-guard.js +21 -6
- package/dist/src/tool-result-guard.js.map +1 -1
- package/dist/src/tool-result-history.d.ts +186 -21
- package/dist/src/tool-result-history.d.ts.map +1 -1
- package/dist/src/tool-result-history.js +428 -89
- package/dist/src/tool-result-history.js.map +1 -1
- package/dist/src/tune-out/coordinator.d.ts +17 -9
- package/dist/src/tune-out/coordinator.d.ts.map +1 -1
- package/dist/src/tune-out/coordinator.js +84 -37
- package/dist/src/tune-out/coordinator.js.map +1 -1
- package/dist/src/types/agent.d.ts +186 -7
- package/dist/src/types/agent.d.ts.map +1 -1
- package/dist/src/types/events.d.ts +19 -2
- package/dist/src/types/events.d.ts.map +1 -1
- package/dist/src/types/framework.d.ts +24 -3
- package/dist/src/types/framework.d.ts.map +1 -1
- package/dist/src/types/module.d.ts +90 -7
- package/dist/src/types/module.d.ts.map +1 -1
- package/dist/src/types/trace.d.ts +57 -2
- package/dist/src/types/trace.d.ts.map +1 -1
- package/package.json +4 -3
- package/src/agent.ts +593 -60
- package/src/api/server.ts +1 -2
- package/src/auto-continue.ts +42 -0
- package/src/code-execution/isolate/scripts.ts +11 -3
- package/src/framework.ts +4004 -1026
- package/src/gate/event-gate.ts +2315 -389
- package/src/gate/index.ts +2 -1
- package/src/gate/recipe-reconcile.ts +355 -0
- package/src/gate/types.ts +70 -8
- package/src/index.ts +7 -4
- package/src/mcpl/channel-registry.ts +1220 -467
- package/src/mcpl/conversation-router.ts +34 -16
- package/src/mcpl/convert-block.ts +14 -1
- package/src/mcpl/hook-orchestrator.ts +33 -6
- package/src/mcpl/index.ts +6 -1
- package/src/mcpl/inference-router.ts +79 -34
- package/src/mcpl/prose-grammar.ts +7 -0
- package/src/mcpl/protocol-family.ts +2 -0
- package/src/mcpl/push-coalescer.ts +51 -13
- package/src/mcpl/reference-fetcher.ts +201 -162
- package/src/mcpl/references.ts +852 -78
- package/src/mcpl/server-connection.ts +145 -7
- package/src/mcpl/tool-lifecycle.ts +21 -0
- package/src/mcpl/tool-presence.ts +650 -0
- package/src/mcpl/tool-result-normalize.ts +18 -6
- package/src/mcpl/tool-signature.ts +285 -0
- package/src/mcpl/toolset-host.ts +637 -0
- package/src/mcpl/toolsets-module.ts +451 -0
- package/src/mcpl/transport.ts +3 -2
- package/src/mcpl/types.ts +22 -1
- package/src/mcpl/visible-content.ts +22 -9
- package/src/module-registry.ts +77 -24
- package/src/modules/discord/index.ts +1 -1
- package/src/modules/history/index.ts +48 -18
- package/src/modules/history/semantic.ts +308 -54
- package/src/modules/workspace/direct-write.ts +425 -0
- package/src/modules/workspace/edit-text.ts +82 -35
- package/src/modules/workspace/effects.ts +1 -1
- package/src/modules/workspace/index.ts +1274 -241
- package/src/modules/workspace/read-page.ts +222 -0
- package/src/modules/workspace/result-text.ts +119 -0
- package/src/modules/workspace/text.ts +58 -32
- package/src/modules/workspace/types.ts +41 -17
- package/src/sent-rows.ts +162 -0
- package/src/silent-heartbeat.ts +17 -10
- package/src/stable-hash.ts +14 -0
- package/src/stored-injections.ts +131 -0
- package/src/text-as-stored.ts +4 -3
- package/src/thinking-drops.ts +141 -0
- package/src/tool-image-ledger.ts +9 -8
- package/src/tool-presentation.ts +3 -1
- package/src/tool-result-guard.ts +19 -6
- package/src/tool-result-history.ts +488 -88
- package/src/tune-out/coordinator.ts +80 -37
- package/src/types/agent.ts +189 -7
- package/src/types/events.ts +19 -2
- package/src/types/framework.ts +21 -3
- package/src/types/module.ts +86 -7
- package/src/types/trace.ts +58 -2
|
@@ -219,6 +219,41 @@ function protocolVersionRejection(config: McplServerConfig, error: { code: numbe
|
|
|
219
219
|
|
|
220
220
|
const errorOf = (value: unknown): Error => (value instanceof Error ? value : new Error(String(value)));
|
|
221
221
|
|
|
222
|
+
/**
|
|
223
|
+
* The RFC-014 members of a server's advertisement (`toolHints`, `toolsets`,
|
|
224
|
+
* `surface`), read from it as the server sent it. They are declarations, not
|
|
225
|
+
* §6.2 capabilities: the host's capability mask, which reads every member as
|
|
226
|
+
* a capability, never applies to them. Null when the server advertised none
|
|
227
|
+
* of them, or no MCPL at all.
|
|
228
|
+
*/
|
|
229
|
+
export function toolsetDeclarationsOf(advertised: unknown): Record<string, unknown> | null {
|
|
230
|
+
if (advertised === null || typeof advertised !== 'object' || Array.isArray(advertised)) return null;
|
|
231
|
+
const { toolHints, toolsets, surface } = advertised as Record<string, unknown>;
|
|
232
|
+
if (toolHints === undefined && toolsets === undefined && surface === undefined) return null;
|
|
233
|
+
return {
|
|
234
|
+
...(toolHints !== undefined ? { toolHints } : {}),
|
|
235
|
+
...(toolsets !== undefined ? { toolsets } : {}),
|
|
236
|
+
...(surface !== undefined ? { surface } : {}),
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/** What the RFC-014 read tap is handed as the connection reads a message
|
|
241
|
+
* that can carry hint changes (see McplServerConnection.setReadTap). */
|
|
242
|
+
export type HintRead =
|
|
243
|
+
/** A response to a request that can carry hints (RFC-014 §6.1 carrier 1).
|
|
244
|
+
* `awaited`: whoever sent it still waits for it; false for a result that
|
|
245
|
+
* arrives after its timeout. A JSON-RPC error carries no state and isn't
|
|
246
|
+
* tapped. */
|
|
247
|
+
| { kind: 'result'; method: string; params: Record<string, unknown>; result: unknown; awaited: boolean }
|
|
248
|
+
/** A push/event (carrier 2), as read, before the plane gate or the grant
|
|
249
|
+
* admit or refuse it. */
|
|
250
|
+
| { kind: 'push'; params: unknown }
|
|
251
|
+
/** A standalone tools/hints (carrier 3). */
|
|
252
|
+
| { kind: 'hints'; params: unknown };
|
|
253
|
+
|
|
254
|
+
/** Requests whose results can carry hint state (RFC-014 §6.1, §4.1). */
|
|
255
|
+
const HINTED_METHODS: ReadonlySet<string> = new Set(['tools/call', 'toolsets/open', 'toolsets/close', 'toolsets/list', 'tools/list']);
|
|
256
|
+
|
|
222
257
|
/**
|
|
223
258
|
* A handshake that failed, and whose cleanup couldn't confirm that the child
|
|
224
259
|
* exited. The transport goes to whoever owns the launch.
|
|
@@ -404,6 +439,46 @@ export class McplServerConnection extends EventEmitter {
|
|
|
404
439
|
* the epoch their opening went to, never across a reconnect. */
|
|
405
440
|
transportEpoch = 0;
|
|
406
441
|
|
|
442
|
+
/**
|
|
443
|
+
* RFC-014: the server's `toolHints`, `toolsets` and `surface` members as it
|
|
444
|
+
* advertised them at the last handshake (toolsetDeclarationsOf), or null.
|
|
445
|
+
* Read by the host's toolset controller; they grant nothing.
|
|
446
|
+
*/
|
|
447
|
+
toolsetDeclarations: Record<string, unknown> | null = null;
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* RFC-014 §6.1: hint changes apply as each message carrying them is read,
|
|
451
|
+
* in rev order (arrival order on stdio and WebSocket), whatever becomes of
|
|
452
|
+
* the message: a push the plane holds or the grant refuses, a result its
|
|
453
|
+
* caller stopped waiting for. The tap sees every such message of the
|
|
454
|
+
* current transport, synchronously, before anything routes it; a late
|
|
455
|
+
* answer's request is remembered past its timeout (hintedRequests) so its
|
|
456
|
+
* hints still apply. Null (the default) taps nothing, and the connection
|
|
457
|
+
* behaves as it always has.
|
|
458
|
+
*/
|
|
459
|
+
private readTap: ((read: HintRead, epoch: number) => void) | null = null;
|
|
460
|
+
/** The hinted requests sent on this transport, by id, kept past a timeout
|
|
461
|
+
* so a late result is still tapped; bounded. Cleared at each boundary,
|
|
462
|
+
* since the next transport numbers its requests afresh. */
|
|
463
|
+
private hintedRequests = new Map<string | number, { method: string; params: Record<string, unknown> }>();
|
|
464
|
+
|
|
465
|
+
/** Install (or, with null, remove) the RFC-014 read tap (see readTap). */
|
|
466
|
+
setReadTap(tap: ((read: HintRead, epoch: number) => void) | null): void {
|
|
467
|
+
this.readTap = tap;
|
|
468
|
+
if (!tap) this.hintedRequests.clear();
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/** Hand a read to the tap, contained: a tap that throws is logged, and the
|
|
472
|
+
* message goes on its way as if there were none. */
|
|
473
|
+
private tap(read: HintRead): void {
|
|
474
|
+
if (!this.readTap) return;
|
|
475
|
+
try {
|
|
476
|
+
this.readTap(read, this.transportEpoch);
|
|
477
|
+
} catch (error) {
|
|
478
|
+
console.error(`[mcpl] ${this.id}: the tool-hints tap failed: ${logDetail(describeCause(error))}`);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
407
482
|
/**
|
|
408
483
|
* §17 manifest tracking (host side of RFC-003): what this host actually
|
|
409
484
|
* fetched and acted on — never the server's announcement log (§17.10:
|
|
@@ -454,6 +529,8 @@ export class McplServerConnection extends EventEmitter {
|
|
|
454
529
|
// (connect-failed, reconnect-failed), the server's stderr, and
|
|
455
530
|
// orphaned-response audits.
|
|
456
531
|
this.replaceHeld(this.keepAcrossBoundary(this.bufferedEvents));
|
|
532
|
+
// The next transport numbers its requests afresh (RFC-014's tap).
|
|
533
|
+
this.hintedRequests.clear();
|
|
457
534
|
// RFC-007 §6.5: a filter is per connection epoch — after a reconnect the
|
|
458
535
|
// server sends it again, and until then receives metadata only.
|
|
459
536
|
this.toolObserveFilter = null;
|
|
@@ -1116,9 +1193,10 @@ export class McplServerConnection extends EventEmitter {
|
|
|
1116
1193
|
owner.holdUnreaped(error.transport, error.cleanup);
|
|
1117
1194
|
throw new McplUnreapedLaunchError(owner, error.failure, error.cleanup);
|
|
1118
1195
|
}
|
|
1119
|
-
const { transport, capabilities, droppedCapabilities, mcpToolsAdvertised, protocolVersion } = established;
|
|
1196
|
+
const { transport, capabilities, droppedCapabilities, mcpToolsAdvertised, protocolVersion, toolsetDeclarations } = established;
|
|
1120
1197
|
|
|
1121
1198
|
const connection = new McplServerConnection(config.id, capabilities, transport);
|
|
1199
|
+
connection.toolsetDeclarations = toolsetDeclarations;
|
|
1122
1200
|
connection.droppedCapabilities = droppedCapabilities;
|
|
1123
1201
|
connection.mcpToolsAdvertised = mcpToolsAdvertised;
|
|
1124
1202
|
connection.protocolVersion = protocolVersion;
|
|
@@ -1172,7 +1250,7 @@ export class McplServerConnection extends EventEmitter {
|
|
|
1172
1250
|
config: McplServerConfig,
|
|
1173
1251
|
hostCapabilities: McplHostCapabilities,
|
|
1174
1252
|
signal?: AbortSignal,
|
|
1175
|
-
): Promise<{ transport: McplTransport; capabilities: McplCapabilities | null; droppedCapabilities: ReadonlySet<string>; mcpToolsAdvertised: boolean; protocolVersion: string }> {
|
|
1253
|
+
): Promise<{ transport: McplTransport; capabilities: McplCapabilities | null; droppedCapabilities: ReadonlySet<string>; mcpToolsAdvertised: boolean; protocolVersion: string; toolsetDeclarations: Record<string, unknown> | null }> {
|
|
1176
1254
|
const closedWhileConnecting = () => new Error(`MCPL server "${config.id}" was closed while connecting`);
|
|
1177
1255
|
if (signal?.aborted) throw closedWhileConnecting();
|
|
1178
1256
|
let transport: McplTransport;
|
|
@@ -1362,7 +1440,10 @@ export class McplServerConnection extends EventEmitter {
|
|
|
1362
1440
|
// listener of its own, in the continuation of this return, before any
|
|
1363
1441
|
// I/O callback can emit.
|
|
1364
1442
|
transport.off('error', logAfterSettled);
|
|
1365
|
-
return {
|
|
1443
|
+
return {
|
|
1444
|
+
transport, capabilities: scoped, droppedCapabilities: new Set(dropped), mcpToolsAdvertised, protocolVersion,
|
|
1445
|
+
toolsetDeclarations: toolsetDeclarationsOf(rawCapabilities),
|
|
1446
|
+
};
|
|
1366
1447
|
} catch (err) {
|
|
1367
1448
|
// Never leak the child / socket if the handshake fails. A cleanup that
|
|
1368
1449
|
// can't confirm the child exited is part of the failure, never
|
|
@@ -1538,7 +1619,7 @@ export class McplServerConnection extends EventEmitter {
|
|
|
1538
1619
|
}
|
|
1539
1620
|
|
|
1540
1621
|
/** Send `channels/open` request and await result. It keeps a deadline as
|
|
1541
|
-
* channels/publish does: a delivery
|
|
1622
|
+
* channels/publish does: a delivery can open a closed channel before it
|
|
1542
1623
|
* publishes, so an open that is never answered would hold the delivery,
|
|
1543
1624
|
* and the turn that waits for it, for good. */
|
|
1544
1625
|
sendChannelsOpen(params: ChannelsOpenParams): Promise<ChannelsOpenResult> {
|
|
@@ -1662,18 +1743,39 @@ export class McplServerConnection extends EventEmitter {
|
|
|
1662
1743
|
throw new Error(`MCPL server "${this.id}" tools/list did not finish within ${MAX_TOOL_LIST_PAGES} pages`);
|
|
1663
1744
|
}
|
|
1664
1745
|
|
|
1665
|
-
/** Send `tools/call` and return the result. Optionally includes
|
|
1746
|
+
/** Send `tools/call` and return the result. Optionally includes
|
|
1747
|
+
* state/checkpoint for stateful tools, and `_meta` (RFC-014 §5.5: the
|
|
1748
|
+
* instance a call is for rides in `_meta["mcpl/instance"]`). */
|
|
1666
1749
|
sendToolsCall(
|
|
1667
1750
|
name: string,
|
|
1668
1751
|
args: Record<string, unknown>,
|
|
1669
1752
|
stateParams?: { state?: unknown; checkpoint?: string },
|
|
1753
|
+
meta?: Record<string, unknown>,
|
|
1670
1754
|
): Promise<McpToolCallResult> {
|
|
1671
1755
|
const params: Record<string, unknown> = { name, arguments: args };
|
|
1672
1756
|
if (stateParams?.state !== undefined) params.state = stateParams.state;
|
|
1673
1757
|
if (stateParams?.checkpoint !== undefined) params.checkpoint = stateParams.checkpoint;
|
|
1758
|
+
if (meta !== undefined) params._meta = meta;
|
|
1674
1759
|
return this.sendRequest('tools/call', params) as Promise<McpToolCallResult>;
|
|
1675
1760
|
}
|
|
1676
1761
|
|
|
1762
|
+
/** RFC-014 §5.2 `toolsets/open` (Host → Server). The server's refusal
|
|
1763
|
+
* rejects, as any error answer does. */
|
|
1764
|
+
sendToolsetsOpen(params: { toolset: string; instance?: string; options?: Record<string, unknown> }, timeoutMs?: number): Promise<Record<string, unknown> | null> {
|
|
1765
|
+
return this.sendRequest('toolsets/open', { ...params }, timeoutMs === undefined ? {} : { timeoutMs }) as Promise<Record<string, unknown> | null>;
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1768
|
+
/** RFC-014 §5.2 `toolsets/close` (Host → Server). */
|
|
1769
|
+
sendToolsetsClose(params: { toolset: string; instance?: string }, timeoutMs?: number): Promise<Record<string, unknown> | null> {
|
|
1770
|
+
return this.sendRequest('toolsets/close', { ...params }, timeoutMs === undefined ? {} : { timeoutMs }) as Promise<Record<string, unknown> | null>;
|
|
1771
|
+
}
|
|
1772
|
+
|
|
1773
|
+
/** RFC-014 §5.3 `toolsets/list` (Host → Server): what the principal has
|
|
1774
|
+
* open, and the full hint state of its dynamic tools. */
|
|
1775
|
+
sendToolsetsList(timeoutMs?: number): Promise<Record<string, unknown> | null> {
|
|
1776
|
+
return this.sendRequest('toolsets/list', {}, timeoutMs === undefined ? {} : { timeoutMs }) as Promise<Record<string, unknown> | null>;
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1677
1779
|
/**
|
|
1678
1780
|
* Send a tools/call with a mandatory request-local deadline. This is used by
|
|
1679
1781
|
* durable external-side-effect accounting, whose safety bound must remain in
|
|
@@ -1940,7 +2042,7 @@ export class McplServerConnection extends EventEmitter {
|
|
|
1940
2042
|
if (!permanent) this.scheduleReconnect();
|
|
1941
2043
|
return;
|
|
1942
2044
|
}
|
|
1943
|
-
const { transport, capabilities, droppedCapabilities, mcpToolsAdvertised, protocolVersion } = established;
|
|
2045
|
+
const { transport, capabilities, droppedCapabilities, mcpToolsAdvertised, protocolVersion, toolsetDeclarations } = established;
|
|
1944
2046
|
|
|
1945
2047
|
if (signal.aborted) {
|
|
1946
2048
|
// Closed by the host while the handshake finished: this launch never
|
|
@@ -1968,6 +2070,7 @@ export class McplServerConnection extends EventEmitter {
|
|
|
1968
2070
|
this.resetPolicyForTransportBoundary();
|
|
1969
2071
|
this.transport = transport;
|
|
1970
2072
|
this.capabilities = capabilities;
|
|
2073
|
+
this.toolsetDeclarations = toolsetDeclarations;
|
|
1971
2074
|
this.droppedCapabilities = droppedCapabilities;
|
|
1972
2075
|
this.mcpToolsAdvertised = mcpToolsAdvertised;
|
|
1973
2076
|
this.protocolVersion = protocolVersion;
|
|
@@ -2112,6 +2215,12 @@ export class McplServerConnection extends EventEmitter {
|
|
|
2112
2215
|
pending.timer.unref?.();
|
|
2113
2216
|
}
|
|
2114
2217
|
this.pendingRequests.set(id, pending);
|
|
2218
|
+
// RFC-014: a request whose result can carry hints is remembered past
|
|
2219
|
+
// its timeout, so a late result's hints still apply (readTap).
|
|
2220
|
+
if (this.readTap && HINTED_METHODS.has(method)) {
|
|
2221
|
+
this.hintedRequests.set(id, { method, params });
|
|
2222
|
+
if (this.hintedRequests.size > 1000) this.hintedRequests.delete(this.hintedRequests.keys().next().value!);
|
|
2223
|
+
}
|
|
2115
2224
|
try {
|
|
2116
2225
|
this.transport!.writeLine(line);
|
|
2117
2226
|
} catch (err) {
|
|
@@ -2120,6 +2229,7 @@ export class McplServerConnection extends EventEmitter {
|
|
|
2120
2229
|
// method, id and the transport's words are the operator's, logged
|
|
2121
2230
|
// after the refusal, as refused() logs.
|
|
2122
2231
|
this.pendingRequests.delete(id);
|
|
2232
|
+
this.hintedRequests.delete(id);
|
|
2123
2233
|
if (pending.timer) clearTimeout(pending.timer);
|
|
2124
2234
|
reject(new McplRequestError(
|
|
2125
2235
|
`The request wasn't sent: this host couldn't write it to the "${this.id}" server, so nothing ran. Try again later.`,
|
|
@@ -2258,7 +2368,12 @@ export class McplServerConnection extends EventEmitter {
|
|
|
2258
2368
|
private dispatchMessage(msg: JsonRpcRequest | JsonRpcResponse, epoch: number): void {
|
|
2259
2369
|
// Is this a response to one of our outbound requests?
|
|
2260
2370
|
if ('id' in msg && msg.id != null && !('method' in msg)) {
|
|
2261
|
-
|
|
2371
|
+
const late = epoch !== this.transportEpoch;
|
|
2372
|
+
// RFC-014: its hints apply as it's read, whether or not anyone still
|
|
2373
|
+
// waits for it. A dead transport's revs are its own, so a response
|
|
2374
|
+
// read after its boundary is never tapped.
|
|
2375
|
+
if (!late) this.tapResponse(msg as JsonRpcResponse);
|
|
2376
|
+
this.handleResponse(msg as JsonRpcResponse, late);
|
|
2262
2377
|
return;
|
|
2263
2378
|
}
|
|
2264
2379
|
|
|
@@ -2282,6 +2397,17 @@ export class McplServerConnection extends EventEmitter {
|
|
|
2282
2397
|
return;
|
|
2283
2398
|
}
|
|
2284
2399
|
|
|
2400
|
+
// RFC-014 §6.1: a push's hints apply as it's read, before the plane gate
|
|
2401
|
+
// holds it or the grant refuses it. A standalone tools/hints is consumed
|
|
2402
|
+
// here when a tap listens (it needs no grant), and answered if it came as
|
|
2403
|
+
// a request; with no tap it stays a method this host doesn't handle.
|
|
2404
|
+
if (request.method === 'push/event') this.tap({ kind: 'push', params: request.params });
|
|
2405
|
+
else if (request.method === 'tools/hints' && this.readTap) {
|
|
2406
|
+
this.tap({ kind: 'hints', params: request.params });
|
|
2407
|
+
if (request.id != null) this.sendResponse(request.id, {});
|
|
2408
|
+
return;
|
|
2409
|
+
}
|
|
2410
|
+
|
|
2285
2411
|
const eventName = typeof request.method === 'string' && Object.hasOwn(McplServerConnection.METHOD_TO_EVENT, request.method)
|
|
2286
2412
|
? McplServerConnection.METHOD_TO_EVENT[request.method] : undefined;
|
|
2287
2413
|
|
|
@@ -2427,6 +2553,18 @@ export class McplServerConnection extends EventEmitter {
|
|
|
2427
2553
|
return true;
|
|
2428
2554
|
}
|
|
2429
2555
|
|
|
2556
|
+
/** Tap a response to a hinted request (RFC-014): its result's hints, or a
|
|
2557
|
+
* list's statement, apply now, awaited or not. */
|
|
2558
|
+
private tapResponse(response: JsonRpcResponse): void {
|
|
2559
|
+
if (!this.readTap) return;
|
|
2560
|
+
const sent = this.hintedRequests.get(response.id);
|
|
2561
|
+
if (!sent) return;
|
|
2562
|
+
this.hintedRequests.delete(response.id);
|
|
2563
|
+
const result = response.result;
|
|
2564
|
+
if (response.error || result === null || typeof result !== 'object' || Array.isArray(result)) return;
|
|
2565
|
+
this.tap({ kind: 'result', method: sent.method, params: sent.params, result, awaited: this.pendingRequests.has(response.id) });
|
|
2566
|
+
}
|
|
2567
|
+
|
|
2430
2568
|
/**
|
|
2431
2569
|
* Handle a JSON-RPC response by resolving/rejecting the corresponding pending request.
|
|
2432
2570
|
*/
|
|
@@ -705,6 +705,27 @@ export class ToolLifecycleEmitter {
|
|
|
705
705
|
});
|
|
706
706
|
}
|
|
707
707
|
|
|
708
|
+
/**
|
|
709
|
+
* A call the host routes to another tool before it executes it: RFC-014's
|
|
710
|
+
* `toolsets--act`, which calls a server's dynamic tool. Until it opens, the
|
|
711
|
+
* call is described as that tool, with that tool's arguments, class and
|
|
712
|
+
* provider, so observers see (and narrowing, filters and class rules apply
|
|
713
|
+
* to) what actually runs, as for a direct call of it. Its ids stay.
|
|
714
|
+
*/
|
|
715
|
+
retarget(agentName: string, callId: string, call: { name: string; input: unknown }): void {
|
|
716
|
+
const tracked = this.calls.get(callKey(agentName, callId));
|
|
717
|
+
if (!tracked || tracked.opened) return;
|
|
718
|
+
const described = this.host.describe(call.name);
|
|
719
|
+
const { serverId: _serverId, serverTool: _serverTool, ...kept } = tracked.call;
|
|
720
|
+
tracked.call = {
|
|
721
|
+
...kept,
|
|
722
|
+
tool: call.name,
|
|
723
|
+
class: described.class,
|
|
724
|
+
...(described.serverId !== undefined ? { serverId: described.serverId, serverTool: described.serverTool } : {}),
|
|
725
|
+
input: call.input,
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
|
|
708
729
|
/**
|
|
709
730
|
* The host refused the call before executing it — its provider is gone,
|
|
710
731
|
* host tool policy denies it, a conversation-bound agent reached outside
|