agentfootprint 12.1.0 → 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 -1
- 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/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 +9 -1
- package/dist/esm/mcp/index.js +8 -0
- package/dist/esm/mcp/index.js.map +1 -1
- package/dist/esm/mcp/mcpClient.d.ts +18 -1
- package/dist/esm/mcp/mcpClient.js +203 -43
- 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/types.d.ts +85 -7
- 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 +7 -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/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 +16 -1
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/mcpClient.js +207 -45
- 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/redaction/knownStrings.js +34 -0
- package/dist/redaction/knownStrings.js.map +1 -1
- package/dist/tool-providers/index.js +12 -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/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 +9 -1
- package/dist/types/mcp/index.d.ts.map +1 -1
- 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/types.d.ts +85 -7
- 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,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* options — what `mcpClientPool` is built from, read and checked once.
|
|
3
|
+
*
|
|
4
|
+
* Pattern: one options type + one reader that refuses by name.
|
|
5
|
+
* Role: The pool's budgets and clocks (`docs/design/2026-10-mcp-client-pool.md`
|
|
6
|
+
* § 3.1). Every number is checked when the pool is built, never at the
|
|
7
|
+
* first call. The pool takes the `mcpClient` options that shape one
|
|
8
|
+
* connection and hands them to every client it opens; it never takes
|
|
9
|
+
* `connection`, because it must build the transport itself — to end
|
|
10
|
+
* the session, to hear the tools-changed notice, and to place the send
|
|
11
|
+
* gate.
|
|
12
|
+
*/
|
|
13
|
+
import type { MemoryIdentity } from '../../memory/identity/types.js';
|
|
14
|
+
import { type McpReplayOptions } from '../replay.js';
|
|
15
|
+
import type { RetryOnThrottle } from '../throttleRetry.js';
|
|
16
|
+
import type { McpJsonSchemaValidator, McpSdk, McpTransport } from '../types.js';
|
|
17
|
+
import type { McpPoolReport } from './record.js';
|
|
18
|
+
import type { PoolClientOpener } from './plan.js';
|
|
19
|
+
/** The identity a pool keys a client by: `{ tenant?, principal?, conversationId }`. */
|
|
20
|
+
export type McpPoolIdentity = MemoryIdentity;
|
|
21
|
+
/** What a conversation's tool list may be shared under: two names the app vouches for. */
|
|
22
|
+
export interface McpPoolShareContext {
|
|
23
|
+
/**
|
|
24
|
+
* What the transport hides in its code — a signer's role, the credentials of
|
|
25
|
+
* an AWS client, a custom `fetch`. Hashed into the context, never recorded.
|
|
26
|
+
*/
|
|
27
|
+
readonly configuration: string;
|
|
28
|
+
/**
|
|
29
|
+
* What the server's tool visibility depends on — a role and a policy version
|
|
30
|
+
* (`'analyst@policy-7'`). A version in the name reaches a new entry without
|
|
31
|
+
* `permissionsChanged`, for conversations that already have a client too:
|
|
32
|
+
* their tools are listed again under the new name, and a tool bound under the
|
|
33
|
+
* old one is refused before it is sent. Hashed into the context, never
|
|
34
|
+
* recorded.
|
|
35
|
+
*/
|
|
36
|
+
readonly authorization: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Share tool definitions between conversations — declared by the app, checked by
|
|
40
|
+
* the pool. `share` is asked for every conversation before anything opens, and
|
|
41
|
+
* again at every listing, every call and every send gate after that, so a
|
|
42
|
+
* changed answer takes effect at the next one; it must be synchronous and
|
|
43
|
+
* cheap. A listing's answer is kept only if the names still give the context it
|
|
44
|
+
* was listed for when it arrives — otherwise it is dropped and listed again. Return both names, or `undefined` to keep that conversation's list to
|
|
45
|
+
* itself. Conversations share a list only when the transport's comparable
|
|
46
|
+
* data, both names and the tenant are all equal. A `share` that throws refuses
|
|
47
|
+
* the open (`init-failed`) or, for a conversation that already has a client,
|
|
48
|
+
* that listing or call (`binding-changed`) — its error as `cause`. Never share
|
|
49
|
+
* a server whose list follows the session (one that unlocks tools as a
|
|
50
|
+
* conversation goes on).
|
|
51
|
+
*/
|
|
52
|
+
export interface McpPoolDefinitionSharing {
|
|
53
|
+
readonly share: (identity: McpPoolIdentity) => McpPoolShareContext | undefined;
|
|
54
|
+
}
|
|
55
|
+
/** Options for {@link mcpClientPool}. */
|
|
56
|
+
export interface McpClientPoolOptions {
|
|
57
|
+
/** The server's name: every tool's `source`, the provider's id, and the record. */
|
|
58
|
+
readonly name: string;
|
|
59
|
+
/**
|
|
60
|
+
* The transport each client is opened over. A FUNCTION of the identity is
|
|
61
|
+
* preferred: it is called before each client opens (and again for every
|
|
62
|
+
* replacement) with the identity the client is for, so each client gets a
|
|
63
|
+
* transport — and a session map — of its own, and a provider can vend THAT
|
|
64
|
+
* user's credential. One shared object serves every client; a shared
|
|
65
|
+
* `'reuse'` runtime transport then needs `maxClients + maxEnding` at or below
|
|
66
|
+
* its 256 remembered connections.
|
|
67
|
+
*/
|
|
68
|
+
readonly transport: McpTransport | ((identity: McpPoolIdentity) => McpTransport);
|
|
69
|
+
/** Open clients — opening, busy or idle. Default 64. */
|
|
70
|
+
readonly maxClients?: number;
|
|
71
|
+
/** Open clients for one principal (one tenant's principal). Default 8. */
|
|
72
|
+
readonly maxClientsPerPrincipal?: number;
|
|
73
|
+
/** Clients being ended at once, until each ending's work has settled. Default 8. */
|
|
74
|
+
readonly maxEnding?: number;
|
|
75
|
+
/**
|
|
76
|
+
* Opens, listings, calls and endings outstanding on any client, each from
|
|
77
|
+
* its admission until its work has settled — not merely until its caller was
|
|
78
|
+
* answered. Default 256. Size it above `maxEnding`.
|
|
79
|
+
*/
|
|
80
|
+
readonly maxOperations?: number;
|
|
81
|
+
/** One open per conversation, under this ceiling. Default 30 000 ms. */
|
|
82
|
+
readonly initTimeoutMs?: number;
|
|
83
|
+
/**
|
|
84
|
+
* End a client idle this long, at the next pool interaction (no timer is
|
|
85
|
+
* installed). Default 600 000 ms — below AgentCore Runtime's 900 s idle
|
|
86
|
+
* timeout; set it lower for a runtime configured lower.
|
|
87
|
+
*/
|
|
88
|
+
readonly idleMs?: number;
|
|
89
|
+
/** An app lease (`acquire`) is revoked past this. Default 900 000 ms (15 minutes). */
|
|
90
|
+
readonly maxLeaseMs?: number;
|
|
91
|
+
/** A tool list is listed again after this, at the next list. Default 600 000 ms. */
|
|
92
|
+
readonly definitionTtlMs?: number;
|
|
93
|
+
/** `'per-client'` (the default), or the app's declared sharing contract. */
|
|
94
|
+
readonly definitions?: 'per-client' | McpPoolDefinitionSharing;
|
|
95
|
+
/** Refuse an identity without a `principal`. Default `true`. */
|
|
96
|
+
readonly requirePrincipal?: boolean;
|
|
97
|
+
/** Refuse an identity without a `tenant` — for multitenant hosts. Default `false`. */
|
|
98
|
+
readonly requireTenant?: boolean;
|
|
99
|
+
/**
|
|
100
|
+
* What happens outside a run — endings, deferred endings, revoked leases,
|
|
101
|
+
* work that settled after its deadline. Ids and outcomes only. A throwing
|
|
102
|
+
* handler is swallowed and changes nothing.
|
|
103
|
+
*/
|
|
104
|
+
readonly onEvent?: (report: McpPoolReport) => void;
|
|
105
|
+
/** Passed to every client: how a tool's result is decoded (`McpClientOptions.resultMode`). */
|
|
106
|
+
readonly resultMode?: 'text' | 'structured' | 'structured-or-json';
|
|
107
|
+
/** Passed to every client: the `initialize` identity. */
|
|
108
|
+
readonly clientInfo?: {
|
|
109
|
+
readonly name: string;
|
|
110
|
+
readonly version: string;
|
|
111
|
+
};
|
|
112
|
+
/** Passed to every client: the 429 retry (`McpClientOptions.retryOnThrottle`). */
|
|
113
|
+
readonly retryOnThrottle?: RetryOnThrottle;
|
|
114
|
+
/**
|
|
115
|
+
* Passed to every client: who may send a tool call again
|
|
116
|
+
* (`McpClientOptions.replay`). Read and checked when the pool is built. The
|
|
117
|
+
* default stays `'compatibility'`. Whatever the policy, the pool's own send
|
|
118
|
+
* gate refuses a pooled request it cannot check (`request-unmatched`).
|
|
119
|
+
*/
|
|
120
|
+
readonly replay?: McpReplayOptions;
|
|
121
|
+
/** Passed to every client: the two SDK modules, imported by you (`McpClientOptions.sdk`). */
|
|
122
|
+
readonly sdk?: McpSdk;
|
|
123
|
+
/**
|
|
124
|
+
* Passed to every client: the JSON Schema validator a tool's own output checks
|
|
125
|
+
* use (`McpClientOptions.jsonSchemaValidator`). With `sdk`, give it: a shared
|
|
126
|
+
* definition always runs its own checks, and without a validator a tool that
|
|
127
|
+
* declares an `outputSchema` is refused unsent.
|
|
128
|
+
*/
|
|
129
|
+
readonly jsonSchemaValidator?: McpJsonSchemaValidator;
|
|
130
|
+
/** Clock seam — tests drive idle, lease and definition ages without waiting. */
|
|
131
|
+
readonly now?: () => number;
|
|
132
|
+
/** @internal Test seam: how a client is opened (the real path is `openPooledClient`). */
|
|
133
|
+
readonly _open?: PoolClientOpener;
|
|
134
|
+
}
|
|
135
|
+
/** The defaults, named so a test and a docstring cannot drift from the code. */
|
|
136
|
+
export declare const MCP_POOL_DEFAULTS: Readonly<{
|
|
137
|
+
maxClients: 64;
|
|
138
|
+
maxClientsPerPrincipal: 8;
|
|
139
|
+
maxEnding: 8;
|
|
140
|
+
maxOperations: 256;
|
|
141
|
+
initTimeoutMs: 30000;
|
|
142
|
+
idleMs: 600000;
|
|
143
|
+
maxLeaseMs: 900000;
|
|
144
|
+
definitionTtlMs: 600000;
|
|
145
|
+
}>;
|
|
146
|
+
/** At most this many shared definition entries are kept; the least recently used goes first. */
|
|
147
|
+
export declare const MAX_DEFINITION_ENTRIES = 256;
|
|
148
|
+
/** A list longer than this is used by its own client and never shared. */
|
|
149
|
+
export declare const MAX_SHARED_TOOLS = 512;
|
|
150
|
+
/** The pool's settings, read once. */
|
|
151
|
+
export interface PoolSettings {
|
|
152
|
+
readonly name: string;
|
|
153
|
+
readonly transport: (identity: McpPoolIdentity) => McpTransport;
|
|
154
|
+
readonly maxClients: number;
|
|
155
|
+
readonly maxClientsPerPrincipal: number;
|
|
156
|
+
readonly maxEnding: number;
|
|
157
|
+
readonly maxOperations: number;
|
|
158
|
+
readonly initTimeoutMs: number;
|
|
159
|
+
readonly idleMs: number;
|
|
160
|
+
readonly maxLeaseMs: number;
|
|
161
|
+
readonly definitionTtlMs: number;
|
|
162
|
+
readonly share: McpPoolDefinitionSharing['share'] | undefined;
|
|
163
|
+
readonly requirePrincipal: boolean;
|
|
164
|
+
readonly requireTenant: boolean;
|
|
165
|
+
readonly onEvent: ((report: McpPoolReport) => void) | undefined;
|
|
166
|
+
readonly client: {
|
|
167
|
+
readonly resultMode?: McpClientPoolOptions['resultMode'];
|
|
168
|
+
readonly clientInfo?: McpClientPoolOptions['clientInfo'];
|
|
169
|
+
readonly retryOnThrottle?: RetryOnThrottle;
|
|
170
|
+
readonly replay?: McpReplayOptions;
|
|
171
|
+
readonly sdk?: McpSdk;
|
|
172
|
+
readonly jsonSchemaValidator?: McpJsonSchemaValidator;
|
|
173
|
+
};
|
|
174
|
+
readonly now: () => number;
|
|
175
|
+
readonly open: PoolClientOpener | undefined;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Read `options` once.
|
|
179
|
+
*
|
|
180
|
+
* @throws TypeError naming the option, when one is missing, mistyped or out of
|
|
181
|
+
* range, or when `connection` is given — here, when the pool is built.
|
|
182
|
+
*/
|
|
183
|
+
export declare function readPoolOptions(options: McpClientPoolOptions): PoolSettings;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* options — what `mcpClientPool` is built from, read and checked once.
|
|
3
|
+
*
|
|
4
|
+
* Pattern: one options type + one reader that refuses by name.
|
|
5
|
+
* Role: The pool's budgets and clocks (`docs/design/2026-10-mcp-client-pool.md`
|
|
6
|
+
* § 3.1). Every number is checked when the pool is built, never at the
|
|
7
|
+
* first call. The pool takes the `mcpClient` options that shape one
|
|
8
|
+
* connection and hands them to every client it opens; it never takes
|
|
9
|
+
* `connection`, because it must build the transport itself — to end
|
|
10
|
+
* the session, to hear the tools-changed notice, and to place the send
|
|
11
|
+
* gate.
|
|
12
|
+
*/
|
|
13
|
+
import { replayDeclarationOf } from '../replay.js';
|
|
14
|
+
/** The defaults, named so a test and a docstring cannot drift from the code. */
|
|
15
|
+
export const MCP_POOL_DEFAULTS = Object.freeze({
|
|
16
|
+
maxClients: 64,
|
|
17
|
+
maxClientsPerPrincipal: 8,
|
|
18
|
+
maxEnding: 8,
|
|
19
|
+
maxOperations: 256,
|
|
20
|
+
initTimeoutMs: 30_000,
|
|
21
|
+
idleMs: 600_000,
|
|
22
|
+
maxLeaseMs: 900_000,
|
|
23
|
+
definitionTtlMs: 600_000,
|
|
24
|
+
});
|
|
25
|
+
/** At most this many shared definition entries are kept; the least recently used goes first. */
|
|
26
|
+
export const MAX_DEFINITION_ENTRIES = 256;
|
|
27
|
+
/** A list longer than this is used by its own client and never shared. */
|
|
28
|
+
export const MAX_SHARED_TOOLS = 512;
|
|
29
|
+
/**
|
|
30
|
+
* Read `options` once.
|
|
31
|
+
*
|
|
32
|
+
* @throws TypeError naming the option, when one is missing, mistyped or out of
|
|
33
|
+
* range, or when `connection` is given — here, when the pool is built.
|
|
34
|
+
*/
|
|
35
|
+
export function readPoolOptions(options) {
|
|
36
|
+
if (options === null || typeof options !== 'object') {
|
|
37
|
+
throw new TypeError('mcpClientPool: options must be an object with `name` and `transport`.');
|
|
38
|
+
}
|
|
39
|
+
const { name } = options;
|
|
40
|
+
if (typeof name !== 'string' || name.trim() === '') {
|
|
41
|
+
throw new TypeError('mcpClientPool: `name` must be a non-empty string — the server name every tool and record carries.');
|
|
42
|
+
}
|
|
43
|
+
const where = `mcpClientPool[${name}]`;
|
|
44
|
+
if (options.connection !== undefined) {
|
|
45
|
+
throw new TypeError(`${where}: \`connection\` is not taken. The pool builds every client's transport itself — ` +
|
|
46
|
+
'to end its session, to hear the tools-changed notice and to place the send gate. Pass ' +
|
|
47
|
+
'`transport`, or a function of the identity that returns one.');
|
|
48
|
+
}
|
|
49
|
+
const transport = transportOf(options.transport, where);
|
|
50
|
+
const count = (key) => positiveInteger(options[key], MCP_POOL_DEFAULTS[key], key, where);
|
|
51
|
+
const definitions = options.definitions ?? 'per-client';
|
|
52
|
+
if (definitions !== 'per-client' && typeof definitions?.share !== 'function') {
|
|
53
|
+
throw new TypeError(`${where}: \`definitions\` must be 'per-client' or { share: (identity) => names | undefined }.`);
|
|
54
|
+
}
|
|
55
|
+
if (options.onEvent !== undefined && typeof options.onEvent !== 'function') {
|
|
56
|
+
throw new TypeError(`${where}: \`onEvent\` must be a function.`);
|
|
57
|
+
}
|
|
58
|
+
if (options.now !== undefined && typeof options.now !== 'function') {
|
|
59
|
+
throw new TypeError(`${where}: the clock option must be a function returning milliseconds.`);
|
|
60
|
+
}
|
|
61
|
+
// Refused by field now, as each client would refuse it at its first open.
|
|
62
|
+
if (options.replay !== undefined)
|
|
63
|
+
replayDeclarationOf(options.replay, where);
|
|
64
|
+
return {
|
|
65
|
+
name,
|
|
66
|
+
transport,
|
|
67
|
+
maxClients: count('maxClients'),
|
|
68
|
+
maxClientsPerPrincipal: count('maxClientsPerPrincipal'),
|
|
69
|
+
maxEnding: count('maxEnding'),
|
|
70
|
+
maxOperations: count('maxOperations'),
|
|
71
|
+
initTimeoutMs: count('initTimeoutMs'),
|
|
72
|
+
idleMs: count('idleMs'),
|
|
73
|
+
maxLeaseMs: count('maxLeaseMs'),
|
|
74
|
+
definitionTtlMs: count('definitionTtlMs'),
|
|
75
|
+
share: definitions === 'per-client' ? undefined : definitions.share,
|
|
76
|
+
requirePrincipal: flag(options.requirePrincipal, true, 'requirePrincipal', where),
|
|
77
|
+
requireTenant: flag(options.requireTenant, false, 'requireTenant', where),
|
|
78
|
+
onEvent: options.onEvent,
|
|
79
|
+
client: {
|
|
80
|
+
...(options.resultMode !== undefined && { resultMode: options.resultMode }),
|
|
81
|
+
...(options.clientInfo !== undefined && { clientInfo: options.clientInfo }),
|
|
82
|
+
...(options.retryOnThrottle !== undefined && { retryOnThrottle: options.retryOnThrottle }),
|
|
83
|
+
...(options.replay !== undefined && { replay: options.replay }),
|
|
84
|
+
...(options.sdk !== undefined && { sdk: options.sdk }),
|
|
85
|
+
...(options.jsonSchemaValidator !== undefined && {
|
|
86
|
+
jsonSchemaValidator: options.jsonSchemaValidator,
|
|
87
|
+
}),
|
|
88
|
+
},
|
|
89
|
+
now: options.now ?? Date.now,
|
|
90
|
+
open: options._open,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/** One transport for everyone, or a function of the identity — as a function either way. */
|
|
94
|
+
function transportOf(value, where) {
|
|
95
|
+
if (typeof value === 'function')
|
|
96
|
+
return value;
|
|
97
|
+
if (value !== null && typeof value === 'object' && typeof value.transport === 'string') {
|
|
98
|
+
return () => value;
|
|
99
|
+
}
|
|
100
|
+
throw new TypeError(`${where}: \`transport\` must be an MCP transport ({ transport: 'http' | 'gateway' | 'stdio', … }) ` +
|
|
101
|
+
'or a function of the identity that returns one.');
|
|
102
|
+
}
|
|
103
|
+
/** A whole number ≥ 1, or the default when absent. */
|
|
104
|
+
function positiveInteger(value, fallback, key, where) {
|
|
105
|
+
if (value === undefined)
|
|
106
|
+
return fallback;
|
|
107
|
+
if (typeof value === 'number' && Number.isInteger(value) && value >= 1)
|
|
108
|
+
return value;
|
|
109
|
+
throw new TypeError(`${where}: \`${key}\` must be a whole number ≥ 1.`);
|
|
110
|
+
}
|
|
111
|
+
/** A boolean, or the default when absent. */
|
|
112
|
+
function flag(value, fallback, key, where) {
|
|
113
|
+
if (value === undefined)
|
|
114
|
+
return fallback;
|
|
115
|
+
if (typeof value === 'boolean')
|
|
116
|
+
return value;
|
|
117
|
+
throw new TypeError(`${where}: \`${key}\` must be true or false.`);
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=options.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"options.js","sourceRoot":"","sources":["../../../../src/mcp/pool/options.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* plan — what one client is opened from, resolved before anything opens, and
|
|
3
|
+
* the context its tool definitions belong to.
|
|
4
|
+
*
|
|
5
|
+
* Pattern: a pure resolution step + two hashes.
|
|
6
|
+
* Role: § 6.1–6.2 of the design. A client is opened from a PLAN: the
|
|
7
|
+
* transport the factory returns for this identity, resolved at that
|
|
8
|
+
* moment and kept unchanged for the client's life. The plan's
|
|
9
|
+
* `configHash` is SHA-256 over a canonical encoding of the transport's
|
|
10
|
+
* comparable data — the pool name; the transport kind and URL; header
|
|
11
|
+
* names and a digest of their values; a gateway's service, scopes,
|
|
12
|
+
* mode and credential provider id; stdio's command, args and working
|
|
13
|
+
* directory; the transport's own stated `settings`; `resultMode` and
|
|
14
|
+
* `clientInfo`. A function (a `fetch`, an AWS client) cannot be
|
|
15
|
+
* compared: the app's `configuration` name covers it.
|
|
16
|
+
*
|
|
17
|
+
* The definitions CONTEXT is the binding a tool is listed under:
|
|
18
|
+
*
|
|
19
|
+
* - per client (the default): `pc:<configHash>` — the client's own list;
|
|
20
|
+
* - shared, when the app's `share` names both a configuration and an
|
|
21
|
+
* authorization: `sh:<sha256(configHash, configuration, authorization,
|
|
22
|
+
* tenant)>`. The tenant is always in it: the app's names widen sharing
|
|
23
|
+
* across principals, never across tenants.
|
|
24
|
+
*
|
|
25
|
+
* The transport is resolved once, when a client opens; the context is not. The
|
|
26
|
+
* app's names can move — a policy version in `authorization` — so the pool asks
|
|
27
|
+
* `share` again at every listing, every call and every send gate
|
|
28
|
+
* (`contextFor`), over the client's own `configHash`: a conversation that
|
|
29
|
+
* already has a client follows a new name as a new conversation would.
|
|
30
|
+
*
|
|
31
|
+
* Neither hash leaves the process; the app's names are hashed and never kept.
|
|
32
|
+
*/
|
|
33
|
+
import type { ClientPort, PooledClientSeam } from '../clientPort.js';
|
|
34
|
+
import type { McpClient, McpClientOptions, McpTransport } from '../types.js';
|
|
35
|
+
import type { PoolKey } from './identityKey.js';
|
|
36
|
+
import type { PoolSettings } from './options.js';
|
|
37
|
+
/** What a client is opened from. */
|
|
38
|
+
export interface ClientPlan {
|
|
39
|
+
readonly transport: McpTransport;
|
|
40
|
+
readonly configHash: string;
|
|
41
|
+
/** The definitions context: `pc:<configHash>` or `sh:<context>`. */
|
|
42
|
+
readonly context: string;
|
|
43
|
+
/** True when the app's sharing contract applies to this conversation. */
|
|
44
|
+
readonly shared: boolean;
|
|
45
|
+
}
|
|
46
|
+
/** An opened client: the ordinary `McpClient`, and the port back (`clientPort.ts`). */
|
|
47
|
+
export interface OpenedClient {
|
|
48
|
+
readonly client: McpClient;
|
|
49
|
+
readonly port: ClientPort;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* How the pool opens one client. The real path is `openPooledClient`; a test
|
|
53
|
+
* injects its own (the `_open` seam).
|
|
54
|
+
*/
|
|
55
|
+
export type PoolClientOpener = (options: McpClientOptions, seam: PooledClientSeam) => Promise<OpenedClient>;
|
|
56
|
+
/**
|
|
57
|
+
* The plan for `key`: the transport the factory returns now, and the context
|
|
58
|
+
* its definitions belong to.
|
|
59
|
+
*
|
|
60
|
+
* @throws what the transport factory or `share` throws, and a TypeError when
|
|
61
|
+
* `share` answers with anything but two names or `undefined` — the caller
|
|
62
|
+
* refuses the open with `init-failed`, the error as its `cause`
|
|
63
|
+
*/
|
|
64
|
+
export declare function resolvePlan(settings: PoolSettings, key: PoolKey): ClientPlan;
|
|
65
|
+
/**
|
|
66
|
+
* The definitions context `key` belongs to now, for a client opened with
|
|
67
|
+
* `configHash`: the app's `share` asked again.
|
|
68
|
+
*
|
|
69
|
+
* @throws what `share` throws, and a TypeError when it answers with anything
|
|
70
|
+
* but two names or `undefined`
|
|
71
|
+
*/
|
|
72
|
+
export declare function contextFor(settings: PoolSettings, key: PoolKey, configHash: string): {
|
|
73
|
+
readonly context: string;
|
|
74
|
+
readonly shared: boolean;
|
|
75
|
+
};
|
|
76
|
+
/** SHA-256 over the transport's comparable data (see the module note). */
|
|
77
|
+
export declare function configHashOf(settings: PoolSettings, transport: McpTransport): string;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* plan — what one client is opened from, resolved before anything opens, and
|
|
3
|
+
* the context its tool definitions belong to.
|
|
4
|
+
*
|
|
5
|
+
* Pattern: a pure resolution step + two hashes.
|
|
6
|
+
* Role: § 6.1–6.2 of the design. A client is opened from a PLAN: the
|
|
7
|
+
* transport the factory returns for this identity, resolved at that
|
|
8
|
+
* moment and kept unchanged for the client's life. The plan's
|
|
9
|
+
* `configHash` is SHA-256 over a canonical encoding of the transport's
|
|
10
|
+
* comparable data — the pool name; the transport kind and URL; header
|
|
11
|
+
* names and a digest of their values; a gateway's service, scopes,
|
|
12
|
+
* mode and credential provider id; stdio's command, args and working
|
|
13
|
+
* directory; the transport's own stated `settings`; `resultMode` and
|
|
14
|
+
* `clientInfo`. A function (a `fetch`, an AWS client) cannot be
|
|
15
|
+
* compared: the app's `configuration` name covers it.
|
|
16
|
+
*
|
|
17
|
+
* The definitions CONTEXT is the binding a tool is listed under:
|
|
18
|
+
*
|
|
19
|
+
* - per client (the default): `pc:<configHash>` — the client's own list;
|
|
20
|
+
* - shared, when the app's `share` names both a configuration and an
|
|
21
|
+
* authorization: `sh:<sha256(configHash, configuration, authorization,
|
|
22
|
+
* tenant)>`. The tenant is always in it: the app's names widen sharing
|
|
23
|
+
* across principals, never across tenants.
|
|
24
|
+
*
|
|
25
|
+
* The transport is resolved once, when a client opens; the context is not. The
|
|
26
|
+
* app's names can move — a policy version in `authorization` — so the pool asks
|
|
27
|
+
* `share` again at every listing, every call and every send gate
|
|
28
|
+
* (`contextFor`), over the client's own `configHash`: a conversation that
|
|
29
|
+
* already has a client follows a new name as a new conversation would.
|
|
30
|
+
*
|
|
31
|
+
* Neither hash leaves the process; the app's names are hashed and never kept.
|
|
32
|
+
*/
|
|
33
|
+
import { canonicalJson } from '../../lib/canonicalJson.js';
|
|
34
|
+
import { sha256Hex } from '../../lib/sha256.js';
|
|
35
|
+
/**
|
|
36
|
+
* The plan for `key`: the transport the factory returns now, and the context
|
|
37
|
+
* its definitions belong to.
|
|
38
|
+
*
|
|
39
|
+
* @throws what the transport factory or `share` throws, and a TypeError when
|
|
40
|
+
* `share` answers with anything but two names or `undefined` — the caller
|
|
41
|
+
* refuses the open with `init-failed`, the error as its `cause`
|
|
42
|
+
*/
|
|
43
|
+
export function resolvePlan(settings, key) {
|
|
44
|
+
const transport = settings.transport(key.identity);
|
|
45
|
+
if (transport === null ||
|
|
46
|
+
typeof transport !== 'object' ||
|
|
47
|
+
typeof transport.transport !== 'string') {
|
|
48
|
+
throw new TypeError(`mcpClientPool[${settings.name}]: the \`transport\` function returned something that is not an MCP transport.`);
|
|
49
|
+
}
|
|
50
|
+
const configHash = configHashOf(settings, transport);
|
|
51
|
+
return { transport, configHash, ...contextFor(settings, key, configHash) };
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The definitions context `key` belongs to now, for a client opened with
|
|
55
|
+
* `configHash`: the app's `share` asked again.
|
|
56
|
+
*
|
|
57
|
+
* @throws what `share` throws, and a TypeError when it answers with anything
|
|
58
|
+
* but two names or `undefined`
|
|
59
|
+
*/
|
|
60
|
+
export function contextFor(settings, key, configHash) {
|
|
61
|
+
const names = shareOf(settings, key);
|
|
62
|
+
if (names === undefined)
|
|
63
|
+
return { context: `pc:${configHash}`, shared: false };
|
|
64
|
+
const context = sha256Hex(canonicalJson({
|
|
65
|
+
configHash,
|
|
66
|
+
configuration: names.configuration,
|
|
67
|
+
authorization: names.authorization,
|
|
68
|
+
tenant: key.tenant,
|
|
69
|
+
}));
|
|
70
|
+
return { context: `sh:${context}`, shared: true };
|
|
71
|
+
}
|
|
72
|
+
/** The app's two names for this conversation, or `undefined` for a list of its own. */
|
|
73
|
+
function shareOf(settings, key) {
|
|
74
|
+
if (settings.share === undefined)
|
|
75
|
+
return undefined;
|
|
76
|
+
const names = settings.share(key.identity);
|
|
77
|
+
if (names === undefined)
|
|
78
|
+
return undefined;
|
|
79
|
+
if (names !== null &&
|
|
80
|
+
typeof names === 'object' &&
|
|
81
|
+
typeof names.configuration === 'string' &&
|
|
82
|
+
typeof names.authorization === 'string') {
|
|
83
|
+
const { configuration, authorization } = names;
|
|
84
|
+
return { configuration, authorization };
|
|
85
|
+
}
|
|
86
|
+
throw new TypeError(`mcpClientPool[${settings.name}]: \`definitions.share\` must return { configuration, authorization } ` +
|
|
87
|
+
'(two strings) or undefined, synchronously.');
|
|
88
|
+
}
|
|
89
|
+
/** SHA-256 over the transport's comparable data (see the module note). */
|
|
90
|
+
export function configHashOf(settings, transport) {
|
|
91
|
+
return sha256Hex(canonicalJson({
|
|
92
|
+
pool: settings.name,
|
|
93
|
+
transport: comparableOf(transport),
|
|
94
|
+
resultMode: settings.client.resultMode ?? null,
|
|
95
|
+
clientInfo: settings.client.clientInfo ?? null,
|
|
96
|
+
}));
|
|
97
|
+
}
|
|
98
|
+
/** The comparable part of a transport: data only, functions left out. */
|
|
99
|
+
function comparableOf(transport) {
|
|
100
|
+
if (transport.transport === 'stdio') {
|
|
101
|
+
return {
|
|
102
|
+
kind: 'stdio',
|
|
103
|
+
command: transport.command,
|
|
104
|
+
args: [...(transport.args ?? [])],
|
|
105
|
+
cwd: transport.cwd ?? null,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
const headers = headersOf(transport.headers);
|
|
109
|
+
if (transport.transport === 'gateway') {
|
|
110
|
+
return {
|
|
111
|
+
kind: 'gateway',
|
|
112
|
+
url: transport.url,
|
|
113
|
+
headers,
|
|
114
|
+
service: transport.service,
|
|
115
|
+
scopes: [...(transport.scopes ?? [])],
|
|
116
|
+
mode: transport.mode ?? null,
|
|
117
|
+
credentials: transport.credentials?.id ?? null,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
kind: 'http',
|
|
122
|
+
url: transport.url,
|
|
123
|
+
headers,
|
|
124
|
+
settings: transport.settings ?? null,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
/** Header names, and a digest of their values — never the values themselves. */
|
|
128
|
+
function headersOf(headers) {
|
|
129
|
+
if (headers === undefined)
|
|
130
|
+
return { names: [], digest: null };
|
|
131
|
+
const lowered = Object.fromEntries(Object.entries(headers).map(([name, value]) => [name.toLowerCase(), value]));
|
|
132
|
+
return { names: Object.keys(lowered).sort(), digest: sha256Hex(canonicalJson(lowered)) };
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=plan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan.js","sourceRoot":"","sources":["../../../../src/mcp/pool/plan.ts"],"names":[],"mappings":"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"}
|