agentfootprint 12.0.1 → 12.2.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/AGENTS.md +1 -1
- package/CAPABILITIES.md +3 -0
- package/ai-instructions/claude-code/SKILL.md +1 -1
- package/dist/adapters/mcp/agentcoreRuntime/invoke.js +113 -7
- package/dist/adapters/mcp/agentcoreRuntime/invoke.js.map +1 -1
- package/dist/adapters/mcp/agentcoreRuntime/session.js +21 -21
- package/dist/adapters/mcp/agentcoreRuntime/session.js.map +1 -1
- package/dist/adapters/mcp/agentcoreRuntime/transport.js +79 -5
- package/dist/adapters/mcp/agentcoreRuntime/transport.js.map +1 -1
- package/dist/core/Agent.js +6 -0
- package/dist/core/Agent.js.map +1 -1
- package/dist/core/agent/stages/toolCalls.js +4 -0
- package/dist/core/agent/stages/toolCalls.js.map +1 -1
- package/dist/core/slots/buildToolsSlot.js +8 -0
- package/dist/core/slots/buildToolsSlot.js.map +1 -1
- package/dist/esm/adapters/mcp/agentcoreRuntime/invoke.d.ts +23 -4
- package/dist/esm/adapters/mcp/agentcoreRuntime/invoke.js +112 -6
- package/dist/esm/adapters/mcp/agentcoreRuntime/invoke.js.map +1 -1
- package/dist/esm/adapters/mcp/agentcoreRuntime/session.d.ts +20 -6
- package/dist/esm/adapters/mcp/agentcoreRuntime/session.js +21 -21
- package/dist/esm/adapters/mcp/agentcoreRuntime/session.js.map +1 -1
- package/dist/esm/adapters/mcp/agentcoreRuntime/transport.js +81 -7
- package/dist/esm/adapters/mcp/agentcoreRuntime/transport.js.map +1 -1
- package/dist/esm/core/Agent.js +6 -0
- package/dist/esm/core/Agent.js.map +1 -1
- package/dist/esm/core/agent/stages/toolCalls.js +4 -0
- package/dist/esm/core/agent/stages/toolCalls.js.map +1 -1
- package/dist/esm/core/slots/buildToolsSlot.d.ts +12 -0
- package/dist/esm/core/slots/buildToolsSlot.js +8 -0
- package/dist/esm/core/slots/buildToolsSlot.js.map +1 -1
- package/dist/esm/events/payloads.d.ts +41 -0
- package/dist/esm/events/registry.d.ts +3 -1
- package/dist/esm/events/registry.js +2 -0
- package/dist/esm/events/registry.js.map +1 -1
- package/dist/esm/identity/types.d.ts +7 -3
- package/dist/esm/identity/types.js.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +4 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/lib/boundedJson.d.ts +14 -7
- package/dist/esm/lib/boundedJson.js +14 -7
- package/dist/esm/lib/boundedJson.js.map +1 -1
- package/dist/esm/mcp/callChecks.d.ts +160 -0
- package/dist/esm/mcp/callChecks.js +237 -0
- package/dist/esm/mcp/callChecks.js.map +1 -0
- package/dist/esm/mcp/callLifetime.js +5 -2
- package/dist/esm/mcp/callLifetime.js.map +1 -1
- package/dist/esm/mcp/clientPort.d.ts +78 -0
- package/dist/esm/mcp/clientPort.js +33 -0
- package/dist/esm/mcp/clientPort.js.map +1 -0
- package/dist/esm/mcp/gatewayTransport.js +13 -3
- package/dist/esm/mcp/gatewayTransport.js.map +1 -1
- package/dist/esm/mcp/index.d.ts +11 -1
- package/dist/esm/mcp/index.js +15 -0
- package/dist/esm/mcp/index.js.map +1 -1
- package/dist/esm/mcp/listingGenerations.d.ts +54 -0
- package/dist/esm/mcp/listingGenerations.js +75 -0
- package/dist/esm/mcp/listingGenerations.js.map +1 -0
- package/dist/esm/mcp/mcpClient.d.ts +18 -1
- package/dist/esm/mcp/mcpClient.js +302 -57
- package/dist/esm/mcp/mcpClient.js.map +1 -1
- package/dist/esm/mcp/mockMcpClient.js +13 -0
- package/dist/esm/mcp/mockMcpClient.js.map +1 -1
- package/dist/esm/mcp/pool/boundTool.d.ts +58 -0
- package/dist/esm/mcp/pool/boundTool.js +218 -0
- package/dist/esm/mcp/pool/boundTool.js.map +1 -0
- package/dist/esm/mcp/pool/clientSet.d.ts +73 -0
- package/dist/esm/mcp/pool/clientSet.js +152 -0
- package/dist/esm/mcp/pool/clientSet.js.map +1 -0
- package/dist/esm/mcp/pool/definitions.d.ts +103 -0
- package/dist/esm/mcp/pool/definitions.js +168 -0
- package/dist/esm/mcp/pool/definitions.js.map +1 -0
- package/dist/esm/mcp/pool/errors.d.ts +76 -0
- package/dist/esm/mcp/pool/errors.js +91 -0
- package/dist/esm/mcp/pool/errors.js.map +1 -0
- package/dist/esm/mcp/pool/identityKey.d.ts +57 -0
- package/dist/esm/mcp/pool/identityKey.js +94 -0
- package/dist/esm/mcp/pool/identityKey.js.map +1 -0
- package/dist/esm/mcp/pool/lease.d.ts +36 -0
- package/dist/esm/mcp/pool/lease.js +51 -0
- package/dist/esm/mcp/pool/lease.js.map +1 -0
- package/dist/esm/mcp/pool/listTools.d.ts +33 -0
- package/dist/esm/mcp/pool/listTools.js +146 -0
- package/dist/esm/mcp/pool/listTools.js.map +1 -0
- package/dist/esm/mcp/pool/mcpClientPool.d.ts +121 -0
- package/dist/esm/mcp/pool/mcpClientPool.js +151 -0
- package/dist/esm/mcp/pool/mcpClientPool.js.map +1 -0
- package/dist/esm/mcp/pool/options.d.ts +183 -0
- package/dist/esm/mcp/pool/options.js +119 -0
- package/dist/esm/mcp/pool/options.js.map +1 -0
- package/dist/esm/mcp/pool/plan.d.ts +77 -0
- package/dist/esm/mcp/pool/plan.js +134 -0
- package/dist/esm/mcp/pool/plan.js.map +1 -0
- package/dist/esm/mcp/pool/pool.d.ts +307 -0
- package/dist/esm/mcp/pool/pool.js +1071 -0
- package/dist/esm/mcp/pool/pool.js.map +1 -0
- package/dist/esm/mcp/pool/pooledClient.d.ts +93 -0
- package/dist/esm/mcp/pool/pooledClient.js +90 -0
- package/dist/esm/mcp/pool/pooledClient.js.map +1 -0
- package/dist/esm/mcp/pool/record.d.ts +80 -0
- package/dist/esm/mcp/pool/record.js +75 -0
- package/dist/esm/mcp/pool/record.js.map +1 -0
- package/dist/esm/mcp/pool/sessionEnded.d.ts +24 -0
- package/dist/esm/mcp/pool/sessionEnded.js +31 -0
- package/dist/esm/mcp/pool/sessionEnded.js.map +1 -0
- package/dist/esm/mcp/pool/work.d.ts +116 -0
- package/dist/esm/mcp/pool/work.js +213 -0
- package/dist/esm/mcp/pool/work.js.map +1 -0
- package/dist/esm/mcp/requestGuard.d.ts +70 -0
- package/dist/esm/mcp/requestGuard.js +98 -0
- package/dist/esm/mcp/requestGuard.js.map +1 -0
- package/dist/esm/mcp/requestLedger.d.ts +35 -1
- package/dist/esm/mcp/requestLedger.js +76 -3
- package/dist/esm/mcp/requestLedger.js.map +1 -1
- package/dist/esm/mcp/seriesEvidence.d.ts +10 -6
- package/dist/esm/mcp/seriesEvidence.js +16 -7
- package/dist/esm/mcp/seriesEvidence.js.map +1 -1
- package/dist/esm/mcp/terminate.d.ts +94 -0
- package/dist/esm/mcp/terminate.js +184 -0
- package/dist/esm/mcp/terminate.js.map +1 -0
- package/dist/esm/mcp/throttleRetry.js +71 -7
- package/dist/esm/mcp/throttleRetry.js.map +1 -1
- package/dist/esm/mcp/toolList.d.ts +90 -0
- package/dist/esm/mcp/toolList.js +123 -0
- package/dist/esm/mcp/toolList.js.map +1 -0
- package/dist/esm/mcp/types.d.ts +197 -3
- package/dist/esm/redaction/knownStrings.js +34 -0
- package/dist/esm/redaction/knownStrings.js.map +1 -1
- package/dist/esm/tool-providers/index.d.ts +2 -2
- package/dist/esm/tool-providers/index.js +15 -1
- package/dist/esm/tool-providers/index.js.map +1 -1
- package/dist/esm/tools/mcpClientUse.d.ts +59 -0
- package/dist/esm/tools/mcpClientUse.js +216 -0
- package/dist/esm/tools/mcpClientUse.js.map +1 -0
- package/dist/esm/tools/toolProvider.d.ts +16 -0
- package/dist/esm/tools/toolSessions.d.ts +41 -0
- package/dist/esm/tools/toolSessions.js +41 -1
- package/dist/esm/tools/toolSessions.js.map +1 -1
- package/dist/events/registry.js +2 -0
- package/dist/events/registry.js.map +1 -1
- package/dist/identity/types.js.map +1 -1
- package/dist/index.js +6 -3
- package/dist/index.js.map +1 -1
- package/dist/lib/boundedJson.js +14 -7
- package/dist/lib/boundedJson.js.map +1 -1
- package/dist/mcp/callChecks.js +247 -0
- package/dist/mcp/callChecks.js.map +1 -0
- package/dist/mcp/callLifetime.js +5 -2
- package/dist/mcp/callLifetime.js.map +1 -1
- package/dist/mcp/clientPort.js +38 -0
- package/dist/mcp/clientPort.js.map +1 -0
- package/dist/mcp/gatewayTransport.js +13 -3
- package/dist/mcp/gatewayTransport.js.map +1 -1
- package/dist/mcp/index.js +25 -1
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/listingGenerations.js +80 -0
- package/dist/mcp/listingGenerations.js.map +1 -0
- package/dist/mcp/mcpClient.js +306 -59
- package/dist/mcp/mcpClient.js.map +1 -1
- package/dist/mcp/mockMcpClient.js +13 -0
- package/dist/mcp/mockMcpClient.js.map +1 -1
- package/dist/mcp/pool/boundTool.js +223 -0
- package/dist/mcp/pool/boundTool.js.map +1 -0
- package/dist/mcp/pool/clientSet.js +157 -0
- package/dist/mcp/pool/clientSet.js.map +1 -0
- package/dist/mcp/pool/definitions.js +174 -0
- package/dist/mcp/pool/definitions.js.map +1 -0
- package/dist/mcp/pool/errors.js +96 -0
- package/dist/mcp/pool/errors.js.map +1 -0
- package/dist/mcp/pool/identityKey.js +99 -0
- package/dist/mcp/pool/identityKey.js.map +1 -0
- package/dist/mcp/pool/lease.js +55 -0
- package/dist/mcp/pool/lease.js.map +1 -0
- package/dist/mcp/pool/listTools.js +150 -0
- package/dist/mcp/pool/listTools.js.map +1 -0
- package/dist/mcp/pool/mcpClientPool.js +155 -0
- package/dist/mcp/pool/mcpClientPool.js.map +1 -0
- package/dist/mcp/pool/options.js +123 -0
- package/dist/mcp/pool/options.js.map +1 -0
- package/dist/mcp/pool/plan.js +140 -0
- package/dist/mcp/pool/plan.js.map +1 -0
- package/dist/mcp/pool/pool.js +1075 -0
- package/dist/mcp/pool/pool.js.map +1 -0
- package/dist/mcp/pool/pooledClient.js +94 -0
- package/dist/mcp/pool/pooledClient.js.map +1 -0
- package/dist/mcp/pool/record.js +80 -0
- package/dist/mcp/pool/record.js.map +1 -0
- package/dist/mcp/pool/sessionEnded.js +35 -0
- package/dist/mcp/pool/sessionEnded.js.map +1 -0
- package/dist/mcp/pool/work.js +219 -0
- package/dist/mcp/pool/work.js.map +1 -0
- package/dist/mcp/requestGuard.js +106 -0
- package/dist/mcp/requestGuard.js.map +1 -0
- package/dist/mcp/requestLedger.js +76 -3
- package/dist/mcp/requestLedger.js.map +1 -1
- package/dist/mcp/seriesEvidence.js +16 -7
- package/dist/mcp/seriesEvidence.js.map +1 -1
- package/dist/mcp/terminate.js +193 -0
- package/dist/mcp/terminate.js.map +1 -0
- package/dist/mcp/throttleRetry.js +71 -7
- package/dist/mcp/throttleRetry.js.map +1 -1
- package/dist/mcp/toolList.js +128 -0
- package/dist/mcp/toolList.js.map +1 -0
- package/dist/redaction/knownStrings.js +34 -0
- package/dist/redaction/knownStrings.js.map +1 -1
- package/dist/tool-providers/index.js +20 -1
- package/dist/tool-providers/index.js.map +1 -1
- package/dist/tools/mcpClientUse.js +225 -0
- package/dist/tools/mcpClientUse.js.map +1 -0
- package/dist/tools/toolSessions.js +44 -1
- package/dist/tools/toolSessions.js.map +1 -1
- package/dist/types/adapters/mcp/agentcoreRuntime/invoke.d.ts +23 -4
- package/dist/types/adapters/mcp/agentcoreRuntime/invoke.d.ts.map +1 -1
- package/dist/types/adapters/mcp/agentcoreRuntime/session.d.ts +20 -6
- package/dist/types/adapters/mcp/agentcoreRuntime/session.d.ts.map +1 -1
- package/dist/types/adapters/mcp/agentcoreRuntime/transport.d.ts.map +1 -1
- package/dist/types/core/Agent.d.ts.map +1 -1
- package/dist/types/core/agent/stages/toolCalls.d.ts.map +1 -1
- package/dist/types/core/slots/buildToolsSlot.d.ts +12 -0
- package/dist/types/core/slots/buildToolsSlot.d.ts.map +1 -1
- package/dist/types/events/payloads.d.ts +41 -0
- package/dist/types/events/payloads.d.ts.map +1 -1
- package/dist/types/events/registry.d.ts +3 -1
- package/dist/types/events/registry.d.ts.map +1 -1
- package/dist/types/identity/types.d.ts +7 -3
- package/dist/types/identity/types.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/lib/boundedJson.d.ts +14 -7
- package/dist/types/lib/boundedJson.d.ts.map +1 -1
- package/dist/types/mcp/callChecks.d.ts +161 -0
- package/dist/types/mcp/callChecks.d.ts.map +1 -0
- package/dist/types/mcp/callLifetime.d.ts.map +1 -1
- package/dist/types/mcp/clientPort.d.ts +79 -0
- package/dist/types/mcp/clientPort.d.ts.map +1 -0
- package/dist/types/mcp/gatewayTransport.d.ts.map +1 -1
- package/dist/types/mcp/index.d.ts +11 -1
- package/dist/types/mcp/index.d.ts.map +1 -1
- package/dist/types/mcp/listingGenerations.d.ts +55 -0
- package/dist/types/mcp/listingGenerations.d.ts.map +1 -0
- package/dist/types/mcp/mcpClient.d.ts +18 -1
- package/dist/types/mcp/mcpClient.d.ts.map +1 -1
- package/dist/types/mcp/mockMcpClient.d.ts.map +1 -1
- package/dist/types/mcp/pool/boundTool.d.ts +59 -0
- package/dist/types/mcp/pool/boundTool.d.ts.map +1 -0
- package/dist/types/mcp/pool/clientSet.d.ts +74 -0
- package/dist/types/mcp/pool/clientSet.d.ts.map +1 -0
- package/dist/types/mcp/pool/definitions.d.ts +104 -0
- package/dist/types/mcp/pool/definitions.d.ts.map +1 -0
- package/dist/types/mcp/pool/errors.d.ts +77 -0
- package/dist/types/mcp/pool/errors.d.ts.map +1 -0
- package/dist/types/mcp/pool/identityKey.d.ts +58 -0
- package/dist/types/mcp/pool/identityKey.d.ts.map +1 -0
- package/dist/types/mcp/pool/lease.d.ts +37 -0
- package/dist/types/mcp/pool/lease.d.ts.map +1 -0
- package/dist/types/mcp/pool/listTools.d.ts +34 -0
- package/dist/types/mcp/pool/listTools.d.ts.map +1 -0
- package/dist/types/mcp/pool/mcpClientPool.d.ts +122 -0
- package/dist/types/mcp/pool/mcpClientPool.d.ts.map +1 -0
- package/dist/types/mcp/pool/options.d.ts +184 -0
- package/dist/types/mcp/pool/options.d.ts.map +1 -0
- package/dist/types/mcp/pool/plan.d.ts +78 -0
- package/dist/types/mcp/pool/plan.d.ts.map +1 -0
- package/dist/types/mcp/pool/pool.d.ts +308 -0
- package/dist/types/mcp/pool/pool.d.ts.map +1 -0
- package/dist/types/mcp/pool/pooledClient.d.ts +94 -0
- package/dist/types/mcp/pool/pooledClient.d.ts.map +1 -0
- package/dist/types/mcp/pool/record.d.ts +81 -0
- package/dist/types/mcp/pool/record.d.ts.map +1 -0
- package/dist/types/mcp/pool/sessionEnded.d.ts +25 -0
- package/dist/types/mcp/pool/sessionEnded.d.ts.map +1 -0
- package/dist/types/mcp/pool/work.d.ts +117 -0
- package/dist/types/mcp/pool/work.d.ts.map +1 -0
- package/dist/types/mcp/requestGuard.d.ts +71 -0
- package/dist/types/mcp/requestGuard.d.ts.map +1 -0
- package/dist/types/mcp/requestLedger.d.ts +35 -1
- package/dist/types/mcp/requestLedger.d.ts.map +1 -1
- package/dist/types/mcp/seriesEvidence.d.ts +10 -6
- package/dist/types/mcp/seriesEvidence.d.ts.map +1 -1
- package/dist/types/mcp/terminate.d.ts +95 -0
- package/dist/types/mcp/terminate.d.ts.map +1 -0
- package/dist/types/mcp/throttleRetry.d.ts.map +1 -1
- package/dist/types/mcp/toolList.d.ts +91 -0
- package/dist/types/mcp/toolList.d.ts.map +1 -0
- package/dist/types/mcp/types.d.ts +197 -3
- package/dist/types/mcp/types.d.ts.map +1 -1
- package/dist/types/redaction/knownStrings.d.ts.map +1 -1
- package/dist/types/tool-providers/index.d.ts +2 -2
- package/dist/types/tool-providers/index.d.ts.map +1 -1
- package/dist/types/tools/mcpClientUse.d.ts +60 -0
- package/dist/types/tools/mcpClientUse.d.ts.map +1 -0
- package/dist/types/tools/toolProvider.d.ts +16 -0
- package/dist/types/tools/toolProvider.d.ts.map +1 -1
- package/dist/types/tools/toolSessions.d.ts +41 -0
- package/dist/types/tools/toolSessions.d.ts.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* boundTool — a tool bound to its conversation, the list it came from and that
|
|
3
|
+
* list's generation, re-checked after every wait and at the send gate.
|
|
4
|
+
*
|
|
5
|
+
* Pattern: the shape every MCP tool takes (`mcpClient.ts` · `mcpToolShape`) +
|
|
6
|
+
* one `execute` that runs the five steps of § 6.3.
|
|
7
|
+
* Role: A bound tool carries its BINDING: the key it was listed for; the
|
|
8
|
+
* context it was listed in (per client, the configuration; shared, the
|
|
9
|
+
* entry's context); the list it came from (per client, the client
|
|
10
|
+
* incarnation; shared, the entry); and that list's generation. Its
|
|
11
|
+
* `execute`:
|
|
12
|
+
*
|
|
13
|
+
* 1. checks that the calling run's caller identity encodes to its key —
|
|
14
|
+
* otherwise `identity-mismatch`, nothing sent;
|
|
15
|
+
* 2. checks its binding is current — otherwise `stale-tools`, nothing sent;
|
|
16
|
+
* 3. is admitted against `maxOperations` and takes a lease on its key's
|
|
17
|
+
* client, opening one from a plan resolved now when the key has none. A
|
|
18
|
+
* client whose plan's context differs from the binding's refuses with
|
|
19
|
+
* `binding-changed` (the client stays for the next list). A client that is
|
|
20
|
+
* not the incarnation the tool was listed from is checked against its OWN
|
|
21
|
+
* fresh list — the call goes on only if that list holds the same
|
|
22
|
+
* definition, compared as canonical JSON; otherwise `stale-tools`. Under
|
|
23
|
+
* the app's sharing contract, a replacement whose context matches uses the
|
|
24
|
+
* shared entry as it stands;
|
|
25
|
+
* 4. after every wait, checks step 2 again — and asks the app's `share`
|
|
26
|
+
* again, so a changed authorization name refuses it (`binding-changed`);
|
|
27
|
+
* 5. sends through the send gate (`mcp/requestGuard.ts`), asked at every
|
|
28
|
+
* boundary the request crosses — before every attempt, after a credential
|
|
29
|
+
* arrives, after signing — classifies the outcome (a server's 404 for the
|
|
30
|
+
* session is `SessionExpiredError`; a handle this process lost is
|
|
31
|
+
* `LocalSessionHandleLostError`; neither is repeated), files its use, and
|
|
32
|
+
* releases its lease once the request's own work has settled.
|
|
33
|
+
*
|
|
34
|
+
* Never another conversation's connection: the binding is to the KEY, and a
|
|
35
|
+
* client of any other key never serves it.
|
|
36
|
+
*/
|
|
37
|
+
import type { Tool } from '../../tools/tools.js';
|
|
38
|
+
import type { Definition } from './definitions.js';
|
|
39
|
+
import { type PoolKey } from './identityKey.js';
|
|
40
|
+
import type { Pool } from './pool.js';
|
|
41
|
+
import { UseFacts } from './record.js';
|
|
42
|
+
/** What a bound tool was listed under. */
|
|
43
|
+
export interface Binding {
|
|
44
|
+
readonly key: PoolKey;
|
|
45
|
+
/** The definitions context: `pc:<configHash>` per client, `sh:<context>` shared. */
|
|
46
|
+
readonly context: string;
|
|
47
|
+
readonly shared: boolean;
|
|
48
|
+
/** The list it came from: an incarnation's own, or a shared entry's. */
|
|
49
|
+
readonly listId: string;
|
|
50
|
+
readonly generation: number;
|
|
51
|
+
/** Per client: the incarnation it was listed from. */
|
|
52
|
+
readonly clientId?: string;
|
|
53
|
+
readonly definition: Definition;
|
|
54
|
+
}
|
|
55
|
+
/** A callable tool for `binding`, shaped as every MCP tool is. */
|
|
56
|
+
export declare function boundTool(pool: Pool, binding: Binding): Tool;
|
|
57
|
+
/** A refusal's word on the record. */
|
|
58
|
+
export declare function noteRefusal(facts: UseFacts, error: unknown): void;
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* boundTool — a tool bound to its conversation, the list it came from and that
|
|
3
|
+
* list's generation, re-checked after every wait and at the send gate.
|
|
4
|
+
*
|
|
5
|
+
* Pattern: the shape every MCP tool takes (`mcpClient.ts` · `mcpToolShape`) +
|
|
6
|
+
* one `execute` that runs the five steps of § 6.3.
|
|
7
|
+
* Role: A bound tool carries its BINDING: the key it was listed for; the
|
|
8
|
+
* context it was listed in (per client, the configuration; shared, the
|
|
9
|
+
* entry's context); the list it came from (per client, the client
|
|
10
|
+
* incarnation; shared, the entry); and that list's generation. Its
|
|
11
|
+
* `execute`:
|
|
12
|
+
*
|
|
13
|
+
* 1. checks that the calling run's caller identity encodes to its key —
|
|
14
|
+
* otherwise `identity-mismatch`, nothing sent;
|
|
15
|
+
* 2. checks its binding is current — otherwise `stale-tools`, nothing sent;
|
|
16
|
+
* 3. is admitted against `maxOperations` and takes a lease on its key's
|
|
17
|
+
* client, opening one from a plan resolved now when the key has none. A
|
|
18
|
+
* client whose plan's context differs from the binding's refuses with
|
|
19
|
+
* `binding-changed` (the client stays for the next list). A client that is
|
|
20
|
+
* not the incarnation the tool was listed from is checked against its OWN
|
|
21
|
+
* fresh list — the call goes on only if that list holds the same
|
|
22
|
+
* definition, compared as canonical JSON; otherwise `stale-tools`. Under
|
|
23
|
+
* the app's sharing contract, a replacement whose context matches uses the
|
|
24
|
+
* shared entry as it stands;
|
|
25
|
+
* 4. after every wait, checks step 2 again — and asks the app's `share`
|
|
26
|
+
* again, so a changed authorization name refuses it (`binding-changed`);
|
|
27
|
+
* 5. sends through the send gate (`mcp/requestGuard.ts`), asked at every
|
|
28
|
+
* boundary the request crosses — before every attempt, after a credential
|
|
29
|
+
* arrives, after signing — classifies the outcome (a server's 404 for the
|
|
30
|
+
* session is `SessionExpiredError`; a handle this process lost is
|
|
31
|
+
* `LocalSessionHandleLostError`; neither is repeated), files its use, and
|
|
32
|
+
* releases its lease once the request's own work has settled.
|
|
33
|
+
*
|
|
34
|
+
* Never another conversation's connection: the binding is to the KEY, and a
|
|
35
|
+
* client of any other key never serves it.
|
|
36
|
+
*/
|
|
37
|
+
import { mcpClientUseOf } from '../../tools/mcpClientUse.js';
|
|
38
|
+
import { LocalSessionHandleLostError } from '../../tools/toolSessions.js';
|
|
39
|
+
import { ServerEndedSessionError } from './sessionEnded.js';
|
|
40
|
+
import { mcpToolShape } from '../mcpClient.js';
|
|
41
|
+
import { withCallGuard } from '../requestGuard.js';
|
|
42
|
+
import { McpPoolRefusedError, PoolFullError } from './errors.js';
|
|
43
|
+
import { poolKeyOf } from './identityKey.js';
|
|
44
|
+
import { UseFacts } from './record.js';
|
|
45
|
+
/** A callable tool for `binding`, shaped as every MCP tool is. */
|
|
46
|
+
export function boundTool(pool, binding) {
|
|
47
|
+
return {
|
|
48
|
+
...mcpToolShape(pool.settings.name, binding.definition.listed),
|
|
49
|
+
execute: (args, ctx) => executeBound(pool, binding, args, ctx),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/** The five steps. */
|
|
53
|
+
async function executeBound(pool, binding, args, ctx) {
|
|
54
|
+
const facts = new UseFacts(pool.settings.name, 'call');
|
|
55
|
+
facts.keyHash = binding.key.keyHash;
|
|
56
|
+
let operation;
|
|
57
|
+
try {
|
|
58
|
+
// 1 — the calling run's caller identity, encoded the one way.
|
|
59
|
+
const caller = poolKeyOf(ctx?.identity, { requirePrincipal: false, requireTenant: false });
|
|
60
|
+
if (typeof caller === 'string' || caller.key !== binding.key.key) {
|
|
61
|
+
throw pool.refused('identity-mismatch');
|
|
62
|
+
}
|
|
63
|
+
pool.interact();
|
|
64
|
+
// 2 — the binding, before anything is admitted.
|
|
65
|
+
if (beforeLease(pool, binding) === 'stale')
|
|
66
|
+
throw pool.refused('stale-tools');
|
|
67
|
+
// 3 — admitted, leased, opened when the key has none; the app's names asked
|
|
68
|
+
// again, so a changed authorization refuses what was listed under the old.
|
|
69
|
+
operation = pool.admit('call', 'call', ctx?.signal);
|
|
70
|
+
const client = await pool.leaseClient(binding.key, operation, facts);
|
|
71
|
+
if (pool.refreshContext(client).context !== binding.context) {
|
|
72
|
+
throw pool.refused('binding-changed');
|
|
73
|
+
}
|
|
74
|
+
const { inner, effective } = await toolOn(pool, binding, client, operation, facts);
|
|
75
|
+
// 4 — after every wait, the binding again.
|
|
76
|
+
operation.check();
|
|
77
|
+
checkHolds(pool, client, effective);
|
|
78
|
+
// 5 — the send gate, asked at every boundary the request crosses.
|
|
79
|
+
operation.guardWith(() => checkHolds(pool, client, effective));
|
|
80
|
+
operation.phase = 'call';
|
|
81
|
+
const callCtx = withCallGuard({ ...(ctx ?? {}), signal: operation.signal }, operation);
|
|
82
|
+
try {
|
|
83
|
+
return await inner.execute(args, callCtx);
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
throw classified(pool, client, operation, facts, error);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
noteRefusal(facts, error);
|
|
91
|
+
throw error;
|
|
92
|
+
}
|
|
93
|
+
finally {
|
|
94
|
+
operation?.finish();
|
|
95
|
+
mcpClientUseOf(ctx)?.file(facts.toUse());
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/** The tool to run on `client`, and the binding the send gate checks it against. */
|
|
99
|
+
async function toolOn(pool, binding, client, operation, facts) {
|
|
100
|
+
const name = binding.definition.listed.name;
|
|
101
|
+
const port = client.port;
|
|
102
|
+
if (port === undefined)
|
|
103
|
+
throw pool.refused('init-failed');
|
|
104
|
+
if (binding.shared) {
|
|
105
|
+
// The shared entry as it stands: a definition this client may never have
|
|
106
|
+
// listed, so the tool carries its own checks (`ClientPort.bind`).
|
|
107
|
+
return { inner: port.bind(binding.definition.listed), effective: binding };
|
|
108
|
+
}
|
|
109
|
+
if (client.clientId === binding.clientId) {
|
|
110
|
+
const own = client.list.tool(name);
|
|
111
|
+
if (own === undefined)
|
|
112
|
+
throw pool.refused('stale-tools');
|
|
113
|
+
return { inner: own, effective: binding };
|
|
114
|
+
}
|
|
115
|
+
// A replacement: its own fresh list, under the context of now, then the
|
|
116
|
+
// same definition or nothing.
|
|
117
|
+
const listed = await pool.ensureListed(client, operation);
|
|
118
|
+
if (listed.context !== binding.context)
|
|
119
|
+
throw pool.refused('binding-changed');
|
|
120
|
+
const fresh = listed.list.definition(name);
|
|
121
|
+
const own = listed.list.tool(name);
|
|
122
|
+
if (fresh === undefined ||
|
|
123
|
+
own === undefined ||
|
|
124
|
+
fresh.canonical !== binding.definition.canonical) {
|
|
125
|
+
facts.revalidated = 'refused';
|
|
126
|
+
throw pool.refused('stale-tools');
|
|
127
|
+
}
|
|
128
|
+
facts.revalidated = 'kept';
|
|
129
|
+
return {
|
|
130
|
+
inner: own,
|
|
131
|
+
effective: {
|
|
132
|
+
...binding,
|
|
133
|
+
clientId: client.clientId,
|
|
134
|
+
listId: listed.list.id,
|
|
135
|
+
generation: listed.list.generation,
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Step 2, before a lease: a shared binding against its entry; a per-client
|
|
141
|
+
* binding against its incarnation when that incarnation is still the key's
|
|
142
|
+
* current client. An incarnation that is gone is not stale yet — step 3 checks
|
|
143
|
+
* the replacement's own list.
|
|
144
|
+
*/
|
|
145
|
+
function beforeLease(pool, binding) {
|
|
146
|
+
if (binding.shared)
|
|
147
|
+
return sharedHolds(pool, binding) ? 'ok' : 'stale';
|
|
148
|
+
const current = pool.clients.currentFor(binding.key.key);
|
|
149
|
+
if (current === undefined || current.clientId !== binding.clientId)
|
|
150
|
+
return 'ok';
|
|
151
|
+
return listHolds(current, binding) ? 'ok' : 'stale';
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* The binding holds on `client` — the one the call runs on: the app's names
|
|
155
|
+
* still give its context, and its list has not moved. Otherwise the refusal:
|
|
156
|
+
* `binding-changed` for a changed context, `stale-tools` for a moved list.
|
|
157
|
+
*/
|
|
158
|
+
function checkHolds(pool, client, binding) {
|
|
159
|
+
if (!pool.contextHolds(client, binding.context))
|
|
160
|
+
throw pool.refused('binding-changed');
|
|
161
|
+
const holds = binding.shared ? sharedHolds(pool, binding) : listHolds(client, binding);
|
|
162
|
+
if (!holds)
|
|
163
|
+
throw pool.refused('stale-tools');
|
|
164
|
+
}
|
|
165
|
+
function sharedHolds(pool, binding) {
|
|
166
|
+
const entry = pool.shared.peek(binding.context);
|
|
167
|
+
return (entry !== undefined &&
|
|
168
|
+
entry.list.id === binding.listId &&
|
|
169
|
+
entry.list.generation === binding.generation);
|
|
170
|
+
}
|
|
171
|
+
function listHolds(client, binding) {
|
|
172
|
+
return client.list.id === binding.listId && client.list.generation === binding.generation;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* What the call failed with, as the pool read it: a server's 404 for the
|
|
176
|
+
* session it issued is `SessionExpiredError`; a handle this process lost is
|
|
177
|
+
* `LocalSessionHandleLostError` (the transport's own object). Either way the
|
|
178
|
+
* client leaves its key, and the call is not repeated. Anything else is the
|
|
179
|
+
* error as it came.
|
|
180
|
+
*/
|
|
181
|
+
function classified(pool, client, operation, facts, error) {
|
|
182
|
+
const lost = pool.classifyFailure(client, operation, error);
|
|
183
|
+
if (lost === 'expired') {
|
|
184
|
+
facts.client = 'expired';
|
|
185
|
+
facts.evidence = 'server-404';
|
|
186
|
+
return new ServerEndedSessionError(pool.settings.name, client.clientId, error);
|
|
187
|
+
}
|
|
188
|
+
if (lost === 'handle-lost') {
|
|
189
|
+
facts.client = 'handle-lost';
|
|
190
|
+
facts.evidence = 'transport-forgot-handle';
|
|
191
|
+
return error instanceof LocalSessionHandleLostError
|
|
192
|
+
? error
|
|
193
|
+
: new LocalSessionHandleLostError({ cause: error });
|
|
194
|
+
}
|
|
195
|
+
return error;
|
|
196
|
+
}
|
|
197
|
+
/** A refusal's word on the record. */
|
|
198
|
+
export function noteRefusal(facts, error) {
|
|
199
|
+
if (error instanceof McpPoolRefusedError) {
|
|
200
|
+
facts.refusal = error.reason;
|
|
201
|
+
}
|
|
202
|
+
else if (error instanceof PoolFullError) {
|
|
203
|
+
facts.refusal = LIMIT_WORDS[error.limit];
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
// No client was touched: the pool refused to give one.
|
|
209
|
+
if (facts.clientId === undefined)
|
|
210
|
+
facts.client = 'refused';
|
|
211
|
+
}
|
|
212
|
+
const LIMIT_WORDS = {
|
|
213
|
+
pool: 'pool-full',
|
|
214
|
+
principal: 'principal-full',
|
|
215
|
+
ending: 'ending-full',
|
|
216
|
+
operations: 'operations-full',
|
|
217
|
+
};
|
|
218
|
+
//# sourceMappingURL=boundTool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boundTool.js","sourceRoot":"","sources":["../../../../src/mcp/pool/boundTool.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* clientSet — which clients the pool holds, and in which of its two sets.
|
|
3
|
+
*
|
|
4
|
+
* Pattern: two sets and their indexes, mutated only together.
|
|
5
|
+
* Role: § 5.3 of the design. The OPEN set holds every client that counts
|
|
6
|
+
* toward `maxClients` — opening, busy, idle, deferred, or an abandoned
|
|
7
|
+
* open that has not settled. The ENDING set holds every client from the
|
|
8
|
+
* decision to end it until that ending's work has settled, and counts
|
|
9
|
+
* toward `maxEnding`. A client leaves the open set only into the ending
|
|
10
|
+
* set — a lost handle too: its ending is its local close — or, when its
|
|
11
|
+
* open failed and left nothing to end, by being dropped. So the pool
|
|
12
|
+
* holds at most `maxClients + maxEnding` client objects.
|
|
13
|
+
*
|
|
14
|
+
* Each key has at most one CURRENT client — the one new leases go to. A client
|
|
15
|
+
* named by `terminate`, or whose session expired, stops being current at once
|
|
16
|
+
* while it stays in the open set, counted, until it can end.
|
|
17
|
+
*
|
|
18
|
+
* What the pool remembers of a key after its client left — the incarnation
|
|
19
|
+
* count and why the last one left — is bounded (`KEY_MEMORY`), least recently
|
|
20
|
+
* used forgotten first: after that a key's count starts again at 1. The record
|
|
21
|
+
* reads it; no binding depends on it.
|
|
22
|
+
*/
|
|
23
|
+
import type { EndReason, PooledClient } from './pooledClient.js';
|
|
24
|
+
/** How many keys' incarnation counts the pool remembers. */
|
|
25
|
+
export declare const KEY_MEMORY = 4096;
|
|
26
|
+
/** Why a key's previous client left, as the record names it. */
|
|
27
|
+
export type PreviousEnd = 'idle' | 'evicted' | 'expired' | 'handle-lost' | 'terminated';
|
|
28
|
+
export declare class ClientSet {
|
|
29
|
+
private readonly open;
|
|
30
|
+
private readonly ending;
|
|
31
|
+
private readonly current;
|
|
32
|
+
private readonly perPrincipal;
|
|
33
|
+
private readonly memory;
|
|
34
|
+
/** Clients in the open set. */
|
|
35
|
+
get openCount(): number;
|
|
36
|
+
/** Clients in the ending set. */
|
|
37
|
+
get endingCount(): number;
|
|
38
|
+
/** Every client in the open set. */
|
|
39
|
+
openClients(): readonly PooledClient[];
|
|
40
|
+
/** Clients of the open set that count for `principal`. */
|
|
41
|
+
principalCount(principal: string): number;
|
|
42
|
+
/** The key's current client — opening or open, taking new leases — if it has one. */
|
|
43
|
+
currentFor(key: string): PooledClient | undefined;
|
|
44
|
+
/** The next incarnation number for `key`, and why its previous client left. */
|
|
45
|
+
nextIncarnation(key: string): {
|
|
46
|
+
readonly generation: number;
|
|
47
|
+
readonly previous?: PreviousEnd;
|
|
48
|
+
};
|
|
49
|
+
/** A new client joins the open set as its key's current client. */
|
|
50
|
+
admit(client: PooledClient): void;
|
|
51
|
+
/**
|
|
52
|
+
* The client stops taking new leases: its key has no current client until the
|
|
53
|
+
* next open, and that open's record says `why`. A client that is no longer
|
|
54
|
+
* its key's current one has already left it, so nothing is recorded again —
|
|
55
|
+
* a later client of the key keeps its own reason.
|
|
56
|
+
*/
|
|
57
|
+
retire(client: PooledClient, why: PreviousEnd): void;
|
|
58
|
+
/** The client moves from the open set into the ending set. */
|
|
59
|
+
moveToEnding(client: PooledClient, why: PreviousEnd | undefined): void;
|
|
60
|
+
/** The client's ending settled: it leaves the ending set. */
|
|
61
|
+
ended(client: PooledClient): void;
|
|
62
|
+
/** The client leaves the open set without ending — a failed open, a lost handle. */
|
|
63
|
+
drop(client: PooledClient, why?: PreviousEnd): void;
|
|
64
|
+
/**
|
|
65
|
+
* The least recently used idle client of the open set — among those `pick`
|
|
66
|
+
* accepts — or `undefined` when every one is busy.
|
|
67
|
+
*/
|
|
68
|
+
leastRecentlyUsedIdle(pick: (client: PooledClient) => boolean): PooledClient | undefined;
|
|
69
|
+
private leaveOpen;
|
|
70
|
+
private remember;
|
|
71
|
+
}
|
|
72
|
+
/** The record's `previous` for an end reason, when it names one. */
|
|
73
|
+
export declare function previousOf(reason: EndReason): PreviousEnd | undefined;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* clientSet — which clients the pool holds, and in which of its two sets.
|
|
3
|
+
*
|
|
4
|
+
* Pattern: two sets and their indexes, mutated only together.
|
|
5
|
+
* Role: § 5.3 of the design. The OPEN set holds every client that counts
|
|
6
|
+
* toward `maxClients` — opening, busy, idle, deferred, or an abandoned
|
|
7
|
+
* open that has not settled. The ENDING set holds every client from the
|
|
8
|
+
* decision to end it until that ending's work has settled, and counts
|
|
9
|
+
* toward `maxEnding`. A client leaves the open set only into the ending
|
|
10
|
+
* set — a lost handle too: its ending is its local close — or, when its
|
|
11
|
+
* open failed and left nothing to end, by being dropped. So the pool
|
|
12
|
+
* holds at most `maxClients + maxEnding` client objects.
|
|
13
|
+
*
|
|
14
|
+
* Each key has at most one CURRENT client — the one new leases go to. A client
|
|
15
|
+
* named by `terminate`, or whose session expired, stops being current at once
|
|
16
|
+
* while it stays in the open set, counted, until it can end.
|
|
17
|
+
*
|
|
18
|
+
* What the pool remembers of a key after its client left — the incarnation
|
|
19
|
+
* count and why the last one left — is bounded (`KEY_MEMORY`), least recently
|
|
20
|
+
* used forgotten first: after that a key's count starts again at 1. The record
|
|
21
|
+
* reads it; no binding depends on it.
|
|
22
|
+
*/
|
|
23
|
+
/** How many keys' incarnation counts the pool remembers. */
|
|
24
|
+
export const KEY_MEMORY = 4096;
|
|
25
|
+
export class ClientSet {
|
|
26
|
+
open = new Set();
|
|
27
|
+
ending = new Set();
|
|
28
|
+
current = new Map();
|
|
29
|
+
perPrincipal = new Map();
|
|
30
|
+
memory = new Map();
|
|
31
|
+
/** Clients in the open set. */
|
|
32
|
+
get openCount() {
|
|
33
|
+
return this.open.size;
|
|
34
|
+
}
|
|
35
|
+
/** Clients in the ending set. */
|
|
36
|
+
get endingCount() {
|
|
37
|
+
return this.ending.size;
|
|
38
|
+
}
|
|
39
|
+
/** Every client in the open set. */
|
|
40
|
+
openClients() {
|
|
41
|
+
return [...this.open];
|
|
42
|
+
}
|
|
43
|
+
/** Clients of the open set that count for `principal`. */
|
|
44
|
+
principalCount(principal) {
|
|
45
|
+
return this.perPrincipal.get(principal) ?? 0;
|
|
46
|
+
}
|
|
47
|
+
/** The key's current client — opening or open, taking new leases — if it has one. */
|
|
48
|
+
currentFor(key) {
|
|
49
|
+
return this.current.get(key);
|
|
50
|
+
}
|
|
51
|
+
/** The next incarnation number for `key`, and why its previous client left. */
|
|
52
|
+
nextIncarnation(key) {
|
|
53
|
+
const remembered = this.remember(key);
|
|
54
|
+
remembered.incarnations += 1;
|
|
55
|
+
const previous = remembered.previous;
|
|
56
|
+
remembered.previous = undefined;
|
|
57
|
+
return { generation: remembered.incarnations, ...(previous !== undefined && { previous }) };
|
|
58
|
+
}
|
|
59
|
+
/** A new client joins the open set as its key's current client. */
|
|
60
|
+
admit(client) {
|
|
61
|
+
this.open.add(client);
|
|
62
|
+
this.current.set(client.key.key, client);
|
|
63
|
+
this.perPrincipal.set(client.key.principal, this.principalCount(client.key.principal) + 1);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The client stops taking new leases: its key has no current client until the
|
|
67
|
+
* next open, and that open's record says `why`. A client that is no longer
|
|
68
|
+
* its key's current one has already left it, so nothing is recorded again —
|
|
69
|
+
* a later client of the key keeps its own reason.
|
|
70
|
+
*/
|
|
71
|
+
retire(client, why) {
|
|
72
|
+
if (this.current.get(client.key.key) !== client)
|
|
73
|
+
return;
|
|
74
|
+
this.current.delete(client.key.key);
|
|
75
|
+
this.remember(client.key.key).previous = why;
|
|
76
|
+
}
|
|
77
|
+
/** The client moves from the open set into the ending set. */
|
|
78
|
+
moveToEnding(client, why) {
|
|
79
|
+
if (why !== undefined)
|
|
80
|
+
this.retire(client, why);
|
|
81
|
+
else if (this.current.get(client.key.key) === client)
|
|
82
|
+
this.current.delete(client.key.key);
|
|
83
|
+
this.leaveOpen(client);
|
|
84
|
+
this.ending.add(client);
|
|
85
|
+
}
|
|
86
|
+
/** The client's ending settled: it leaves the ending set. */
|
|
87
|
+
ended(client) {
|
|
88
|
+
this.ending.delete(client);
|
|
89
|
+
}
|
|
90
|
+
/** The client leaves the open set without ending — a failed open, a lost handle. */
|
|
91
|
+
drop(client, why) {
|
|
92
|
+
if (why !== undefined)
|
|
93
|
+
this.retire(client, why);
|
|
94
|
+
else if (this.current.get(client.key.key) === client)
|
|
95
|
+
this.current.delete(client.key.key);
|
|
96
|
+
this.leaveOpen(client);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* The least recently used idle client of the open set — among those `pick`
|
|
100
|
+
* accepts — or `undefined` when every one is busy.
|
|
101
|
+
*/
|
|
102
|
+
leastRecentlyUsedIdle(pick) {
|
|
103
|
+
let oldest;
|
|
104
|
+
for (const client of this.open) {
|
|
105
|
+
if (client.busy || client.state !== 'open' || !pick(client))
|
|
106
|
+
continue;
|
|
107
|
+
if (oldest === undefined || client.usedAt < oldest.usedAt)
|
|
108
|
+
oldest = client;
|
|
109
|
+
}
|
|
110
|
+
return oldest;
|
|
111
|
+
}
|
|
112
|
+
leaveOpen(client) {
|
|
113
|
+
if (!this.open.delete(client))
|
|
114
|
+
return;
|
|
115
|
+
const left = this.principalCount(client.key.principal) - 1;
|
|
116
|
+
if (left > 0)
|
|
117
|
+
this.perPrincipal.set(client.key.principal, left);
|
|
118
|
+
else
|
|
119
|
+
this.perPrincipal.delete(client.key.principal);
|
|
120
|
+
}
|
|
121
|
+
remember(key) {
|
|
122
|
+
let remembered = this.memory.get(key);
|
|
123
|
+
if (remembered !== undefined)
|
|
124
|
+
this.memory.delete(key);
|
|
125
|
+
else
|
|
126
|
+
remembered = { incarnations: 0, previous: undefined };
|
|
127
|
+
this.memory.set(key, remembered);
|
|
128
|
+
while (this.memory.size > KEY_MEMORY) {
|
|
129
|
+
const oldest = this.memory.keys().next().value;
|
|
130
|
+
if (oldest === undefined)
|
|
131
|
+
break;
|
|
132
|
+
this.memory.delete(oldest);
|
|
133
|
+
}
|
|
134
|
+
return remembered;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/** The record's `previous` for an end reason, when it names one. */
|
|
138
|
+
export function previousOf(reason) {
|
|
139
|
+
switch (reason) {
|
|
140
|
+
case 'idle':
|
|
141
|
+
case 'evicted':
|
|
142
|
+
case 'expired':
|
|
143
|
+
case 'handle-lost':
|
|
144
|
+
return reason;
|
|
145
|
+
case 'terminated':
|
|
146
|
+
case 'closed':
|
|
147
|
+
return 'terminated';
|
|
148
|
+
case 'abandoned-open':
|
|
149
|
+
return undefined;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=clientSet.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clientSet.js","sourceRoot":"","sources":["../../../../src/mcp/pool/clientSet.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAIH,4DAA4D;AAC5D,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,CAAC;AAW/B,MAAM,OAAO,SAAS;IACH,IAAI,GAAG,IAAI,GAAG,EAAgB,CAAC;IAC/B,MAAM,GAAG,IAAI,GAAG,EAAgB,CAAC;IACjC,OAAO,GAAG,IAAI,GAAG,EAAwB,CAAC;IAC1C,YAAY,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,MAAM,GAAG,IAAI,GAAG,EAAqB,CAAC;IAEvD,+BAA+B;IAC/B,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;IACxB,CAAC;IAED,iCAAiC;IACjC,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;IAC1B,CAAC;IAED,oCAAoC;IACpC,WAAW;QACT,OAAO,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC;IAED,0DAA0D;IAC1D,cAAc,CAAC,SAAiB;QAC9B,OAAO,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAC/C,CAAC;IAED,qFAAqF;IACrF,UAAU,CAAC,GAAW;QACpB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC/B,CAAC;IAED,+EAA+E;IAC/E,eAAe,CAAC,GAAW;QACzB,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QACtC,UAAU,CAAC,YAAY,IAAI,CAAC,CAAC;QAC7B,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,CAAC;QACrC,UAAU,CAAC,QAAQ,GAAG,SAAS,CAAC;QAChC,OAAO,EAAE,UAAU,EAAE,UAAU,CAAC,YAAY,EAAE,GAAG,CAAC,QAAQ,KAAK,SAAS,IAAI,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;IAC9F,CAAC;IAED,mEAAmE;IACnE,KAAK,CAAC,MAAoB;QACxB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACtB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QACzC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;IAC7F,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,MAAoB,EAAE,GAAgB;QAC3C,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,MAAM;YAAE,OAAO;QACxD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACpC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,GAAG,GAAG,CAAC;IAC/C,CAAC;IAED,8DAA8D;IAC9D,YAAY,CAAC,MAAoB,EAAE,GAA4B;QAC7D,IAAI,GAAG,KAAK,SAAS;YAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;aAC3C,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,MAAM;YAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC1F,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QACvB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC1B,CAAC;IAED,6DAA6D;IAC7D,KAAK,CAAC,MAAoB;QACxB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC7B,CAAC;IAED,oFAAoF;IACpF,IAAI,CAAC,MAAoB,EAAE,GAAiB;QAC1C,IAAI,GAAG,KAAK,SAAS;YAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;aAC3C,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,MAAM;YAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC1F,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACzB,CAAC;IAED;;;OAGG;IACH,qBAAqB,CAAC,IAAuC;QAC3D,IAAI,MAAgC,CAAC;QACrC,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YAC/B,IAAI,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,KAAK,KAAK,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;gBAAE,SAAS;YACtE,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM;gBAAE,MAAM,GAAG,MAAM,CAAC;QAC7E,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAEO,SAAS,CAAC,MAAoB;QACpC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;YAAE,OAAO;QACtC,MAAM,IAAI,GAAG,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QAC3D,IAAI,IAAI,GAAG,CAAC;YAAE,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;;YAC3D,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACtD,CAAC;IAEO,QAAQ,CAAC,GAAW;QAC1B,IAAI,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACtC,IAAI,UAAU,KAAK,SAAS;YAAE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;;YACjD,UAAU,GAAG,EAAE,YAAY,EAAE,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;QAC3D,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;QACjC,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,GAAG,UAAU,EAAE,CAAC;YACrC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;YAC/C,IAAI,MAAM,KAAK,SAAS;gBAAE,MAAM;YAChC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC7B,CAAC;QACD,OAAO,UAAU,CAAC;IACpB,CAAC;CACF;AAED,oEAAoE;AACpE,MAAM,UAAU,UAAU,CAAC,MAAiB;IAC1C,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,MAAM,CAAC;QACZ,KAAK,SAAS,CAAC;QACf,KAAK,SAAS,CAAC;QACf,KAAK,aAAa;YAChB,OAAO,MAAM,CAAC;QAChB,KAAK,YAAY,CAAC;QAClB,KAAK,QAAQ;YACX,OAAO,YAAY,CAAC;QACtB,KAAK,gBAAgB;YACnB,OAAO,SAAS,CAAC;IACrB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* definitions — the tool lists a pool serves, and when each stops being served.
|
|
3
|
+
*
|
|
4
|
+
* Pattern: one list type, two owners — a client incarnation, or a shared entry.
|
|
5
|
+
* Role: § 6 of the design. A definition is what one `tools/list` entry says:
|
|
6
|
+
* name, description, input schema, and the declarations its `_meta`
|
|
7
|
+
* carries. The executable half (the connection) is never shared.
|
|
8
|
+
*
|
|
9
|
+
* - PER CLIENT (the default): a conversation's list is its own client's, and
|
|
10
|
+
* it belongs to that client INCARNATION alone — a replacement client, even
|
|
11
|
+
* one opened from the same plan, is a new session whose list may differ,
|
|
12
|
+
* so it lists again.
|
|
13
|
+
* - SHARED, only under the app's declared contract: one entry per context
|
|
14
|
+
* (`plan.ts`), listed on the client of whichever conversation needs it,
|
|
15
|
+
* bounded at `MAX_DEFINITION_ENTRIES` entries; a list longer than
|
|
16
|
+
* `MAX_SHARED_TOOLS` is used by its own client and not shared.
|
|
17
|
+
*
|
|
18
|
+
* A list stops being served in four ways, plus a time limit: the configuration
|
|
19
|
+
* (a new context is a new list), the server's tools-changed notice, the app's
|
|
20
|
+
* `permissionsChanged`, and `toolsChanged`. Invalidation moves the list's
|
|
21
|
+
* GENERATION at once — every tool bound to an older one is refused — and marks
|
|
22
|
+
* it to be listed again. A lapsed time limit lists again at the next list, and
|
|
23
|
+
* moves the generation only if the new list differs. A generation counts within
|
|
24
|
+
* one list: a replacement client and a new entry start counts of their own, and
|
|
25
|
+
* a binding names the list it came from, so no number of an old list can match
|
|
26
|
+
* a new one. A listing still in flight when its list's generation moved is
|
|
27
|
+
* discarded, never written.
|
|
28
|
+
*
|
|
29
|
+
* Every list is a frozen plain-data copy (`mcp/plainData.ts` · `copyJsonObject`),
|
|
30
|
+
* written once per generation, by one listing at a time.
|
|
31
|
+
*/
|
|
32
|
+
import type { Tool } from '../../tools/tools.js';
|
|
33
|
+
import type { McpListedTool } from '../types.js';
|
|
34
|
+
/** One definition, frozen, and its canonical JSON — what "the same definition" compares. */
|
|
35
|
+
export interface Definition {
|
|
36
|
+
readonly listed: McpListedTool;
|
|
37
|
+
readonly canonical: string;
|
|
38
|
+
}
|
|
39
|
+
/** A frozen copy of `listed`, or `undefined` when it is not plain JSON data. */
|
|
40
|
+
export declare function definitionOf(listed: McpListedTool): Definition | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* One list: an incarnation's own or a shared entry. Its `id` is stable for its
|
|
43
|
+
* life; its `generation` moves on invalidation, and on a time-limit relisting
|
|
44
|
+
* that changed it.
|
|
45
|
+
*/
|
|
46
|
+
export declare class DefinitionList {
|
|
47
|
+
readonly id: string;
|
|
48
|
+
private current;
|
|
49
|
+
private tools;
|
|
50
|
+
/** Has any listing been written? */
|
|
51
|
+
private written;
|
|
52
|
+
/** 0 until the first listing is written. */
|
|
53
|
+
generation: number;
|
|
54
|
+
/** Invalidated: listed again before it is served. */
|
|
55
|
+
stale: boolean;
|
|
56
|
+
/** When it was last written, on the pool's clock. */
|
|
57
|
+
listedAt: number;
|
|
58
|
+
/** The listing in flight, joined by anyone who needs the same list. */
|
|
59
|
+
listing: Promise<void> | undefined;
|
|
60
|
+
/** Can it be served at `now`, as it stands? */
|
|
61
|
+
servable(now: number, ttlMs: number): boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Invalidate: every older binding is refused, the next list lists again, and
|
|
64
|
+
* a listing in flight is discarded — even a first one, before anything was
|
|
65
|
+
* written. Generations only ever increase.
|
|
66
|
+
*/
|
|
67
|
+
invalidate(): void;
|
|
68
|
+
/** The definition named `name`, at the current generation. */
|
|
69
|
+
definition(name: string): Definition | undefined;
|
|
70
|
+
/** The client's own wrapped tool named `name` (per-client lists only). */
|
|
71
|
+
tool(name: string): Tool | undefined;
|
|
72
|
+
/** Every definition, in listing order. */
|
|
73
|
+
all(): readonly Definition[];
|
|
74
|
+
/**
|
|
75
|
+
* Write a listing that began at generation `began`. Refused — `false`, nothing
|
|
76
|
+
* written — when the generation moved while it ran. A first write moves the
|
|
77
|
+
* generation to a number no binding has seen; a write after invalidation keeps
|
|
78
|
+
* the moved one; a time-limit rewrite moves it only when the list differs.
|
|
79
|
+
*/
|
|
80
|
+
write(began: number, definitions: readonly Definition[], tools: readonly Tool[] | undefined, now: number): boolean;
|
|
81
|
+
}
|
|
82
|
+
/** A shared entry: a list and the tenant its context carries. */
|
|
83
|
+
export interface SharedEntry {
|
|
84
|
+
readonly list: DefinitionList;
|
|
85
|
+
readonly tenant: string;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* The shared definition entries, by context — bounded at
|
|
89
|
+
* `MAX_DEFINITION_ENTRIES`, the least recently used dropped first. A dropped
|
|
90
|
+
* entry's bindings are refused as stale: the entry they name is gone.
|
|
91
|
+
*/
|
|
92
|
+
export declare class SharedDefinitions {
|
|
93
|
+
private readonly maxEntries;
|
|
94
|
+
private readonly entries;
|
|
95
|
+
constructor(maxEntries?: number);
|
|
96
|
+
get size(): number;
|
|
97
|
+
/** The entry for `context`, created empty when there is none; most recently used. */
|
|
98
|
+
entryFor(context: string, tenant: string): SharedEntry;
|
|
99
|
+
/** The entry for `context` if it still exists — never created. */
|
|
100
|
+
peek(context: string): SharedEntry | undefined;
|
|
101
|
+
/** Invalidate every entry `matches` selects. */
|
|
102
|
+
invalidate(matches: (entry: SharedEntry) => boolean): void;
|
|
103
|
+
}
|