agentfootprint 11.4.0 → 11.5.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/CAPABILITIES.md +2 -1
- package/dist/adapters/mcp/agentcoreRuntime/invoke.js +7 -6
- package/dist/adapters/mcp/agentcoreRuntime/invoke.js.map +1 -1
- package/dist/adapters/mcp/agentcoreRuntime/sessionBusy.js +36 -2
- package/dist/adapters/mcp/agentcoreRuntime/sessionBusy.js.map +1 -1
- package/dist/adapters/mcp/agentcoreRuntime/transport.js +3 -1
- package/dist/adapters/mcp/agentcoreRuntime/transport.js.map +1 -1
- package/dist/esm/adapters/mcp/agentcoreRuntime/invoke.d.ts +7 -6
- package/dist/esm/adapters/mcp/agentcoreRuntime/invoke.js +7 -6
- package/dist/esm/adapters/mcp/agentcoreRuntime/invoke.js.map +1 -1
- package/dist/esm/adapters/mcp/agentcoreRuntime/sessionBusy.d.ts +32 -1
- package/dist/esm/adapters/mcp/agentcoreRuntime/sessionBusy.js +35 -1
- package/dist/esm/adapters/mcp/agentcoreRuntime/sessionBusy.js.map +1 -1
- package/dist/esm/adapters/mcp/agentcoreRuntime/transport.js +4 -2
- package/dist/esm/adapters/mcp/agentcoreRuntime/transport.js.map +1 -1
- package/dist/esm/events/payloads.d.ts +128 -27
- package/dist/esm/mcp/callLifetime.d.ts +57 -14
- package/dist/esm/mcp/callLifetime.js +67 -19
- package/dist/esm/mcp/callLifetime.js.map +1 -1
- package/dist/esm/mcp/callMarker.d.ts +43 -0
- package/dist/esm/mcp/callMarker.js +69 -0
- package/dist/esm/mcp/callMarker.js.map +1 -0
- package/dist/esm/mcp/connectionRefusals.js +7 -0
- package/dist/esm/mcp/connectionRefusals.js.map +1 -1
- package/dist/esm/mcp/index.d.ts +2 -0
- package/dist/esm/mcp/index.js +3 -0
- package/dist/esm/mcp/index.js.map +1 -1
- package/dist/esm/mcp/mcpClient.js +93 -26
- package/dist/esm/mcp/mcpClient.js.map +1 -1
- package/dist/esm/mcp/replay.d.ts +283 -0
- package/dist/esm/mcp/replay.js +401 -0
- package/dist/esm/mcp/replay.js.map +1 -0
- package/dist/esm/mcp/replayUnavailable.d.ts +55 -0
- package/dist/esm/mcp/replayUnavailable.js +74 -0
- package/dist/esm/mcp/replayUnavailable.js.map +1 -0
- package/dist/esm/mcp/requestLedger.d.ts +115 -32
- package/dist/esm/mcp/requestLedger.js +193 -52
- package/dist/esm/mcp/requestLedger.js.map +1 -1
- package/dist/esm/mcp/seriesEvidence.d.ts +73 -15
- package/dist/esm/mcp/seriesEvidence.js +114 -21
- package/dist/esm/mcp/seriesEvidence.js.map +1 -1
- package/dist/esm/mcp/throttleRetry.d.ts +86 -42
- package/dist/esm/mcp/throttleRetry.js +158 -50
- package/dist/esm/mcp/throttleRetry.js.map +1 -1
- package/dist/esm/mcp/toolResult.d.ts +6 -0
- package/dist/esm/mcp/toolResult.js +15 -0
- package/dist/esm/mcp/toolResult.js.map +1 -1
- package/dist/esm/mcp/types.d.ts +70 -8
- package/dist/esm/redaction/knownStrings.js +13 -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 +4 -0
- package/dist/esm/tool-providers/index.js.map +1 -1
- package/dist/esm/tools/requestEvidence.js +60 -0
- package/dist/esm/tools/requestEvidence.js.map +1 -1
- package/dist/mcp/callLifetime.js +66 -18
- package/dist/mcp/callLifetime.js.map +1 -1
- package/dist/mcp/callMarker.js +74 -0
- package/dist/mcp/callMarker.js.map +1 -0
- package/dist/mcp/connectionRefusals.js +7 -0
- package/dist/mcp/connectionRefusals.js.map +1 -1
- package/dist/mcp/index.js +5 -1
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/mcpClient.js +91 -24
- package/dist/mcp/mcpClient.js.map +1 -1
- package/dist/mcp/replay.js +411 -0
- package/dist/mcp/replay.js.map +1 -0
- package/dist/mcp/replayUnavailable.js +79 -0
- package/dist/mcp/replayUnavailable.js.map +1 -0
- package/dist/mcp/requestLedger.js +193 -52
- package/dist/mcp/requestLedger.js.map +1 -1
- package/dist/mcp/seriesEvidence.js +116 -22
- package/dist/mcp/seriesEvidence.js.map +1 -1
- package/dist/mcp/throttleRetry.js +160 -51
- package/dist/mcp/throttleRetry.js.map +1 -1
- package/dist/mcp/toolResult.js +17 -1
- package/dist/mcp/toolResult.js.map +1 -1
- package/dist/redaction/knownStrings.js +13 -0
- package/dist/redaction/knownStrings.js.map +1 -1
- package/dist/tool-providers/index.js +5 -1
- package/dist/tool-providers/index.js.map +1 -1
- package/dist/tools/requestEvidence.js +60 -0
- package/dist/tools/requestEvidence.js.map +1 -1
- package/dist/types/adapters/mcp/agentcoreRuntime/invoke.d.ts +7 -6
- package/dist/types/adapters/mcp/agentcoreRuntime/invoke.d.ts.map +1 -1
- package/dist/types/adapters/mcp/agentcoreRuntime/sessionBusy.d.ts +32 -1
- package/dist/types/adapters/mcp/agentcoreRuntime/sessionBusy.d.ts.map +1 -1
- package/dist/types/adapters/mcp/agentcoreRuntime/transport.d.ts.map +1 -1
- package/dist/types/events/payloads.d.ts +128 -27
- package/dist/types/events/payloads.d.ts.map +1 -1
- package/dist/types/mcp/callLifetime.d.ts +57 -14
- package/dist/types/mcp/callLifetime.d.ts.map +1 -1
- package/dist/types/mcp/callMarker.d.ts +44 -0
- package/dist/types/mcp/callMarker.d.ts.map +1 -0
- package/dist/types/mcp/index.d.ts +2 -0
- package/dist/types/mcp/index.d.ts.map +1 -1
- package/dist/types/mcp/mcpClient.d.ts.map +1 -1
- package/dist/types/mcp/replay.d.ts +284 -0
- package/dist/types/mcp/replay.d.ts.map +1 -0
- package/dist/types/mcp/replayUnavailable.d.ts +56 -0
- package/dist/types/mcp/replayUnavailable.d.ts.map +1 -0
- package/dist/types/mcp/requestLedger.d.ts +115 -32
- package/dist/types/mcp/requestLedger.d.ts.map +1 -1
- package/dist/types/mcp/seriesEvidence.d.ts +73 -15
- package/dist/types/mcp/seriesEvidence.d.ts.map +1 -1
- package/dist/types/mcp/throttleRetry.d.ts +86 -42
- package/dist/types/mcp/throttleRetry.d.ts.map +1 -1
- package/dist/types/mcp/toolResult.d.ts +6 -0
- package/dist/types/mcp/toolResult.d.ts.map +1 -1
- package/dist/types/mcp/types.d.ts +70 -8
- 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/requestEvidence.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* replay — who may send an MCP tool call again, and what is known of what each
|
|
3
|
+
* attempt did: two facts, decided apart and recorded apart.
|
|
4
|
+
*
|
|
5
|
+
* Pattern: a declaration read once + one permission rule + one accumulator.
|
|
6
|
+
* Role: Layer-3 tool transport. `mcpClient({ replay })` reads the app's
|
|
7
|
+
* declaration (`replayDeclarationOf`) when the client is built. The
|
|
8
|
+
* call's retry series (`throttleRetry.ts` · `Series`) asks `permit`
|
|
9
|
+
* before every repeat and records the answer beside the attempt;
|
|
10
|
+
* `SeriesKnowledge` carries what is known of the series' effect from
|
|
11
|
+
* one attempt to the next.
|
|
12
|
+
*
|
|
13
|
+
* ── THE LAW ──────────────────────────────────────────────────────────────────
|
|
14
|
+
* PERMISSION to repeat comes only from the app: the (server, tool) pairs it
|
|
15
|
+
* declares — the server is the client the declaration is given to — and the
|
|
16
|
+
* failure kinds each tool may be repeated on. Never from the model, a tool's
|
|
17
|
+
* description or a server's annotations (`readOnlyHint`): nothing here reads
|
|
18
|
+
* them. It is configuration, read once when the client is built — never a
|
|
19
|
+
* per-call question.
|
|
20
|
+
*
|
|
21
|
+
* KNOWLEDGE of whether an attempt ran comes only from the record and from an
|
|
22
|
+
* established service guarantee. No reply this library reads is one. A 429 is
|
|
23
|
+
* not: RFC 6585 §4 defines it as rate limiting, and `Retry-After` says when to
|
|
24
|
+
* ask again, not that asking is safe. AgentCore Runtime's session-busy body is
|
|
25
|
+
* not either: AWS documents a retryable conflict while a session starts or
|
|
26
|
+
* stops, and the body is matched by its code and words, which a server behind
|
|
27
|
+
* the runtime can send after it ran the call. So every attempt that was sent
|
|
28
|
+
* and did not complete is `unknown`; `none` is only a request never sent. A
|
|
29
|
+
* vendor's own SDK may retry its own 429s because the vendor documents its own
|
|
30
|
+
* service; a generic MCP client cannot assume that of an arbitrary server.
|
|
31
|
+
*
|
|
32
|
+
* Neither is read from the other: a permission never makes an effect known,
|
|
33
|
+
* and an effect never grants a permission.
|
|
34
|
+
*
|
|
35
|
+
* ── WHAT A REPEAT IS ─────────────────────────────────────────────────────────
|
|
36
|
+
* The same validated, authorized request sent again: the same body — the same
|
|
37
|
+
* arguments — to the same server, in the same session, as the same identity.
|
|
38
|
+
* Nothing is re-planned. A credential the library vends, or a signature, is
|
|
39
|
+
* made afresh for each attempt (signatures expire), for that same identity.
|
|
40
|
+
*
|
|
41
|
+
* ── THE POLICIES ─────────────────────────────────────────────────────────────
|
|
42
|
+
* 'compatibility' — the 11.x default, today's behaviour by name: a 429 is
|
|
43
|
+
* repeated for every tool, on the ASSUMPTION that the server refused it
|
|
44
|
+
* before running it. Each such repeat records that assumption; the
|
|
45
|
+
* attempt's effect stays `unknown`, and the caller sees the error it
|
|
46
|
+
* always did.
|
|
47
|
+
* 'app-authorized' — opt-in now, the default in the next major: a 429 or a
|
|
48
|
+
* network failure is repeated only when the app's entry for the tool
|
|
49
|
+
* names it. What the caller sees follows footprintjs's law
|
|
50
|
+
* (`RequestReplay` · `uncertain`, `callLifetime.ts`): a received refusal
|
|
51
|
+
* or error keeps the server's error as it came, its effect still
|
|
52
|
+
* `unknown`; a call that genuinely lacked an answer somewhere — an
|
|
53
|
+
* attempt with no response, or a request accepted over HTTP whose
|
|
54
|
+
* JSON-RPC answer never came — and then fails, fails with
|
|
55
|
+
* `UnknownEffectError`, its final error the cause. That needs each
|
|
56
|
+
* request matched to its call and each answer seen; a connection that
|
|
57
|
+
* cannot give both refuses the call before it is sent
|
|
58
|
+
* (`replayUnavailable.ts`), so the contract never silently weakens.
|
|
59
|
+
*
|
|
60
|
+
* Under both, the adapter the app chose retries its own refusal within the
|
|
61
|
+
* budget — AgentCore's session-busy reply — as the ADAPTER's policy, on an
|
|
62
|
+
* assumption it records the same way (`permission: { by: 'adapter', assumes }`),
|
|
63
|
+
* never as evidence. And THE SIDE-EFFECT GUARD holds: a tool the app declares
|
|
64
|
+
* as making changes is never repeated automatically unless the server is
|
|
65
|
+
* declared to deduplicate by an idempotency key the call carries —
|
|
66
|
+
* footprintjs's law, a retry of an effect without a key can apply it twice. A
|
|
67
|
+
* key protects only when the server uses it; the library cannot check that.
|
|
68
|
+
*
|
|
69
|
+
* The protocol's own requests (`initialize`, `tools/list`, notifications) run
|
|
70
|
+
* no tool. The declaration does not govern them, and a 429 on one is asked
|
|
71
|
+
* again under both policies, as it always was. They file no record.
|
|
72
|
+
*/
|
|
73
|
+
import { errorCategory } from 'footprintjs';
|
|
74
|
+
/** @internal No declaration: the compatibility policy, no tool named. */
|
|
75
|
+
export const COMPATIBILITY = Object.freeze({
|
|
76
|
+
policy: 'compatibility',
|
|
77
|
+
tools: new Map(),
|
|
78
|
+
});
|
|
79
|
+
const POLICIES = new Set(['app-authorized', 'compatibility']);
|
|
80
|
+
const KINDS = new Set(['throttle', 'network']);
|
|
81
|
+
/**
|
|
82
|
+
* @internal `options` read as a declaration — `undefined` is the compatibility
|
|
83
|
+
* policy with nothing named.
|
|
84
|
+
*
|
|
85
|
+
* @throws TypeError naming the field, never echoing a value, for anything the
|
|
86
|
+
* rule cannot mean: an unknown field, a policy or kind it does not know, an
|
|
87
|
+
* argument name that is not one.
|
|
88
|
+
*/
|
|
89
|
+
export function replayDeclarationOf(options, where) {
|
|
90
|
+
if (options === undefined)
|
|
91
|
+
return COMPATIBILITY;
|
|
92
|
+
const read = new DeclarationReader(`${where}: \`replay\``);
|
|
93
|
+
const top = read.record(options, '', ['policy', 'tools', 'deduplicates']);
|
|
94
|
+
const policy = top.policy === undefined ? 'compatibility' : read.policy(top.policy);
|
|
95
|
+
const tools = new Map();
|
|
96
|
+
if (top.tools !== undefined) {
|
|
97
|
+
const named = read.record(top.tools, '.tools', undefined);
|
|
98
|
+
for (const [tool, entry] of Object.entries(named)) {
|
|
99
|
+
if (tool.length === 0)
|
|
100
|
+
read.refuse('.tools', 'tool names that are not empty');
|
|
101
|
+
tools.set(tool, read.tool(entry, tool));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const keyArgument = top.deduplicates === undefined ? undefined : read.deduplication(top.deduplicates);
|
|
105
|
+
return Object.freeze({
|
|
106
|
+
policy,
|
|
107
|
+
tools,
|
|
108
|
+
...(keyArgument !== undefined && { keyArgument }),
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
/** Reads one declaration, refusing by field what it cannot mean. */
|
|
112
|
+
class DeclarationReader {
|
|
113
|
+
where;
|
|
114
|
+
constructor(where) {
|
|
115
|
+
this.where = where;
|
|
116
|
+
}
|
|
117
|
+
/** A plain object, its own keys within `allowed` (any key when `undefined`). */
|
|
118
|
+
record(value, at, allowed) {
|
|
119
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
120
|
+
this.refuse(at, 'an object');
|
|
121
|
+
}
|
|
122
|
+
const record = value;
|
|
123
|
+
if (allowed !== undefined) {
|
|
124
|
+
for (const key of Object.keys(record)) {
|
|
125
|
+
if (!allowed.includes(key)) {
|
|
126
|
+
this.refuse(at, `an object with only ${allowed.map((k) => `\`${k}\``).join(', ')}`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return record;
|
|
131
|
+
}
|
|
132
|
+
policy(value) {
|
|
133
|
+
if (typeof value === 'string' && POLICIES.has(value))
|
|
134
|
+
return value;
|
|
135
|
+
return this.refuse('.policy', "'app-authorized' or 'compatibility'");
|
|
136
|
+
}
|
|
137
|
+
tool(value, tool) {
|
|
138
|
+
const at = `.tools[${JSON.stringify(tool)}]`;
|
|
139
|
+
const entry = this.record(value, at, ['on', 'changes']);
|
|
140
|
+
const on = new Set();
|
|
141
|
+
if (entry.on !== undefined) {
|
|
142
|
+
if (!Array.isArray(entry.on))
|
|
143
|
+
this.refuse(`${at}.on`, "an array of 'throttle' or 'network'");
|
|
144
|
+
for (const kind of entry.on) {
|
|
145
|
+
if (typeof kind !== 'string' || !KINDS.has(kind)) {
|
|
146
|
+
this.refuse(`${at}.on`, "an array of 'throttle' or 'network'");
|
|
147
|
+
}
|
|
148
|
+
on.add(kind);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (entry.changes !== undefined && typeof entry.changes !== 'boolean') {
|
|
152
|
+
this.refuse(`${at}.changes`, 'a boolean');
|
|
153
|
+
}
|
|
154
|
+
return Object.freeze({ on, changes: entry.changes === true });
|
|
155
|
+
}
|
|
156
|
+
deduplication(value) {
|
|
157
|
+
const declared = this.record(value, '.deduplicates', ['argument']);
|
|
158
|
+
const { argument } = declared;
|
|
159
|
+
if (typeof argument === 'string' &&
|
|
160
|
+
argument.length >= 1 &&
|
|
161
|
+
argument.length <= 128 &&
|
|
162
|
+
!hasControl(argument)) {
|
|
163
|
+
return argument;
|
|
164
|
+
}
|
|
165
|
+
return this.refuse('.deduplicates.argument', 'an argument name of 1–128 characters with no control character');
|
|
166
|
+
}
|
|
167
|
+
refuse(at, wanted) {
|
|
168
|
+
throw new TypeError(`${this.where}${at} must be ${wanted}.`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* @internal Whether the declaration needs a retry series even with the 429
|
|
173
|
+
* rule off (`retryOnThrottle: false`): a tool declared repeatable on
|
|
174
|
+
* `'network'`, or the `'app-authorized'` policy — its record and its "may have
|
|
175
|
+
* run" rest on the series.
|
|
176
|
+
*/
|
|
177
|
+
export function needsSeries(declaration) {
|
|
178
|
+
if (declaration.policy === 'app-authorized')
|
|
179
|
+
return true;
|
|
180
|
+
for (const entry of declaration.tools.values())
|
|
181
|
+
if (entry.on.has('network'))
|
|
182
|
+
return true;
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
const PROTOCOL = Object.freeze({ kind: 'protocol' });
|
|
186
|
+
/** A request that may be a tool call and names no tool the rule can read: no entry applies. */
|
|
187
|
+
const UNNAMED_TOOL = Object.freeze({ kind: 'tool', keyCarried: false });
|
|
188
|
+
/**
|
|
189
|
+
* @internal `body` — the request exactly as it will be sent — read as a
|
|
190
|
+
* target. A declaration that names nothing under `'compatibility'` decides the
|
|
191
|
+
* same for every request, so its bodies are never parsed.
|
|
192
|
+
*/
|
|
193
|
+
export function replayTargetOf(body, declaration) {
|
|
194
|
+
if (declaration.policy === 'compatibility' && declaration.tools.size === 0)
|
|
195
|
+
return UNNAMED_TOOL;
|
|
196
|
+
if (body === undefined || body === null)
|
|
197
|
+
return PROTOCOL;
|
|
198
|
+
if (typeof body !== 'string')
|
|
199
|
+
return UNNAMED_TOOL;
|
|
200
|
+
let message;
|
|
201
|
+
try {
|
|
202
|
+
message = JSON.parse(body);
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
return UNNAMED_TOOL;
|
|
206
|
+
}
|
|
207
|
+
if (Array.isArray(message))
|
|
208
|
+
return message.some(isToolCall) ? UNNAMED_TOOL : PROTOCOL;
|
|
209
|
+
if (!isToolCall(message))
|
|
210
|
+
return PROTOCOL;
|
|
211
|
+
const params = message.params;
|
|
212
|
+
if (!isRecord(params))
|
|
213
|
+
return UNNAMED_TOOL;
|
|
214
|
+
const { name, arguments: args } = params;
|
|
215
|
+
const { keyArgument } = declaration;
|
|
216
|
+
return {
|
|
217
|
+
kind: 'tool',
|
|
218
|
+
...(typeof name === 'string' && { name }),
|
|
219
|
+
keyCarried: keyArgument !== undefined && carriesKey(args, keyArgument),
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
function isToolCall(message) {
|
|
223
|
+
return isRecord(message) && message.method === 'tools/call';
|
|
224
|
+
}
|
|
225
|
+
/** The arguments hold a key under `argument`: a non-empty string. The server judges the rest. */
|
|
226
|
+
function carriesKey(args, argument) {
|
|
227
|
+
if (!isRecord(args) || !Object.prototype.hasOwnProperty.call(args, argument))
|
|
228
|
+
return false;
|
|
229
|
+
const key = args[argument];
|
|
230
|
+
return typeof key === 'string' && key.length > 0;
|
|
231
|
+
}
|
|
232
|
+
function isRecord(value) {
|
|
233
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
234
|
+
}
|
|
235
|
+
/** @internal A thrown failure the rule can read: footprintjs's `network` category, or nothing. */
|
|
236
|
+
export function thrownFailure(error) {
|
|
237
|
+
return errorCategory(error) === 'network' ? { kind: 'network' } : undefined;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* @internal THE rule: may the series send `target` again after `failure`?
|
|
241
|
+
* Asked before every repeat; the budget is asked after it.
|
|
242
|
+
*/
|
|
243
|
+
export function permit(declaration, target, failure) {
|
|
244
|
+
if (target.kind === 'protocol') {
|
|
245
|
+
// No tool runs: a refusal is asked again as it always was, a failure is not.
|
|
246
|
+
return failure.kind === 'network' ? NOT_RETRYABLE : REPEAT_PROTOCOL;
|
|
247
|
+
}
|
|
248
|
+
const entry = target.name === undefined ? undefined : declaration.tools.get(target.name);
|
|
249
|
+
// The side-effect guard comes first, under every policy and every permission.
|
|
250
|
+
if (entry?.changes === true && (declaration.keyArgument === undefined || !target.keyCarried)) {
|
|
251
|
+
return CHANGES_WITHOUT_KEY;
|
|
252
|
+
}
|
|
253
|
+
const keyed = entry?.changes === true;
|
|
254
|
+
if (failure.assumes !== undefined) {
|
|
255
|
+
return repeat({ by: 'adapter', assumes: failure.assumes }, keyed);
|
|
256
|
+
}
|
|
257
|
+
if (failure.kind !== 'session-busy' && entry?.on.has(failure.kind) === true) {
|
|
258
|
+
return repeat({ by: 'declaration', on: failure.kind }, keyed);
|
|
259
|
+
}
|
|
260
|
+
if (failure.kind === 'throttle' && declaration.policy === 'compatibility') {
|
|
261
|
+
return repeat({ by: 'compatibility', assumes: 'refused-before-execution' }, keyed);
|
|
262
|
+
}
|
|
263
|
+
return failure.kind === 'throttle' ? NOT_PERMITTED : NOT_RETRYABLE;
|
|
264
|
+
}
|
|
265
|
+
const REPEAT_PROTOCOL = Object.freeze({ repeat: true });
|
|
266
|
+
const NOT_RETRYABLE = Object.freeze({ repeat: false, declined: 'not-retryable' });
|
|
267
|
+
const NOT_PERMITTED = Object.freeze({ repeat: false, declined: 'not-permitted' });
|
|
268
|
+
const CHANGES_WITHOUT_KEY = Object.freeze({
|
|
269
|
+
repeat: false,
|
|
270
|
+
declined: 'changes-without-key',
|
|
271
|
+
});
|
|
272
|
+
function repeat(permission, deduplicated) {
|
|
273
|
+
return {
|
|
274
|
+
repeat: true,
|
|
275
|
+
permission: Object.freeze({ ...permission, ...(deduplicated && { deduplicated: true }) }),
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
// ─── What is known of a series' effect ───────────────────────────────
|
|
279
|
+
/**
|
|
280
|
+
* @internal What is known of a whole series' effect, one attempt at a time.
|
|
281
|
+
* Uncertainty survives: once any attempt is `unknown`, the series is, whatever
|
|
282
|
+
* later attempts show — a proven refusal does not resolve it, and a success
|
|
283
|
+
* does not prove the operation happened only once. Evidence that something ran
|
|
284
|
+
* (`applied`) is never erased by a later failure.
|
|
285
|
+
*/
|
|
286
|
+
export class SeriesKnowledge {
|
|
287
|
+
unknown = false;
|
|
288
|
+
applied = false;
|
|
289
|
+
note(effect) {
|
|
290
|
+
if (effect === 'unknown')
|
|
291
|
+
this.unknown = true;
|
|
292
|
+
else if (effect === 'applied')
|
|
293
|
+
this.applied = true;
|
|
294
|
+
}
|
|
295
|
+
get effect() {
|
|
296
|
+
if (this.unknown)
|
|
297
|
+
return 'unknown';
|
|
298
|
+
return this.applied ? 'applied' : 'none';
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* @internal One `tools/call` request's replay facts, shared by the series that
|
|
303
|
+
* sends it, the ledger that watches its answer, and the call that waits for it
|
|
304
|
+
* — the ledger hands all three the same object (`requestLedger.ts`).
|
|
305
|
+
*
|
|
306
|
+
* TWO LAYERS, KEPT APART. The series sees HTTP: whether an attempt got a
|
|
307
|
+
* response at all, and the status of the one it delivered. A response is not
|
|
308
|
+
* the MCP call's answer — a 202, or a stream, can be accepted and never carry
|
|
309
|
+
* it — so the ledger watches the connection's incoming messages for the
|
|
310
|
+
* JSON-RPC response to THIS request's id. And "its effect is unknown" is not
|
|
311
|
+
* "it lacked an answer": a received 429 or error is an answer whose effect is
|
|
312
|
+
* unknown.
|
|
313
|
+
*
|
|
314
|
+
* Each fact is written once, by its owner: the series (what it sent, whether an
|
|
315
|
+
* attempt lacked any response, what it delivered, the policy), the ledger (the
|
|
316
|
+
* logical answer), the call (how it settled).
|
|
317
|
+
*/
|
|
318
|
+
export class RequestReplay {
|
|
319
|
+
id;
|
|
320
|
+
requests = 0;
|
|
321
|
+
appAuthorized = false;
|
|
322
|
+
attemptUnanswered = false;
|
|
323
|
+
delivered;
|
|
324
|
+
logical;
|
|
325
|
+
settled;
|
|
326
|
+
failedAsMayHaveRun = false;
|
|
327
|
+
/**
|
|
328
|
+
* @param id the request's JSON-RPC id, how its answer is found.
|
|
329
|
+
* @param watched whether the connection's incoming messages are observed —
|
|
330
|
+
* without that, the logical layer is unknown and never decides anything.
|
|
331
|
+
*/
|
|
332
|
+
constructor(id, watched) {
|
|
333
|
+
this.id = id;
|
|
334
|
+
this.logical = watched ? 'awaited' : 'unwatched';
|
|
335
|
+
}
|
|
336
|
+
/** The series sent the request once more. */
|
|
337
|
+
sentOne() {
|
|
338
|
+
this.requests += 1;
|
|
339
|
+
}
|
|
340
|
+
/** The series runs under the `'app-authorized'` policy. */
|
|
341
|
+
appAuthorizedPolicy() {
|
|
342
|
+
this.appAuthorized = true;
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* The series: an attempt genuinely lacked an answer — no response arrived
|
|
346
|
+
* (a lost connection, a timeout, a cut). A received refusal is never this.
|
|
347
|
+
*/
|
|
348
|
+
noAnswer() {
|
|
349
|
+
this.attemptUnanswered = true;
|
|
350
|
+
}
|
|
351
|
+
/** The series handed the MCP SDK a response with this status. */
|
|
352
|
+
deliveredStatus(status) {
|
|
353
|
+
this.delivered = status;
|
|
354
|
+
}
|
|
355
|
+
/** The ledger: the JSON-RPC response to this request's id arrived. */
|
|
356
|
+
answered() {
|
|
357
|
+
if (this.logical === 'awaited')
|
|
358
|
+
this.logical = 'answered';
|
|
359
|
+
}
|
|
360
|
+
/** The call settled; `mayHaveRun` when it failed as "may have run". The first word stands. */
|
|
361
|
+
settle(verdict, mayHaveRun) {
|
|
362
|
+
if (this.settled !== undefined)
|
|
363
|
+
return;
|
|
364
|
+
this.settled = verdict;
|
|
365
|
+
this.failedAsMayHaveRun = mayHaveRun;
|
|
366
|
+
}
|
|
367
|
+
get sent() {
|
|
368
|
+
return this.requests;
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Under `'app-authorized'`, a call that does not complete fails as "may have
|
|
372
|
+
* run" when the call genuinely lacked an answer somewhere: an attempt got no
|
|
373
|
+
* response, or the HTTP layer accepted the request (a 2xx) and the JSON-RPC
|
|
374
|
+
* response never came. A chain of received refusals and errors is an answer
|
|
375
|
+
* throughout: it keeps the server's error.
|
|
376
|
+
*/
|
|
377
|
+
get uncertain() {
|
|
378
|
+
return this.appAuthorized && (this.attemptUnanswered || this.acceptedWithoutAnswer);
|
|
379
|
+
}
|
|
380
|
+
/** The HTTP layer took the request (2xx), and the logical answer never arrived. */
|
|
381
|
+
get acceptedWithoutAnswer() {
|
|
382
|
+
const status = this.delivered;
|
|
383
|
+
return this.logical === 'awaited' && status !== undefined && status >= 200 && status < 300;
|
|
384
|
+
}
|
|
385
|
+
get verdict() {
|
|
386
|
+
return this.settled;
|
|
387
|
+
}
|
|
388
|
+
get mayHaveRun() {
|
|
389
|
+
return this.failedAsMayHaveRun;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
/** Whether `text` holds a control character (C0, DEL or C1). */
|
|
393
|
+
function hasControl(text) {
|
|
394
|
+
for (let i = 0; i < text.length; i++) {
|
|
395
|
+
const code = text.charCodeAt(i);
|
|
396
|
+
if (code < 0x20 || (code >= 0x7f && code <= 0x9f))
|
|
397
|
+
return true;
|
|
398
|
+
}
|
|
399
|
+
return false;
|
|
400
|
+
}
|
|
401
|
+
//# sourceMappingURL=replay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replay.js","sourceRoot":"","sources":["../../../src/mcp/replay.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* replayUnavailable — when a connection cannot keep the `'app-authorized'`
|
|
3
|
+
* contract, a call under it is refused BEFORE it is sent, and the error says why.
|
|
4
|
+
*
|
|
5
|
+
* Pattern: a readiness rule + one typed refusal.
|
|
6
|
+
* Role: Under `'app-authorized'`, a call that genuinely lacked an answer and
|
|
7
|
+
* then fails is raised as "may have run" (`callLifetime.ts`). That
|
|
8
|
+
* rests on two things the library must ESTABLISH for every call,
|
|
9
|
+
* never assume:
|
|
10
|
+
*
|
|
11
|
+
* 1. ITS REQUEST IS MATCHED to the call, by the call's private marker, taken
|
|
12
|
+
* at the transport's `send` (`callMarker.ts`, `requestLedger.ts`).
|
|
13
|
+
* - `'requests-unwatched'`: the library cannot watch this transport's
|
|
14
|
+
* `send` at all (stdio, or a transport that refuses the wrap).
|
|
15
|
+
* - `'request-unmatched'`: a request reached `send` without its call's
|
|
16
|
+
* marker (an SDK that rebuilt the params and dropped `_meta`), so it
|
|
17
|
+
* cannot be tied to any call.
|
|
18
|
+
* 2. ITS ANSWER IS SEEN: the JSON-RPC response to the request's id, read as
|
|
19
|
+
* it arrives. This is checked once the connection is initialized: the
|
|
20
|
+
* `initialize` answer must have passed the library's observer.
|
|
21
|
+
* - `'answers-unobserved'`: it did not. An MCP SDK whose `connect`
|
|
22
|
+
* REPLACES the transport's `onmessage` handler instead of keeping it
|
|
23
|
+
* (SDK 1.10's `Protocol.connect` does) leaves the observer out.
|
|
24
|
+
*
|
|
25
|
+
* Without either, a request the server accepted and never answered would reach
|
|
26
|
+
* the caller as a plain timeout: the strict contract, silently weakened. So the
|
|
27
|
+
* call is refused instead, and nothing is sent. `'compatibility'` asks none of
|
|
28
|
+
* this and keeps its behaviour.
|
|
29
|
+
*/
|
|
30
|
+
/** Why a call under `'app-authorized'` was refused before it was sent. */
|
|
31
|
+
export type McpReplayUnavailableReason = 'requests-unwatched' | 'answers-unobserved' | 'request-unmatched';
|
|
32
|
+
/**
|
|
33
|
+
* A tool call refused before it was sent, because this connection cannot keep
|
|
34
|
+
* the `'app-authorized'` replay contract (`reason`). Nothing reached the server.
|
|
35
|
+
* Names the server and the tool, never an argument.
|
|
36
|
+
*/
|
|
37
|
+
export declare class McpReplayUnavailableError extends Error {
|
|
38
|
+
readonly reason: McpReplayUnavailableReason;
|
|
39
|
+
readonly server: string;
|
|
40
|
+
readonly tool: string;
|
|
41
|
+
readonly code: "ERR_MCP_REPLAY_UNAVAILABLE";
|
|
42
|
+
/** Nothing was sent: the refusal came before the request left the client. */
|
|
43
|
+
readonly sent: false;
|
|
44
|
+
constructor(reason: McpReplayUnavailableReason, server: string, tool: string);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Why a connection cannot take a call under `'app-authorized'` before anything
|
|
48
|
+
* is sent — `undefined` when it can. `ledger` is the connection's request
|
|
49
|
+
* ledger: absent when the library could not watch the transport's requests.
|
|
50
|
+
* A request that reaches the transport without its marker is refused there, by
|
|
51
|
+
* the ledger (`'request-unmatched'`).
|
|
52
|
+
*/
|
|
53
|
+
export declare function unavailableBeforeSending(ledger: {
|
|
54
|
+
readonly answersObserved: boolean;
|
|
55
|
+
} | undefined): McpReplayUnavailableReason | undefined;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* replayUnavailable — when a connection cannot keep the `'app-authorized'`
|
|
3
|
+
* contract, a call under it is refused BEFORE it is sent, and the error says why.
|
|
4
|
+
*
|
|
5
|
+
* Pattern: a readiness rule + one typed refusal.
|
|
6
|
+
* Role: Under `'app-authorized'`, a call that genuinely lacked an answer and
|
|
7
|
+
* then fails is raised as "may have run" (`callLifetime.ts`). That
|
|
8
|
+
* rests on two things the library must ESTABLISH for every call,
|
|
9
|
+
* never assume:
|
|
10
|
+
*
|
|
11
|
+
* 1. ITS REQUEST IS MATCHED to the call, by the call's private marker, taken
|
|
12
|
+
* at the transport's `send` (`callMarker.ts`, `requestLedger.ts`).
|
|
13
|
+
* - `'requests-unwatched'`: the library cannot watch this transport's
|
|
14
|
+
* `send` at all (stdio, or a transport that refuses the wrap).
|
|
15
|
+
* - `'request-unmatched'`: a request reached `send` without its call's
|
|
16
|
+
* marker (an SDK that rebuilt the params and dropped `_meta`), so it
|
|
17
|
+
* cannot be tied to any call.
|
|
18
|
+
* 2. ITS ANSWER IS SEEN: the JSON-RPC response to the request's id, read as
|
|
19
|
+
* it arrives. This is checked once the connection is initialized: the
|
|
20
|
+
* `initialize` answer must have passed the library's observer.
|
|
21
|
+
* - `'answers-unobserved'`: it did not. An MCP SDK whose `connect`
|
|
22
|
+
* REPLACES the transport's `onmessage` handler instead of keeping it
|
|
23
|
+
* (SDK 1.10's `Protocol.connect` does) leaves the observer out.
|
|
24
|
+
*
|
|
25
|
+
* Without either, a request the server accepted and never answered would reach
|
|
26
|
+
* the caller as a plain timeout: the strict contract, silently weakened. So the
|
|
27
|
+
* call is refused instead, and nothing is sent. `'compatibility'` asks none of
|
|
28
|
+
* this and keeps its behaviour.
|
|
29
|
+
*/
|
|
30
|
+
/** The fixed sentence each reason reads as. Never an argument, a header or an answer. */
|
|
31
|
+
const BECAUSE = {
|
|
32
|
+
'requests-unwatched': "the library cannot watch this transport's requests, so it cannot match a request to its call",
|
|
33
|
+
'answers-unobserved': "this connection's answers do not pass the library's observer: the MCP SDK replaced the " +
|
|
34
|
+
"transport's message handler when it connected",
|
|
35
|
+
'request-unmatched': "the request reached the transport without its call's marker: the MCP SDK rebuilt the " +
|
|
36
|
+
"request's params",
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* A tool call refused before it was sent, because this connection cannot keep
|
|
40
|
+
* the `'app-authorized'` replay contract (`reason`). Nothing reached the server.
|
|
41
|
+
* Names the server and the tool, never an argument.
|
|
42
|
+
*/
|
|
43
|
+
export class McpReplayUnavailableError extends Error {
|
|
44
|
+
reason;
|
|
45
|
+
server;
|
|
46
|
+
tool;
|
|
47
|
+
code = 'ERR_MCP_REPLAY_UNAVAILABLE';
|
|
48
|
+
/** Nothing was sent: the refusal came before the request left the client. */
|
|
49
|
+
sent = false;
|
|
50
|
+
constructor(reason, server, tool) {
|
|
51
|
+
super(`mcpClient[${server}]: the call to '${tool}' was not sent. The 'app-authorized' replay ` +
|
|
52
|
+
`policy needs each request matched to its call and each answer seen, and ` +
|
|
53
|
+
`${BECAUSE[reason]}.`);
|
|
54
|
+
this.reason = reason;
|
|
55
|
+
this.server = server;
|
|
56
|
+
this.tool = tool;
|
|
57
|
+
this.name = 'McpReplayUnavailableError';
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Why a connection cannot take a call under `'app-authorized'` before anything
|
|
62
|
+
* is sent — `undefined` when it can. `ledger` is the connection's request
|
|
63
|
+
* ledger: absent when the library could not watch the transport's requests.
|
|
64
|
+
* A request that reaches the transport without its marker is refused there, by
|
|
65
|
+
* the ledger (`'request-unmatched'`).
|
|
66
|
+
*/
|
|
67
|
+
export function unavailableBeforeSending(ledger) {
|
|
68
|
+
if (ledger === undefined)
|
|
69
|
+
return 'requests-unwatched';
|
|
70
|
+
if (!ledger.answersObserved)
|
|
71
|
+
return 'answers-unobserved';
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=replayUnavailable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replayUnavailable.js","sourceRoot":"","sources":["../../../src/mcp/replayUnavailable.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAQH,yFAAyF;AACzF,MAAM,OAAO,GAAyD;IACpE,oBAAoB,EAClB,8FAA8F;IAChG,oBAAoB,EAClB,yFAAyF;QACzF,+CAA+C;IACjD,mBAAmB,EACjB,uFAAuF;QACvF,kBAAkB;CACrB,CAAC;AAEF;;;;GAIG;AACH,MAAM,OAAO,yBAA0B,SAAQ,KAAK;IAMvC;IACA;IACA;IAPF,IAAI,GAAG,4BAAqC,CAAC;IACtD,6EAA6E;IACpE,IAAI,GAAG,KAAc,CAAC;IAE/B,YACW,MAAkC,EAClC,MAAc,EACd,IAAY;QAErB,KAAK,CACH,aAAa,MAAM,mBAAmB,IAAI,8CAA8C;YACtF,0EAA0E;YAC1E,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CACxB,CAAC;QARO,WAAM,GAAN,MAAM,CAA4B;QAClC,WAAM,GAAN,MAAM,CAAQ;QACd,SAAI,GAAJ,IAAI,CAAQ;QAOrB,IAAI,CAAC,IAAI,GAAG,2BAA2B,CAAC;IAC1C,CAAC;CACF;AAED;;;;;;GAMG;AACH,MAAM,UAAU,wBAAwB,CACtC,MAAyD;IAEzD,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,oBAAoB,CAAC;IACtD,IAAI,CAAC,MAAM,CAAC,eAAe;QAAE,OAAO,oBAAoB,CAAC;IACzD,OAAO,SAAS,CAAC;AACnB,CAAC"}
|