@astral-mcp/projection 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +93 -22
- package/dist/index.d.mts +1448 -423
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +8626 -1805
- package/dist/index.mjs.map +1 -1
- package/package.json +13 -8
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,142 @@
|
|
|
1
|
+
import { Client } from "@modelcontextprotocol/client";
|
|
1
2
|
import "@sebastianwessel/quickjs";
|
|
2
3
|
import { CallToolResult, ContentBlock, ElicitRequestFormParams, HandlerResultTypeMap, Implementation, InputRequest, InputRequests, InputRequiredResult, InputResponse, InputResponseView, InputResponses, InputResponses as InputResponses$1, McpServer, McpServerFactory, ProtocolEra, ProtocolEra as ProtocolEra$1, acceptedContent, inputRequired, inputResponse, isInputRequiredResult } from "@modelcontextprotocol/server";
|
|
3
|
-
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Token residence (PRD R59, S1): the {@link TokenStore} contract, its keys,
|
|
6
|
+
* the in-memory store, and the default store — the OS credential store
|
|
7
|
+
* through the optional native library `@napi-rs/keyring` (macOS Keychain,
|
|
8
|
+
* Linux Secret Service, Windows Credential Manager), with an in-memory
|
|
9
|
+
* fallback.
|
|
10
|
+
*
|
|
11
|
+
* A store holds opaque strings. What Projection stores under a `tokens:`
|
|
12
|
+
* key is its own serialization of a token set (`token-record.ts`); a store
|
|
13
|
+
* never parses it.
|
|
14
|
+
*
|
|
15
|
+
* Nothing in this module logs, rethrows or embeds a stored value or an
|
|
16
|
+
* error from the keyring library: a store failure is reported in fixed
|
|
17
|
+
* library copy.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Where upstream credentials live (R59). `WrapOptions.tokenStore` replaces
|
|
21
|
+
* the default OS-credential-store one; a horizontally scaled deployment
|
|
22
|
+
* supplies a shared store (R27).
|
|
23
|
+
*
|
|
24
|
+
* Keys and values are opaque strings. A key is `<kind>:<JSON array>`; the
|
|
25
|
+
* kind before the first `:` says what the value is, so a store can route
|
|
26
|
+
* or label entries without parsing anything else:
|
|
27
|
+
*
|
|
28
|
+
* - `tokens:["<canonical resource URL>","<client id>","<principal>"]` — a
|
|
29
|
+
* token set ({@link tokenStoreKey});
|
|
30
|
+
* - `registration:…` — reserved for dynamic client registrations (phase 1b).
|
|
31
|
+
*
|
|
32
|
+
* Values are Projection's serialization (ASCII JSON) and must be returned
|
|
33
|
+
* byte for byte.
|
|
34
|
+
*
|
|
35
|
+
* - `get` returns the value stored under `key`, or `undefined`.
|
|
36
|
+
* - `set` replaces it.
|
|
37
|
+
* - `delete` removes it and reports whether it removed anything. With
|
|
38
|
+
* `expected`, it is a compare-and-delete (R58): it deletes only when the
|
|
39
|
+
* stored value is still exactly `expected`, so a value another process
|
|
40
|
+
* has just stored survives.
|
|
41
|
+
*
|
|
42
|
+
* Implementations must never log or surface a value.
|
|
43
|
+
*/
|
|
44
|
+
interface TokenStore {
|
|
45
|
+
get(key: string): Promise<string | undefined>;
|
|
46
|
+
set(key: string, value: string): Promise<void>;
|
|
47
|
+
delete(key: string, expected?: string): Promise<boolean>;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The process-local store: the fallback, and what tests and single-process
|
|
51
|
+
* deployments that want no persistence can pass as `tokenStore`.
|
|
52
|
+
*/
|
|
53
|
+
export declare class MemoryTokenStore implements TokenStore {
|
|
54
|
+
#private;
|
|
55
|
+
get(key: string): Promise<string | undefined>;
|
|
56
|
+
set(key: string, value: string): Promise<void>;
|
|
57
|
+
delete(key: string, expected?: string): Promise<boolean>;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Upstream sources given by URL (PRD §6.10, R51, R60): the `UpstreamSpec`
|
|
61
|
+
* shape, how it is told apart from the other source kinds, and its
|
|
62
|
+
* validation and canonical form.
|
|
63
|
+
*
|
|
64
|
+
* Every mistake here is a `validate` {@link ProjectionError} raised at wrap
|
|
65
|
+
* time, before anything connects (R39's rule). Messages never echo a value
|
|
66
|
+
* that could be a secret: the client secret is never quoted, and a URL is
|
|
67
|
+
* quoted only after it parsed without userinfo.
|
|
68
|
+
*/
|
|
69
|
+
/**
|
|
70
|
+
* OAuth settings of an {@link UpstreamSpec} (R51, R52, R53, R61). Phase 1
|
|
71
|
+
* needs a client the integrator registered beforehand: `clientId`, and a
|
|
72
|
+
* discouraged `clientSecret`. `clientMetadataUrl` and
|
|
73
|
+
* `authorizationEndpoint` arrive with phase 1b and are `validate` errors
|
|
74
|
+
* until then.
|
|
75
|
+
*/
|
|
76
|
+
interface UpstreamOAuthOptions {
|
|
77
|
+
/** The OAuth client id the integrator registered with the server's authorization server (R52). */
|
|
78
|
+
clientId: string;
|
|
79
|
+
/** Client secret for a confidential client. Discouraged: the device flow needs none (R52). */
|
|
80
|
+
clientSecret?: string;
|
|
81
|
+
/** Scopes to request, replacing the SDK's selection from metadata (R53). */
|
|
82
|
+
scopes?: string[];
|
|
83
|
+
/**
|
|
84
|
+
* Extra hosts the user may be sent to for sign-in, beyond the
|
|
85
|
+
* authorization server's own (R61). Bare lowercase ASCII hostnames, not IP addresses.
|
|
86
|
+
*/
|
|
87
|
+
signInHosts?: string[];
|
|
88
|
+
/** Token endpoint override for servers with incomplete metadata (R53). `https:` only. */
|
|
89
|
+
tokenEndpoint?: string;
|
|
90
|
+
/** Device authorization endpoint override (R53). `https:` only. */
|
|
91
|
+
deviceAuthorizationEndpoint?: string;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* A remote MCP server given by its URL (R51). Projection builds the
|
|
95
|
+
* Streamable HTTP transport itself, installs its own OAuth client provider
|
|
96
|
+
* on it, owns the resulting connection and closes it in `close()`.
|
|
97
|
+
*/
|
|
98
|
+
interface UpstreamSpec {
|
|
99
|
+
/** The server's Streamable HTTP endpoint. `https:`, or `http:` on a loopback host. */
|
|
100
|
+
url: string | URL;
|
|
101
|
+
oauth?: UpstreamOAuthOptions;
|
|
102
|
+
}
|
|
103
|
+
/** A validated {@link UpstreamOAuthOptions}, frozen at wrap time. */
|
|
104
|
+
interface ResolvedOAuth {
|
|
105
|
+
readonly clientId: string;
|
|
106
|
+
readonly clientSecret?: string;
|
|
107
|
+
readonly scopes?: readonly string[];
|
|
108
|
+
readonly signInHosts?: readonly string[];
|
|
109
|
+
readonly tokenEndpoint?: string;
|
|
110
|
+
readonly deviceAuthorizationEndpoint?: string;
|
|
111
|
+
}
|
|
112
|
+
/** A validated {@link UpstreamSpec}. */
|
|
113
|
+
interface ResolvedUpstream {
|
|
114
|
+
/** The parsed URL the transport connects to. */
|
|
115
|
+
readonly url: URL;
|
|
116
|
+
/** The canonical URL (R51): the resource's identity, token-store key and blob binding. */
|
|
117
|
+
readonly canonicalUrl: string;
|
|
118
|
+
/** The R60 per-server identity, `url:<canonical url>`. */
|
|
119
|
+
readonly identity: string;
|
|
120
|
+
/** The URL's hostname as the parser gives it (ASCII, punycode for IDNs). */
|
|
121
|
+
readonly host: string;
|
|
122
|
+
readonly oauth?: ResolvedOAuth;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Why an upstream auth step failed, in library terms:
|
|
126
|
+
*
|
|
127
|
+
* - `sign_in_required`: no usable token (none stored, none that refreshes).
|
|
128
|
+
* - `no_client`: the server requires sign-in and no `oauth.clientId` is configured (R52).
|
|
129
|
+
* - `insufficient_scope`: the granted scopes do not cover the request (R58).
|
|
130
|
+
* - `refresh_failed`: a refresh could not complete for a transient reason
|
|
131
|
+
* (a 5xx, a malformed response, a network failure); the stored record is kept.
|
|
132
|
+
* - `client_rejected`: the authorization server refused the OAuth client
|
|
133
|
+
* (`invalid_client` / `unauthorized_client`); the stored record is kept.
|
|
134
|
+
* - `insecure_endpoint`: an authorization-server endpoint is not `https:` (R61).
|
|
135
|
+
* - `oauth_error`: the authorization server refused (its `error` code, when valid, rides along).
|
|
136
|
+
* - `token_store`: the token store failed.
|
|
137
|
+
* - `failed`: anything else in the auth machinery (invalid metadata, for example).
|
|
138
|
+
*/
|
|
139
|
+
type UpstreamAuthReason = 'sign_in_required' | 'no_client' | 'insufficient_scope' | 'refresh_failed' | 'client_rejected' | 'insecure_endpoint' | 'oauth_error' | 'token_store' | 'failed';
|
|
5
140
|
/**
|
|
6
141
|
* Shared core types for Projection.
|
|
7
142
|
*
|
|
@@ -11,16 +146,16 @@ import { Client } from "@modelcontextprotocol/client";
|
|
|
11
146
|
* nothing inside the package.
|
|
12
147
|
*/
|
|
13
148
|
/** Alias identifying a wrapped server. v1 instantiates exactly one. */
|
|
14
|
-
type ServerAlias = string;
|
|
15
|
-
declare const DEFAULT_SERVER_ALIAS: ServerAlias;
|
|
149
|
+
export type ServerAlias = string;
|
|
150
|
+
export declare const DEFAULT_SERVER_ALIAS: ServerAlias;
|
|
16
151
|
/** MCP tool annotations we care about (untrusted hints — S5). */
|
|
17
|
-
interface ToolAnnotations {
|
|
152
|
+
export interface ToolAnnotations {
|
|
18
153
|
readOnlyHint?: boolean;
|
|
19
154
|
destructiveHint?: boolean;
|
|
20
155
|
idempotentHint?: boolean;
|
|
21
156
|
title?: string;
|
|
22
157
|
}
|
|
23
|
-
interface ToolCatalogEntry {
|
|
158
|
+
export interface ToolCatalogEntry {
|
|
24
159
|
/** Tool name as declared by the wrapped server. */
|
|
25
160
|
name: string;
|
|
26
161
|
description?: string;
|
|
@@ -30,11 +165,11 @@ interface ToolCatalogEntry {
|
|
|
30
165
|
outputSchema?: Record<string, unknown>;
|
|
31
166
|
annotations?: ToolAnnotations;
|
|
32
167
|
}
|
|
33
|
-
interface ToolCatalog {
|
|
168
|
+
export interface ToolCatalog {
|
|
34
169
|
/** Keyed by server alias (R30). v1 has exactly one entry. */
|
|
35
170
|
servers: Map<ServerAlias, ServerCatalog>;
|
|
36
171
|
}
|
|
37
|
-
interface ServerCatalog {
|
|
172
|
+
export interface ServerCatalog {
|
|
38
173
|
alias: ServerAlias;
|
|
39
174
|
/** Stable identity string recorded into blobs (R30). */
|
|
40
175
|
identity: string;
|
|
@@ -50,7 +185,7 @@ interface ServerCatalog {
|
|
|
50
185
|
* must not be able to make the policy gate see a different entry (e.g. a
|
|
51
186
|
* later `readOnlyHint: true` copy) than the one typegen exposed.
|
|
52
187
|
*/
|
|
53
|
-
declare function findTool(catalog: ServerCatalog, name: string): ToolCatalogEntry | undefined;
|
|
188
|
+
export declare function findTool(catalog: ServerCatalog, name: string): ToolCatalogEntry | undefined;
|
|
54
189
|
/**
|
|
55
190
|
* Drop tools whose name repeats an earlier one; the first occurrence wins.
|
|
56
191
|
*
|
|
@@ -58,30 +193,173 @@ declare function findTool(catalog: ServerCatalog, name: string): ToolCatalogEntr
|
|
|
58
193
|
* catalog projection), so they all agree with {@link findTool} on which
|
|
59
194
|
* entry a name denotes (S5).
|
|
60
195
|
*/
|
|
61
|
-
declare function dedupeTools(tools: ToolCatalogEntry[]): ToolCatalogEntry[];
|
|
196
|
+
export declare function dedupeTools(tools: ToolCatalogEntry[]): ToolCatalogEntry[];
|
|
62
197
|
/** Narrow to a non-null, non-array object. */
|
|
63
|
-
declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
198
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
199
|
+
/** A plain object (prototype `Object.prototype` or `null`): what a configuration value must be. */
|
|
200
|
+
export declare function isPlainObject(value: unknown): value is Record<string, unknown>;
|
|
201
|
+
/**
|
|
202
|
+
* One wrapped-server progress report (MCP `notifications/progress`), as the
|
|
203
|
+
* upstream sent it: `message` is server-authored text (S5), not yet
|
|
204
|
+
* neutralized.
|
|
205
|
+
*/
|
|
206
|
+
export interface UpstreamProgress {
|
|
207
|
+
progress: number;
|
|
208
|
+
total?: number;
|
|
209
|
+
message?: string;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Host-side options for one upstream call: never model-authored. `signal`
|
|
213
|
+
* cancels the upstream request (MCP `notifications/cancelled`) when the
|
|
214
|
+
* facade's own request is cancelled; `onProgress` receives the upstream's
|
|
215
|
+
* progress reports, and its presence asks the upstream for them.
|
|
216
|
+
*/
|
|
217
|
+
export interface UpstreamCallOptions {
|
|
218
|
+
signal?: AbortSignal;
|
|
219
|
+
onProgress?: (progress: UpstreamProgress) => void;
|
|
220
|
+
/**
|
|
221
|
+
* Re-issue a call whose upstream asked for input (G1, DECISIONS D24): the
|
|
222
|
+
* answers keyed by the upstream's own keys, and the upstream's own
|
|
223
|
+
* `requestState` echoed verbatim. Read only from a verified blob and the
|
|
224
|
+
* human's responses, never from tool arguments.
|
|
225
|
+
*/
|
|
226
|
+
input?: UpstreamInputResume;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* What a wrapped server asked for when its tool answered `input_required`
|
|
230
|
+
* (MCP 2026-07-28 MRTR), as received: untrusted server data (S5), not yet
|
|
231
|
+
* sanitized. Present only for a box that forwards upstream elicitation.
|
|
232
|
+
*/
|
|
233
|
+
export interface UpstreamInputRequest {
|
|
234
|
+
/** The upstream's embedded requests, keyed by its own keys. */
|
|
235
|
+
requests: Record<string, unknown>;
|
|
236
|
+
/** The upstream's opaque state, to be echoed back verbatim. */
|
|
237
|
+
requestState?: string;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Whether a box passes its server's own questions (MCP elicitation, form or
|
|
241
|
+
* URL mode) on to the human (G1, DECISIONS D24). `'refuse'` (the default)
|
|
242
|
+
* declares no elicitation capability to the server, and a question fails the
|
|
243
|
+
* call with a result that says so. `'forward'` declares the capability and
|
|
244
|
+
* relays each question over MRTR, attributed to the box and never shaped
|
|
245
|
+
* like an approval. Sampling and roots are never forwarded.
|
|
246
|
+
*/
|
|
247
|
+
export type UpstreamElicitationMode = 'forward' | 'refuse';
|
|
248
|
+
/** The answers and state that re-issue a call whose upstream asked for input. */
|
|
249
|
+
export interface UpstreamInputResume {
|
|
250
|
+
/** Bare `ElicitResult`s keyed by the upstream's own request keys. */
|
|
251
|
+
responses: Record<string, unknown>;
|
|
252
|
+
requestState?: string;
|
|
253
|
+
}
|
|
64
254
|
/** Executes a tool call against the wrapped server. Implemented by the introspector. */
|
|
65
|
-
interface ToolExecutor {
|
|
66
|
-
callTool(alias: ServerAlias, name: string, args: Record<string, unknown
|
|
255
|
+
export interface ToolExecutor {
|
|
256
|
+
callTool(alias: ServerAlias, name: string, args: Record<string, unknown>, options?: UpstreamCallOptions): Promise<ToolCallOutcome>;
|
|
67
257
|
}
|
|
68
|
-
type ToolCallOutcome = {
|
|
258
|
+
export type ToolCallOutcome = {
|
|
69
259
|
ok: true;
|
|
70
260
|
result: unknown;
|
|
261
|
+
/**
|
|
262
|
+
* Set when `result` is the upstream's raw `content` array (no
|
|
263
|
+
* `structuredContent`, not a single text block), so a renderer can
|
|
264
|
+
* tell content blocks from structured data that happens to be an
|
|
265
|
+
* array.
|
|
266
|
+
*/
|
|
267
|
+
rawContent?: true;
|
|
268
|
+
/**
|
|
269
|
+
* Short text the server sent beside its `structuredContent` that is
|
|
270
|
+
* not a JSON mirror of it (a warning such as "returning 10 of 40"),
|
|
271
|
+
* raw but redacted like the result (S1). Shown to the model only as
|
|
272
|
+
* a neutralized, quoted result note (D29, `catalog/result-notes.ts`).
|
|
273
|
+
*/
|
|
274
|
+
serverNote?: string;
|
|
71
275
|
} | {
|
|
72
276
|
ok: false;
|
|
73
277
|
error: {
|
|
74
278
|
message: string;
|
|
75
279
|
detail?: unknown;
|
|
76
280
|
};
|
|
281
|
+
/**
|
|
282
|
+
* Set when the call failed because upstream authorization failed (a
|
|
283
|
+
* source given by URL, R51): `call_tool` reports it in the R25 `auth`
|
|
284
|
+
* phase with this library-copy hint, not as an `upstream` failure.
|
|
285
|
+
*/
|
|
286
|
+
auth?: {
|
|
287
|
+
hint: string;
|
|
288
|
+
};
|
|
289
|
+
/**
|
|
290
|
+
* The upstream asked the user for input and its box forwards upstream
|
|
291
|
+
* elicitation (G1): the gated entry points turn this into their own
|
|
292
|
+
* `input_required`. A consumer that does not handle it sees an
|
|
293
|
+
* ordinary failure, so nothing is ever forwarded by accident.
|
|
294
|
+
*/
|
|
295
|
+
upstreamInput?: UpstreamInputRequest;
|
|
296
|
+
/**
|
|
297
|
+
* The upstream asked the user for input that is not forwarded (its
|
|
298
|
+
* box refuses upstream elicitation, or it asked for sampling or
|
|
299
|
+
* roots). `error.message` says so; the arguments were not the problem.
|
|
300
|
+
*/
|
|
301
|
+
inputRefused?: true;
|
|
77
302
|
};
|
|
78
|
-
type PolicyDecision = 'auto' | 'ask' | 'deny';
|
|
79
|
-
interface PolicyOverrides {
|
|
303
|
+
export type PolicyDecision = 'auto' | 'ask' | 'deny';
|
|
304
|
+
export interface PolicyOverrides {
|
|
80
305
|
/** Per-tool decision override, keyed by tool name (v1: single server). */
|
|
81
306
|
[toolName: string]: PolicyDecision;
|
|
82
307
|
}
|
|
308
|
+
/**
|
|
309
|
+
* Approvals mode (R49). `'every-write'` (the default) asks before every tool
|
|
310
|
+
* that is not read-only; `'destructive-only'` also lets a non-read-only tool
|
|
311
|
+
* run without approval when its annotations declare `destructiveHint: false`
|
|
312
|
+
* exactly. That extends the trust placed in `readOnlyHint` to
|
|
313
|
+
* `destructiveHint: false` (S5): enable it only for servers you trust.
|
|
314
|
+
*/
|
|
315
|
+
export type ApprovalsMode = 'every-write' | 'destructive-only';
|
|
316
|
+
/**
|
|
317
|
+
* Everything a policy decision reads besides the tool itself (R14, R49):
|
|
318
|
+
* the per-tool overrides and the resolved approvals mode, both frozen at
|
|
319
|
+
* wrap time. Every surface decides through `decidePolicy` with one of these
|
|
320
|
+
* (build it with `policyContext`), so the posture a model is shown is the
|
|
321
|
+
* decision the gate makes.
|
|
322
|
+
*
|
|
323
|
+
* `approvals` is required — the context is *resolved* — so a bare
|
|
324
|
+
* {@link PolicyOverrides} object cannot be passed where a context is
|
|
325
|
+
* expected and silently lose its overrides. A multi-server facade adds its
|
|
326
|
+
* per-source modes as `toolApprovals`, resolved per qualified tool (they
|
|
327
|
+
* take precedence over `approvals`, R49), without changing `decidePolicy`'s
|
|
328
|
+
* signature.
|
|
329
|
+
*/
|
|
330
|
+
export interface PolicyContext {
|
|
331
|
+
/** Per-tool overrides (R14); an override always wins. */
|
|
332
|
+
overrides?: PolicyOverrides | undefined;
|
|
333
|
+
/** The approvals mode (R49) that applies when no override does. */
|
|
334
|
+
approvals: ApprovalsMode;
|
|
335
|
+
/**
|
|
336
|
+
* A multi-server facade's per-source approvals modes (R49), resolved at
|
|
337
|
+
* wrap time to one entry per qualified tool name (`fs.write_file`) from
|
|
338
|
+
* the boxed view's members — so the box is never re-derived by splitting
|
|
339
|
+
* a name. A tool with an entry decides with that mode instead of
|
|
340
|
+
* `approvals`. Absent on single-server facades.
|
|
341
|
+
*/
|
|
342
|
+
toolApprovals?: ReadonlyMap<string, ApprovalsMode> | undefined;
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* The policy options every deciding surface takes (R14, R49) — the gate
|
|
346
|
+
* deps, `searchTools`, `describeTool`, the machine host and typegen — in one
|
|
347
|
+
* shape, turned into a {@link PolicyContext} by `policyContext`. Absent
|
|
348
|
+
* options mean no overrides and `'every-write'`.
|
|
349
|
+
*/
|
|
350
|
+
export interface PolicyOptions {
|
|
351
|
+
/** Per-tool policy overrides (R14). */
|
|
352
|
+
policy?: PolicyOverrides | undefined;
|
|
353
|
+
/** Approvals mode (R49), decided with `policy`. */
|
|
354
|
+
approvals?: ApprovalsMode | undefined;
|
|
355
|
+
/**
|
|
356
|
+
* Per-source approvals modes of a multi-server facade, resolved per
|
|
357
|
+
* qualified tool name (R49; see {@link PolicyContext.toolApprovals}).
|
|
358
|
+
*/
|
|
359
|
+
toolApprovals?: ReadonlyMap<string, ApprovalsMode> | undefined;
|
|
360
|
+
}
|
|
83
361
|
/** A tool call requested by the machine via the `toolCall` action creator. */
|
|
84
|
-
interface PendingToolCall {
|
|
362
|
+
export interface PendingToolCall {
|
|
85
363
|
/** Unique id for this call within the workflow (also the inputRequest key). */
|
|
86
364
|
id: string;
|
|
87
365
|
name: string;
|
|
@@ -90,13 +368,23 @@ interface PendingToolCall {
|
|
|
90
368
|
stateName: string;
|
|
91
369
|
}
|
|
92
370
|
/** Events the host injects into the machine. The machine never performs I/O itself. */
|
|
93
|
-
type HostEvent = {
|
|
371
|
+
export type HostEvent = {
|
|
94
372
|
type: 'TOOL_RESULT';
|
|
95
373
|
callId: string;
|
|
374
|
+
/** The call's own arguments — as sealed, for an approved call (S2). */
|
|
375
|
+
args: Record<string, unknown>;
|
|
96
376
|
result: unknown;
|
|
377
|
+
/**
|
|
378
|
+
* The upstream's text, when `result` is that text parsed: a single
|
|
379
|
+
* text block holding JSON for an object or an array (D29).
|
|
380
|
+
*/
|
|
381
|
+
text?: string;
|
|
382
|
+
/** The facade's notes on the result (R35.6, D29); absent when there are none. */
|
|
383
|
+
notes?: string[];
|
|
97
384
|
} | {
|
|
98
385
|
type: 'TOOL_ERROR';
|
|
99
386
|
callId: string;
|
|
387
|
+
args: Record<string, unknown>;
|
|
100
388
|
error: {
|
|
101
389
|
message: string;
|
|
102
390
|
detail?: unknown;
|
|
@@ -104,18 +392,24 @@ type HostEvent = {
|
|
|
104
392
|
} | {
|
|
105
393
|
type: 'TOOL_DENIED';
|
|
106
394
|
callId: string;
|
|
395
|
+
args: Record<string, unknown>;
|
|
107
396
|
reason?: string;
|
|
108
397
|
};
|
|
109
|
-
type ErrorPhase = 'transpile' | 'validate' | 'runtime' | 'cap_exceeded' | 'blob_verification' | 'protocol' |
|
|
398
|
+
export type ErrorPhase = 'transpile' | 'validate' | 'runtime' | 'cap_exceeded' | 'blob_verification' | 'protocol' |
|
|
110
399
|
/** An `isError` result or transport failure from the wrapped tool, surfaced by `call_tool` (R25). */
|
|
111
|
-
'upstream' |
|
|
112
|
-
|
|
400
|
+
'upstream' |
|
|
401
|
+
/**
|
|
402
|
+
* An upstream sign-in is required or could not complete (R25, R57):
|
|
403
|
+
* library copy only, plus at most the OAuth `error` code.
|
|
404
|
+
*/
|
|
405
|
+
'auth' | 'terminal';
|
|
406
|
+
export interface Diagnostic {
|
|
113
407
|
message: string;
|
|
114
408
|
/** Optional source location within the submitted code. */
|
|
115
409
|
line?: number;
|
|
116
410
|
column?: number;
|
|
117
411
|
}
|
|
118
|
-
interface StructuredError {
|
|
412
|
+
export interface StructuredError {
|
|
119
413
|
phase: ErrorPhase;
|
|
120
414
|
message: string;
|
|
121
415
|
diagnostics: Diagnostic[];
|
|
@@ -123,8 +417,8 @@ interface StructuredError {
|
|
|
123
417
|
hint: string;
|
|
124
418
|
logs: LogEntry[];
|
|
125
419
|
}
|
|
126
|
-
type LogLevel = 'log' | 'info' | 'warn' | 'error' | 'debug';
|
|
127
|
-
interface LogEntry {
|
|
420
|
+
export type LogLevel = 'log' | 'info' | 'warn' | 'error' | 'debug';
|
|
421
|
+
export interface LogEntry {
|
|
128
422
|
level: LogLevel;
|
|
129
423
|
message: string;
|
|
130
424
|
/** Milliseconds since segment start. */
|
|
@@ -132,7 +426,7 @@ interface LogEntry {
|
|
|
132
426
|
/** Zero-based MRTR segment index this entry was captured in. */
|
|
133
427
|
segment: number;
|
|
134
428
|
}
|
|
135
|
-
interface ResourceCaps {
|
|
429
|
+
export interface ResourceCaps {
|
|
136
430
|
/** Wall-clock per execution segment, ms. */
|
|
137
431
|
segmentWallClockMs: number;
|
|
138
432
|
/** WASM memory, bytes. */
|
|
@@ -154,14 +448,19 @@ interface ResourceCaps {
|
|
|
154
448
|
/** Captured console output per segment, bytes. */
|
|
155
449
|
maxConsoleBytes: number;
|
|
156
450
|
}
|
|
157
|
-
declare const DEFAULT_CAPS: ResourceCaps;
|
|
158
|
-
declare const MAX_CAPS: ResourceCaps;
|
|
451
|
+
export declare const DEFAULT_CAPS: ResourceCaps;
|
|
452
|
+
export declare const MAX_CAPS: ResourceCaps;
|
|
159
453
|
/** Clamp requested caps into [MIN_CAPS, MAX_CAPS] (R12). */
|
|
160
|
-
declare function clampCaps(requested: Partial<ResourceCaps>): ResourceCaps;
|
|
161
|
-
declare const BLOB_SCHEMA_VERSION = 1;
|
|
162
|
-
/**
|
|
163
|
-
|
|
164
|
-
|
|
454
|
+
export declare function clampCaps(requested: Partial<ResourceCaps>): ResourceCaps;
|
|
455
|
+
export declare const BLOB_SCHEMA_VERSION = 1;
|
|
456
|
+
/**
|
|
457
|
+
* Which tool minted a blob (R36): a blob opened by the other tool is
|
|
458
|
+
* rejected. `upstream_input` is minted by `call_tool` when the wrapped
|
|
459
|
+
* tool's own server asked the human a question (G1, D24); `call_tool`
|
|
460
|
+
* opens both of its kinds.
|
|
461
|
+
*/
|
|
462
|
+
export type BlobKind = 'machine' | 'call' | 'upstream_input';
|
|
463
|
+
export interface BlobPayload {
|
|
165
464
|
/** Blob schema version; unknown versions are rejected. */
|
|
166
465
|
v: number;
|
|
167
466
|
/** Minting tool (R36). */
|
|
@@ -190,8 +489,27 @@ interface BlobPayload {
|
|
|
190
489
|
* of its final segment.
|
|
191
490
|
*/
|
|
192
491
|
audit: AuditEntry[];
|
|
492
|
+
/**
|
|
493
|
+
* How this suspension was put to the person (R15): present, and
|
|
494
|
+
* `'batched'`, only when it was one batched elicitation; absent means
|
|
495
|
+
* one elicitation per pending call. Sealed so the resume answers the
|
|
496
|
+
* prompt that was actually shown, whatever the resuming instance's
|
|
497
|
+
* option (D25). Only on `machine` blobs with two or more pending calls;
|
|
498
|
+
* never on `call` or `upstream_input` blobs (both minted by `call_tool`,
|
|
499
|
+
* which never batches).
|
|
500
|
+
*/
|
|
501
|
+
prompts?: 'batched';
|
|
193
502
|
/** Round-trip counter (R18). */
|
|
194
503
|
trip: number;
|
|
504
|
+
/**
|
|
505
|
+
* Calls that already ran under the gate and are waiting on their own
|
|
506
|
+
* server's question (G1, D24), keyed by call id: the call, how it was
|
|
507
|
+
* authorized, the server's sealed `requestState`, and the questions.
|
|
508
|
+
* Never part of `pending` (S2): an answer re-issues the recorded call
|
|
509
|
+
* and approves nothing. Exactly one entry keyed `call` for an
|
|
510
|
+
* `upstream_input` blob; absent from `call` blobs.
|
|
511
|
+
*/
|
|
512
|
+
upstreamInput?: Record<string, UpstreamHold>;
|
|
195
513
|
/** Random nonce for replay scoping. */
|
|
196
514
|
nonce: string;
|
|
197
515
|
/** Issued-at, Unix ms. */
|
|
@@ -199,7 +517,39 @@ interface BlobPayload {
|
|
|
199
517
|
/** Expiry, Unix ms (default TTL 15 min). */
|
|
200
518
|
exp: number;
|
|
201
519
|
}
|
|
202
|
-
|
|
520
|
+
/** A sanitized MCP elicitation schema: a flat object of primitive fields (G1). */
|
|
521
|
+
export interface ElicitationSchema {
|
|
522
|
+
type: 'object';
|
|
523
|
+
properties: Record<string, Record<string, unknown>>;
|
|
524
|
+
required?: string[];
|
|
525
|
+
}
|
|
526
|
+
/** One forwarded question as sealed: enough to map and check the answer (G1). */
|
|
527
|
+
export interface ForwardedQuestion {
|
|
528
|
+
/** The server's own key for this request. */
|
|
529
|
+
key: string;
|
|
530
|
+
/**
|
|
531
|
+
* `form`: the human filled the sanitized form in `schema`. `url`: the
|
|
532
|
+
* human was shown a link (as URL mode, or as a form with the link as inert
|
|
533
|
+
* text when the client cannot open links); accepting sends no content.
|
|
534
|
+
*/
|
|
535
|
+
mode: 'form' | 'url';
|
|
536
|
+
/** The sanitized schema the human answered (`form` only). */
|
|
537
|
+
schema?: ElicitationSchema;
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* A call that ran under the gate and is waiting on its server's question
|
|
541
|
+
* (G1, D24), sealed in the blob's `upstreamInput` — never in the S2
|
|
542
|
+
* `pending` map: an answer re-issues the recorded call, it approves nothing.
|
|
543
|
+
*/
|
|
544
|
+
export interface UpstreamHold extends PendingToolCall {
|
|
545
|
+
/** How the call was authorized when it first ran. */
|
|
546
|
+
decision: 'auto' | 'approved';
|
|
547
|
+
/** The server's own opaque state, echoed verbatim on the re-issue. */
|
|
548
|
+
requestState?: string;
|
|
549
|
+
/** The questions, keyed by the facade's keys. */
|
|
550
|
+
questions: Record<string, ForwardedQuestion>;
|
|
551
|
+
}
|
|
552
|
+
export interface BlobSealerConfig {
|
|
203
553
|
/** Current encryption key (32 bytes). */
|
|
204
554
|
key: Uint8Array;
|
|
205
555
|
/** Previous keys accepted for decryption during rotation (R22). */
|
|
@@ -221,7 +571,7 @@ interface BlobSealerConfig {
|
|
|
221
571
|
* blob kinds. No default ships: an in-memory one would silently break
|
|
222
572
|
* horizontal scaling (G4), so the integrator supplies a shared store.
|
|
223
573
|
*/
|
|
224
|
-
interface ReplayCache {
|
|
574
|
+
export interface ReplayCache {
|
|
225
575
|
/**
|
|
226
576
|
* Record `nonce` until `exp` (Unix ms) and resolve `true` when it had
|
|
227
577
|
* already been recorded — i.e. this approval is being replayed.
|
|
@@ -237,24 +587,37 @@ interface ReplayCache {
|
|
|
237
587
|
seen(nonce: string, exp: number): Promise<boolean>;
|
|
238
588
|
}
|
|
239
589
|
/** Verdict delivered on retry for a pending gated call. */
|
|
240
|
-
type ApprovalVerdict = {
|
|
590
|
+
export type ApprovalVerdict = {
|
|
241
591
|
kind: 'approved';
|
|
242
592
|
} | {
|
|
243
593
|
kind: 'declined';
|
|
244
594
|
reason?: string;
|
|
245
595
|
};
|
|
246
596
|
/** Outcome of running one execution segment. */
|
|
247
|
-
|
|
597
|
+
/** Most result notes a completed segment reports (D29). */
|
|
598
|
+
export declare const MAX_SEGMENT_NOTES = 10;
|
|
599
|
+
export type SegmentOutcome = {
|
|
248
600
|
status: 'completed';
|
|
249
601
|
/** Final machine output/context to return to the client. */
|
|
250
602
|
output: unknown;
|
|
251
603
|
/** Per-call approval audit trail. */
|
|
252
604
|
audit: AuditEntry[];
|
|
253
605
|
logs: LogEntry[];
|
|
606
|
+
/**
|
|
607
|
+
* The result notes of this segment's tool calls (R35.6, D29), each
|
|
608
|
+
* prefixed with its tool's name, at most {@link MAX_SEGMENT_NOTES};
|
|
609
|
+
* absent when there are none.
|
|
610
|
+
*/
|
|
611
|
+
notes?: string[];
|
|
254
612
|
} | {
|
|
255
613
|
status: 'suspended';
|
|
256
614
|
/** Gated calls awaiting approval, keyed by inputRequest key. */
|
|
257
615
|
pending: Record<string, PendingToolCall>;
|
|
616
|
+
/**
|
|
617
|
+
* Calls that ran and whose server asked the human a question (G1):
|
|
618
|
+
* the adapter forwards each question and seals the hold.
|
|
619
|
+
*/
|
|
620
|
+
held: HeldCall[];
|
|
258
621
|
/** Actor snapshot to seal into the blob. */
|
|
259
622
|
snapshot: unknown;
|
|
260
623
|
trip: number;
|
|
@@ -263,22 +626,36 @@ type SegmentOutcome = {
|
|
|
263
626
|
} | {
|
|
264
627
|
status: 'error';
|
|
265
628
|
error: StructuredError;
|
|
629
|
+
/**
|
|
630
|
+
* The workflow's audit trail up to the failure (earlier segments'
|
|
631
|
+
* included), so the result can say which approved calls already ran
|
|
632
|
+
* and which were refused. Absent when nothing was audited.
|
|
633
|
+
*/
|
|
634
|
+
audit?: AuditEntry[];
|
|
266
635
|
};
|
|
267
|
-
|
|
636
|
+
/** A call whose server answered `input_required` on a box that forwards (G1). */
|
|
637
|
+
export interface HeldCall {
|
|
638
|
+
call: PendingToolCall;
|
|
639
|
+
decision: 'auto' | 'approved';
|
|
640
|
+
input: UpstreamInputRequest;
|
|
641
|
+
}
|
|
642
|
+
export interface AuditEntry {
|
|
268
643
|
callId: string;
|
|
269
644
|
tool: string;
|
|
270
645
|
args: Record<string, unknown>;
|
|
271
646
|
decision: 'auto' | 'approved' | 'declined' | 'denied_by_policy' | 'error';
|
|
272
647
|
reason?: string;
|
|
273
648
|
}
|
|
274
|
-
interface RedactionHook {
|
|
649
|
+
export interface RedactionHook {
|
|
275
650
|
/**
|
|
276
651
|
* Mask sensitive argument fields before they appear in elicitation copy
|
|
277
|
-
* (R16). Receives the tool name
|
|
652
|
+
* (R16). Receives the tool name — qualified (`<alias>.<tool>`) on a
|
|
653
|
+
* multi-server facade — and a copy of the resolved args; returns args to
|
|
654
|
+
* display.
|
|
278
655
|
*/
|
|
279
656
|
(toolName: string, args: Record<string, unknown>): Record<string, unknown>;
|
|
280
657
|
}
|
|
281
|
-
interface ProjectionConfig {
|
|
658
|
+
export interface ProjectionConfig {
|
|
282
659
|
/** Resource cap overrides, clamped at maxima (R12). */
|
|
283
660
|
caps?: Partial<ResourceCaps>;
|
|
284
661
|
/** Per-tool policy overrides (R14). */
|
|
@@ -293,21 +670,111 @@ interface ProjectionConfig {
|
|
|
293
670
|
*/
|
|
294
671
|
codeModePreamble?: string;
|
|
295
672
|
}
|
|
296
|
-
|
|
297
|
-
|
|
673
|
+
/**
|
|
674
|
+
* Error carrying the structured `isError` body every failure returns (R25).
|
|
675
|
+
*/
|
|
676
|
+
export declare class ProjectionError extends Error {
|
|
677
|
+
readonly phase: ErrorPhase;
|
|
678
|
+
readonly diagnostics: Diagnostic[];
|
|
679
|
+
readonly hint: string;
|
|
680
|
+
logs: LogEntry[];
|
|
681
|
+
constructor(phase: ErrorPhase, message: string, options?: {
|
|
682
|
+
diagnostics?: Diagnostic[];
|
|
683
|
+
hint?: string;
|
|
684
|
+
logs?: LogEntry[];
|
|
685
|
+
cause?: unknown;
|
|
686
|
+
});
|
|
687
|
+
toStructured(): StructuredError;
|
|
688
|
+
}
|
|
689
|
+
/** Convert any thrown value into a StructuredError, defaulting to `runtime` phase. */
|
|
690
|
+
export declare function toStructuredError(err: unknown, logs?: LogEntry[]): StructuredError;
|
|
691
|
+
/**
|
|
692
|
+
* An upstream auth failure of a source given by URL (R25 `auth`, R57):
|
|
693
|
+
* library copy naming the source, plus at most the OAuth `error` code; the
|
|
694
|
+
* typed {@link reason} is what callers branch on (just-in-time sign-in
|
|
695
|
+
* re-locks a box on it).
|
|
696
|
+
*/
|
|
697
|
+
declare class UpstreamAuthProjectionError extends ProjectionError {
|
|
698
|
+
readonly reason: UpstreamAuthReason;
|
|
699
|
+
readonly oauthCode?: string;
|
|
700
|
+
constructor(reason: UpstreamAuthReason, oauthCode: string | undefined, message: string, hint: string);
|
|
701
|
+
}
|
|
702
|
+
/**
|
|
703
|
+
* One `UpstreamSpec` source, validated and bound to its token store. The
|
|
704
|
+
* introspector opens it like any other source; Projection owns the client.
|
|
705
|
+
*/
|
|
706
|
+
declare class UpstreamConnector {
|
|
707
|
+
#private;
|
|
708
|
+
readonly upstream: ResolvedUpstream;
|
|
709
|
+
/** How messages name the source: `source "github"`, or `the wrapped server`. */
|
|
710
|
+
readonly label: string;
|
|
711
|
+
constructor(upstream: ResolvedUpstream, store: TokenStore, label: string);
|
|
712
|
+
/** The R60 identity, `url:<canonical url>`. */
|
|
713
|
+
get identity(): string;
|
|
714
|
+
/** The source and its host, for messages: `source "github" (api.github.com)`. */
|
|
715
|
+
get where(): string;
|
|
716
|
+
/**
|
|
717
|
+
* Connect (R51): Streamable HTTP with Projection's auth provider and
|
|
718
|
+
* guarded fetch, `onInsufficientScope: 'throw'` and the SDK's default
|
|
719
|
+
* `redirectPolicy` (never `'follow'`, R58, R61). Aborting `signal` closes
|
|
720
|
+
* the half-open connection. Any failure is {@link toError}'s.
|
|
721
|
+
*/
|
|
722
|
+
open(signal?: AbortSignal, options?: {
|
|
723
|
+
forwardElicitation?: boolean;
|
|
724
|
+
}): Promise<Client>;
|
|
725
|
+
/** `text` from this source, inert, with every credential it presented redacted, and bounded. */
|
|
726
|
+
redactText(text: string): string;
|
|
727
|
+
/**
|
|
728
|
+
* `value` from this source with every credential it presented redacted.
|
|
729
|
+
* Throws a {@link ProjectionError} when the value cannot be checked (fail
|
|
730
|
+
* closed, `SecretSet.scrubValue`).
|
|
731
|
+
*/
|
|
732
|
+
redact<T>(value: T): T;
|
|
733
|
+
/**
|
|
734
|
+
* Abort every auth request this connection's runs have in flight (a
|
|
735
|
+
* refresh, its discovery), so a facade that closes leaves no refresh
|
|
736
|
+
* behind for another facade over the same store to wait on.
|
|
737
|
+
*/
|
|
738
|
+
close(): void;
|
|
739
|
+
/** The typed `auth` error for `err`, when it is an upstream auth failure. */
|
|
740
|
+
authFailure(err: unknown): UpstreamAuthProjectionError | undefined;
|
|
741
|
+
/**
|
|
742
|
+
* Any failure to connect to or introspect this source, as a
|
|
743
|
+
* {@link ProjectionError}: an auth failure as the typed `auth` error; a
|
|
744
|
+
* refused request in fixed copy; anything else a `protocol` error (R47)
|
|
745
|
+
* whose text is redacted and bounded — never the raw error, whose
|
|
746
|
+
* message, data or cause may quote a server body that echoes the token.
|
|
747
|
+
*/
|
|
748
|
+
toError(err: unknown): ProjectionError;
|
|
749
|
+
/** A tool call to this source that threw, as the outcome `call_tool` and a machine see (R25). */
|
|
750
|
+
callFailure(name: string, err: unknown): Extract<ToolCallOutcome, {
|
|
751
|
+
ok: false;
|
|
752
|
+
}>;
|
|
753
|
+
/**
|
|
754
|
+
* Delete the stored tokens of this source (R59). No revocation yet (phase
|
|
755
|
+
* 1b). A store that cannot delete is an `auth` error: a sign-out must not
|
|
756
|
+
* silently leave the token behind.
|
|
757
|
+
*/
|
|
758
|
+
signOut(): Promise<void>;
|
|
759
|
+
}
|
|
298
760
|
/** Envelope format version prefix. Bump (proj2, …) on any wire-format change. */
|
|
299
|
-
declare const ENVELOPE_PREFIX = "proj1";
|
|
761
|
+
export declare const ENVELOPE_PREFIX = "proj1";
|
|
300
762
|
/** Default blob TTL for `machine` blobs: 15 minutes (R20). */
|
|
301
|
-
declare const DEFAULT_TTL_MS: number;
|
|
763
|
+
export declare const DEFAULT_TTL_MS: number;
|
|
302
764
|
/**
|
|
303
765
|
* Default TTL for `call` blobs: 5 minutes (R38). An approved `call` blob has
|
|
304
766
|
* no forward step, so it is a standing capability until it expires (D4, D9).
|
|
305
767
|
*/
|
|
306
|
-
declare const DEFAULT_CALL_TTL_MS: number;
|
|
768
|
+
export declare const DEFAULT_CALL_TTL_MS: number;
|
|
307
769
|
/** Default sealed-blob size ceiling: 128 KB (R23). */
|
|
308
|
-
declare const DEFAULT_MAX_BLOB_BYTES: number;
|
|
770
|
+
export declare const DEFAULT_MAX_BLOB_BYTES: number;
|
|
771
|
+
/**
|
|
772
|
+
* Which facade kind a blob belongs to (R41): `single` for `wrapMcpServer`,
|
|
773
|
+
* `multi` for `wrapMcpServers`. Selects the HKDF info string; nothing else.
|
|
774
|
+
*/
|
|
775
|
+
type BlobKeyDomain = 'single' | 'multi';
|
|
309
776
|
/** SHA-256 hex digest of a code argument (S4 binding — R20 `codeHash`). */
|
|
310
|
-
declare function hashCode(code: string): string;
|
|
777
|
+
export declare function hashCode(code: string): string;
|
|
311
778
|
interface BuildBlobPayloadInput {
|
|
312
779
|
/** Minting tool (R36). Default `machine`. */
|
|
313
780
|
kind?: BlobKind | undefined;
|
|
@@ -324,10 +791,20 @@ interface BuildBlobPayloadInput {
|
|
|
324
791
|
pending: Record<string, PendingToolCall>;
|
|
325
792
|
/** Approval audit accumulated so far in this workflow (PRD §8). */
|
|
326
793
|
audit?: AuditEntry[] | undefined;
|
|
794
|
+
/** `'batched'` when the suspension was one batched prompt (R15, D25). */
|
|
795
|
+
prompts?: 'batched' | undefined;
|
|
327
796
|
/** Round-trip counter (R18). */
|
|
328
797
|
trip: number;
|
|
798
|
+
/** Calls waiting on their own server's question (G1); see `BlobPayload.upstreamInput`. */
|
|
799
|
+
upstreamInput?: Record<string, UpstreamHold> | undefined;
|
|
329
800
|
/** Blob TTL in ms. Default {@link DEFAULT_TTL_MS} (15 minutes). */
|
|
330
801
|
ttlMs?: number | undefined;
|
|
802
|
+
/**
|
|
803
|
+
* Latest allowed `exp`, Unix ms: the blob expires at `iat + ttlMs` or this,
|
|
804
|
+
* whichever is earlier. A blob minted to continue an earlier one carries
|
|
805
|
+
* that blob's `exp` here, so a chain of round trips never extends it (R38).
|
|
806
|
+
*/
|
|
807
|
+
notAfter?: number | undefined;
|
|
331
808
|
/** Clock override for testability, Unix ms. Default `Date.now()`. */
|
|
332
809
|
now?: number | undefined;
|
|
333
810
|
}
|
|
@@ -335,19 +812,23 @@ interface BuildBlobPayloadInput {
|
|
|
335
812
|
* Construct a fully populated {@link BlobPayload}: stamps the schema and
|
|
336
813
|
* library/XState versions, hashes the code, and fills nonce/iat/exp.
|
|
337
814
|
*/
|
|
338
|
-
declare function buildBlobPayload(input: BuildBlobPayloadInput): BlobPayload;
|
|
815
|
+
export declare function buildBlobPayload(input: BuildBlobPayloadInput): BlobPayload;
|
|
339
816
|
/**
|
|
340
817
|
* Seal a payload into an opaque `proj1.` envelope string (R19, R23).
|
|
341
818
|
*
|
|
342
819
|
* JSON → deflateRaw → AES-256-GCM under an HKDF-SHA256 per-blob derived key,
|
|
343
820
|
* with the envelope header as AAD. Throws ProjectionError phase `terminal` if
|
|
344
821
|
* the sealed envelope exceeds `maxBlobBytes` (default 128 KB) or the config
|
|
345
|
-
* keys are malformed.
|
|
822
|
+
* keys are malformed. `domain` selects the facade kind's key domain (R41);
|
|
823
|
+
* it defaults to `single`, the only domain before multi-server facades.
|
|
346
824
|
*/
|
|
347
|
-
declare function sealBlob(payload: BlobPayload, config: BlobSealerConfig): string;
|
|
825
|
+
export declare function sealBlob(payload: BlobPayload, config: BlobSealerConfig, domain?: BlobKeyDomain): string;
|
|
348
826
|
interface OpenBlobOptions {
|
|
349
|
-
/**
|
|
350
|
-
|
|
827
|
+
/**
|
|
828
|
+
* The kind(s) the opening tool accepts (R36). Default `machine`.
|
|
829
|
+
* `call_tool` opens `call` and `upstream_input` blobs.
|
|
830
|
+
*/
|
|
831
|
+
expectedKind?: BlobKind | readonly BlobKind[] | undefined;
|
|
351
832
|
/**
|
|
352
833
|
* The text re-sent with the retry (S4): the `code` argument for
|
|
353
834
|
* `execute_machine`, the canonical request for `call_tool`.
|
|
@@ -356,6 +837,11 @@ interface OpenBlobOptions {
|
|
|
356
837
|
/** Identity of the wrapped server handling the retry (R30). */
|
|
357
838
|
serverIdentity: string;
|
|
358
839
|
config: BlobSealerConfig;
|
|
840
|
+
/**
|
|
841
|
+
* Key domain of the facade opening the blob (R41). Default `single`. A
|
|
842
|
+
* blob sealed in the other domain fails authentication.
|
|
843
|
+
*/
|
|
844
|
+
domain?: BlobKeyDomain | undefined;
|
|
359
845
|
/** Clock override for testability, Unix ms. Default `Date.now()`. */
|
|
360
846
|
now?: number | undefined;
|
|
361
847
|
}
|
|
@@ -374,9 +860,7 @@ interface OpenBlobOptions {
|
|
|
374
860
|
* failed check (never key material or plaintext). A payload is only
|
|
375
861
|
* returned once every check has passed.
|
|
376
862
|
*/
|
|
377
|
-
declare function openBlob(blob: string, options: OpenBlobOptions): BlobPayload;
|
|
378
|
-
//#endregion
|
|
379
|
-
//#region src/blob/versions.d.ts
|
|
863
|
+
export declare function openBlob(blob: string, options: OpenBlobOptions): BlobPayload;
|
|
380
864
|
/**
|
|
381
865
|
* Version constants recorded into every blob (R20) and strict-matched on
|
|
382
866
|
* open (R21, Q3: no migration — mismatches are rejected).
|
|
@@ -389,11 +873,175 @@ declare function openBlob(blob: string, options: OpenBlobOptions): BlobPayload;
|
|
|
389
873
|
* CI instead of silently minting blobs with a stale version stamp.
|
|
390
874
|
*/
|
|
391
875
|
/** `@astral-mcp/projection` package version (must match packages/projection/package.json). */
|
|
392
|
-
declare const LIB_VERSION = "0.
|
|
876
|
+
export declare const LIB_VERSION = "0.9.0";
|
|
393
877
|
/** Pinned XState version (must match the exact pin in package.json — D2). */
|
|
394
|
-
declare const XSTATE_VERSION = "5.32.5";
|
|
395
|
-
|
|
396
|
-
|
|
878
|
+
export declare const XSTATE_VERSION = "5.32.5";
|
|
879
|
+
/**
|
|
880
|
+
* Decide the policy for a catalog tool (R14, R49). Precedence, strongest
|
|
881
|
+
* first:
|
|
882
|
+
*
|
|
883
|
+
* 1. A developer override (`auto` | `ask` | `deny`) always wins. An override
|
|
884
|
+
* entry with an unrecognized value (possible when the config arrives
|
|
885
|
+
* untyped) fails safe to `ask` — it never grants `auto`.
|
|
886
|
+
* 2. `annotations.readOnlyHint === true` yields `auto` in every mode.
|
|
887
|
+
* 3. Under `approvals: 'destructive-only'`, a tool whose annotations declare
|
|
888
|
+
* `destructiveHint === false` exactly yields `auto`; a missing hint or any
|
|
889
|
+
* other value still asks (the MCP default reads a non-read-only tool as
|
|
890
|
+
* destructive).
|
|
891
|
+
* 4. Otherwise `ask` (S5: unannotated defaults to ask).
|
|
892
|
+
*
|
|
893
|
+
* A context whose `approvals` is not one of the two modes throws a
|
|
894
|
+
* `TypeError` — fail closed, never decide on a malformed context (the wrap
|
|
895
|
+
* rejects malformed modes up front with a `validate` error — see
|
|
896
|
+
* {@link assertApprovalsMode}).
|
|
897
|
+
*
|
|
898
|
+
* Only own properties of `context.overrides` and of `entry.annotations` are
|
|
899
|
+
* consulted, so tools named like `Object.prototype` members (`toString`,
|
|
900
|
+
* `constructor`, ...) cannot pick up inherited junk as a decision, and an
|
|
901
|
+
* annotations object cannot inherit a relaxing hint from its prototype.
|
|
902
|
+
*/
|
|
903
|
+
export declare function decidePolicy(entry: ToolCatalogEntry, context?: PolicyContext): PolicyDecision;
|
|
904
|
+
/**
|
|
905
|
+
* Build the {@link PolicyContext} for a set of {@link PolicyOptions} — the
|
|
906
|
+
* per-tool `policy` overrides (R14), the `approvals` mode and a multi-server
|
|
907
|
+
* facade's per-tool `toolApprovals` (R49), the shape the gate deps, the
|
|
908
|
+
* catalog tools, the machine host and typegen all take. An absent mode
|
|
909
|
+
* resolves to `'every-write'`.
|
|
910
|
+
*/
|
|
911
|
+
export declare function policyContext(options?: PolicyOptions): PolicyContext;
|
|
912
|
+
/**
|
|
913
|
+
* Validate an `approvals` option (R49): absent, or one of the two modes. A
|
|
914
|
+
* malformed value is a `validate`-phase {@link ProjectionError} — the mode
|
|
915
|
+
* is one deploy-time switch, so a typo fails the wrap instead of silently
|
|
916
|
+
* changing who gets asked (DECISIONS D17). `wrapMcpServer` calls it; call
|
|
917
|
+
* it yourself when passing `approvals` to `executeMachine` or
|
|
918
|
+
* `generateToolTypes` directly.
|
|
919
|
+
*/
|
|
920
|
+
export declare function assertApprovalsMode(value: unknown): ApprovalsMode | undefined;
|
|
921
|
+
/** The three normative posture strings (R33) — models match on these. */
|
|
922
|
+
type ToolPosture = 'runs immediately' | 'asks for approval' | 'blocked by policy';
|
|
923
|
+
/** Map a policy decision to its plain-words posture (R33). Shared by every surface that names one. */
|
|
924
|
+
export declare function postureOf(decision: PolicyDecision): ToolPosture;
|
|
925
|
+
/** Result of resolving a pending call against a server catalog. */
|
|
926
|
+
type ResolvedCallPolicy = {
|
|
927
|
+
ok: true;
|
|
928
|
+
decision: PolicyDecision;
|
|
929
|
+
entry: ToolCatalogEntry;
|
|
930
|
+
} | {
|
|
931
|
+
ok: false;
|
|
932
|
+
error: ProjectionError;
|
|
933
|
+
};
|
|
934
|
+
/**
|
|
935
|
+
* Resolve a machine-requested tool call against the server catalog and
|
|
936
|
+
* decide its policy.
|
|
937
|
+
*
|
|
938
|
+
* Returns the decision together with the matching catalog entry (the entry
|
|
939
|
+
* is what elicitation copy must be built from — S6), or a structured
|
|
940
|
+
* `validate`-phase {@link ProjectionError} when the machine names a tool the
|
|
941
|
+
* wrapped server does not declare.
|
|
942
|
+
*/
|
|
943
|
+
export declare function resolveCallPolicy(call: PendingToolCall, catalog: ServerCatalog, context?: PolicyContext): ResolvedCallPolicy;
|
|
944
|
+
/**
|
|
945
|
+
* Canonical TOOL_DENIED reason for calls blocked by a `deny` policy
|
|
946
|
+
* decision. A `deny` decision never produces an elicitation — the machine
|
|
947
|
+
* host injects `TOOL_DENIED` with this reason instead.
|
|
948
|
+
*/
|
|
949
|
+
export declare const POLICY_DENIED_REASON = "denied by server policy";
|
|
950
|
+
/**
|
|
951
|
+
* Build the TOOL_DENIED reason string for a policy-denied call. With a tool
|
|
952
|
+
* name it names the denied tool so the machine (and audit trail) can tell
|
|
953
|
+
* which call was blocked.
|
|
954
|
+
*/
|
|
955
|
+
export declare function policyDeniedReason(toolName?: string): string;
|
|
956
|
+
/** Structured approval copy for one gated call (input to the MRTR adapter). */
|
|
957
|
+
interface ElicitationCopy {
|
|
958
|
+
/** Short action title, e.g. `Approve: delete file`. */
|
|
959
|
+
title: string;
|
|
960
|
+
/** Full human-readable summary (plain text with an indented JSON block). */
|
|
961
|
+
message: string;
|
|
962
|
+
/** Tool name, taken from the catalog entry; qualified with `box` when given. */
|
|
963
|
+
toolName: string;
|
|
964
|
+
/** Machine state path that requested the call (raw; render with care). */
|
|
965
|
+
stateName: string;
|
|
966
|
+
/** The redacted arguments as rendered in `message` (JSON text, possibly truncated). */
|
|
967
|
+
args: string;
|
|
968
|
+
/** True when the catalog entry carries `destructiveHint: true`. */
|
|
969
|
+
destructive: boolean;
|
|
970
|
+
}
|
|
971
|
+
interface ElicitationCopyOptions {
|
|
972
|
+
/** Redaction hook applied to args before any rendering (R16). */
|
|
973
|
+
redact?: RedactionHook;
|
|
974
|
+
/**
|
|
975
|
+
* Who asked: a machine state (default) or a direct `call_tool` request
|
|
976
|
+
* (R35). Selects the footer; nothing else differs.
|
|
977
|
+
*/
|
|
978
|
+
origin?: 'machine' | 'direct';
|
|
979
|
+
/**
|
|
980
|
+
* Multi-server facades (R40, R46): the alias of the box the tool lives
|
|
981
|
+
* in. `entry` is then the box's own catalog entry (bare name) and the
|
|
982
|
+
* call's name is qualified. The copy names the box (R46): the title reads
|
|
983
|
+
* `Approve (fs): …` (`⚠ Approve (fs): …` when destructive), the purpose
|
|
984
|
+
* line `What fs says it does: "…"` — always rendered, `(no description)`
|
|
985
|
+
* when the server gives none — and the footer `Tool: fs.write_file`, or
|
|
986
|
+
* `Tool: fs › "github.delete_repo"` when the bare name has a dot, so it
|
|
987
|
+
* never reads as another box's tool. The redaction hook receives the
|
|
988
|
+
* qualified name. The alias is the host's, never a server-reported name
|
|
989
|
+
* (S6).
|
|
990
|
+
*/
|
|
991
|
+
box?: string;
|
|
992
|
+
/**
|
|
993
|
+
* Backstop cap on the whole rendered args JSON, in characters. Default
|
|
994
|
+
* 4096. Each string leaf is independently capped at 512 characters first.
|
|
995
|
+
*/
|
|
996
|
+
maxArgsChars?: number;
|
|
997
|
+
}
|
|
998
|
+
/**
|
|
999
|
+
* Build the approval summary for a gated call.
|
|
1000
|
+
*
|
|
1001
|
+
* `entry` MUST be the catalog entry the call was resolved against (use
|
|
1002
|
+
* `resolveCallPolicy`); the copy takes the tool name, title and description
|
|
1003
|
+
* from it, never from the pending call or its arguments.
|
|
1004
|
+
*/
|
|
1005
|
+
export declare function buildElicitationCopy(call: PendingToolCall, entry: ToolCatalogEntry, options?: ElicitationCopyOptions): ElicitationCopy;
|
|
1006
|
+
/**
|
|
1007
|
+
* Rendering untrusted strings inertly (S5, S6).
|
|
1008
|
+
*
|
|
1009
|
+
* Tool names, descriptions, the wrapped server's identity, and machine state
|
|
1010
|
+
* names all flow from a possibly third-party server or a model-authored
|
|
1011
|
+
* machine. Wherever they are rendered into human-facing prompt copy or into
|
|
1012
|
+
* generated source, they must not be able to forge structure with control
|
|
1013
|
+
* characters (newlines, ESC for ANSI sequences) or reorder text with Unicode
|
|
1014
|
+
* bidi overrides. Centralizing the character class here keeps every call site
|
|
1015
|
+
* on the same, complete definition — an earlier divergence let generated
|
|
1016
|
+
* `.d.ts`/bridge comments miss the directional-isolate range that the
|
|
1017
|
+
* elicitation copy already stripped.
|
|
1018
|
+
*/
|
|
1019
|
+
/**
|
|
1020
|
+
* Replace control and bidi characters with the replacement character.
|
|
1021
|
+
* `allowNewline` keeps `\n` (for multi-line blocks whose formatting newlines
|
|
1022
|
+
* are part of the rendering, e.g. a pretty-printed JSON block); `allowTab`
|
|
1023
|
+
* also keeps `\t`, for text printed verbatim between fences (D35), where a
|
|
1024
|
+
* tab is the text's own (a Makefile recipe, a TSV row) and cannot start a
|
|
1025
|
+
* line. `allowTab` takes effect only with `allowNewline`.
|
|
1026
|
+
*/
|
|
1027
|
+
export declare function neutralizeControlChars(text: string, options?: {
|
|
1028
|
+
allowNewline?: boolean;
|
|
1029
|
+
allowTab?: boolean;
|
|
1030
|
+
}): string;
|
|
1031
|
+
/** Truncate to `maxChars`, appending an ellipsis when shortened. */
|
|
1032
|
+
export declare function capLength(text: string, maxChars: number): string;
|
|
1033
|
+
/**
|
|
1034
|
+
* The one-line human purpose: the first sentence of the server's
|
|
1035
|
+
* description, neutralized and capped. Runs of replacement characters
|
|
1036
|
+
* (neutralized control bytes) are collapsed so a hostile or malformed
|
|
1037
|
+
* description does not render as a wall of `�`. Undefined when the server
|
|
1038
|
+
* gave no usable description.
|
|
1039
|
+
*
|
|
1040
|
+
* One rule, three consumers — the approval copy (R16), the `discover_apis`
|
|
1041
|
+
* overview and `search_tools` (R33) — so every projection describes a tool
|
|
1042
|
+
* identically.
|
|
1043
|
+
*/
|
|
1044
|
+
export declare function purposeSentence(description: string | undefined): string | undefined;
|
|
397
1045
|
/**
|
|
398
1046
|
* What a `tools/call` handler may return on the 2026-07-28 era: a finished
|
|
399
1047
|
* result, or the input-required suspension. This is the SDK's own
|
|
@@ -427,7 +1075,7 @@ interface RetryEnvelope {
|
|
|
427
1075
|
* an approval prompt whose only payload is an optional decline reason is our
|
|
428
1076
|
* product decision (R16/R17), not a protocol shape.
|
|
429
1077
|
*/
|
|
430
|
-
declare const APPROVAL_SCHEMA: ElicitRequestFormParams['requestedSchema'];
|
|
1078
|
+
export declare const APPROVAL_SCHEMA: ElicitRequestFormParams['requestedSchema'];
|
|
431
1079
|
/**
|
|
432
1080
|
* Protocol revision that introduced MRTR.
|
|
433
1081
|
*
|
|
@@ -438,7 +1086,7 @@ declare const APPROVAL_SCHEMA: ElicitRequestFormParams['requestedSchema'];
|
|
|
438
1086
|
* `'modern'` rather than as a version string. We keep the literal purely so
|
|
439
1087
|
* error copy can name the revision a client must speak (R28).
|
|
440
1088
|
*/
|
|
441
|
-
declare const MRTR_PROTOCOL_REVISION = "2026-07-28";
|
|
1089
|
+
export declare const MRTR_PROTOCOL_REVISION = "2026-07-28";
|
|
442
1090
|
/**
|
|
443
1091
|
* Whether the era a request is being served on can carry MRTR (R28).
|
|
444
1092
|
*
|
|
@@ -449,9 +1097,13 @@ declare const MRTR_PROTOCOL_REVISION = "2026-07-28";
|
|
|
449
1097
|
* no `extra.protocolVersion` to read, and reading one was the bug this
|
|
450
1098
|
* migration fixes (docs/DECISIONS.md D1).
|
|
451
1099
|
*/
|
|
452
|
-
declare function supportsMrtr(era: ProtocolEra$1 | undefined): boolean;
|
|
453
|
-
|
|
454
|
-
|
|
1100
|
+
export declare function supportsMrtr(era: ProtocolEra$1 | undefined): boolean;
|
|
1101
|
+
/** How a suspension with several gated calls is put to the person. */
|
|
1102
|
+
type ApprovalPrompts = 'per-call' | 'batched';
|
|
1103
|
+
/** The single inputRequests key of a batched prompt (reserved namespace). */
|
|
1104
|
+
export declare const BATCH_KEY = "projection:approvals";
|
|
1105
|
+
/** The boolean field that carries the verdict of the call at 0-based position `i`. */
|
|
1106
|
+
export declare function batchField(i: number): string;
|
|
455
1107
|
/**
|
|
456
1108
|
* `execute_machine` arguments plus the MRTR retry envelope.
|
|
457
1109
|
*
|
|
@@ -476,12 +1128,18 @@ interface ExecuteMachineArgs extends RetryEnvelope {
|
|
|
476
1128
|
* protocol-era and replay settings. `execute_machine` adds the sandbox
|
|
477
1129
|
* bridge; `call_tool` needs nothing more (R35, R38).
|
|
478
1130
|
*/
|
|
479
|
-
interface ApprovalGateDeps {
|
|
1131
|
+
interface ApprovalGateDeps extends PolicyOptions {
|
|
480
1132
|
catalog: ServerCatalog;
|
|
481
1133
|
executor: ToolExecutor;
|
|
482
1134
|
blob: BlobSealerConfig;
|
|
1135
|
+
/**
|
|
1136
|
+
* The key domain of the facade kind serving this gate (R41): `single` for
|
|
1137
|
+
* `wrapMcpServer`, `multi` for `wrapMcpServers`. Checked against the
|
|
1138
|
+
* catalog, failing closed on a mismatch (`blobKeyDomain`). Omitted, it is
|
|
1139
|
+
* derived from the catalog alone.
|
|
1140
|
+
*/
|
|
1141
|
+
blobDomain?: BlobKeyDomain;
|
|
483
1142
|
caps?: Partial<ResourceCaps>;
|
|
484
|
-
policy?: PolicyOverrides;
|
|
485
1143
|
redact?: RedactionHook;
|
|
486
1144
|
/**
|
|
487
1145
|
* Protocol era this request is being served on, for down-negotiation
|
|
@@ -510,8 +1168,21 @@ interface ApprovalGateDeps {
|
|
|
510
1168
|
* not know) leaves the shim to decide.
|
|
511
1169
|
*/
|
|
512
1170
|
clientCanElicit?: boolean;
|
|
1171
|
+
/**
|
|
1172
|
+
* Whether the connected client declared URL-mode elicitation. A forwarded
|
|
1173
|
+
* server question that is a link (G1) is sent in URL mode only then;
|
|
1174
|
+
* otherwise the link is shown as inert text in a form.
|
|
1175
|
+
*/
|
|
1176
|
+
clientUrlElicitation?: boolean;
|
|
513
1177
|
/** Optional shared replay cache (R38); consulted after verification, before execution. */
|
|
514
1178
|
replayCache?: ReplayCache;
|
|
1179
|
+
/**
|
|
1180
|
+
* How a suspension holding several gated calls is put to the person
|
|
1181
|
+
* (`WrapOptions.approvalPrompts`, batch.ts): one elicitation per call
|
|
1182
|
+
* (default) or one batched elicitation for all of them. Only machine
|
|
1183
|
+
* suspensions can hold several; a `call_tool` suspension holds one.
|
|
1184
|
+
*/
|
|
1185
|
+
approvalPrompts?: ApprovalPrompts;
|
|
515
1186
|
}
|
|
516
1187
|
interface ExecuteMachineDeps extends ApprovalGateDeps {
|
|
517
1188
|
/** The `@astral-mcp/tools` bridge module source the sandbox loads (R2). */
|
|
@@ -524,11 +1195,9 @@ interface ExecuteMachineDeps extends ApprovalGateDeps {
|
|
|
524
1195
|
* Statelessness (R27): everything needed to resume rides in `requestState`,
|
|
525
1196
|
* so any instance can serve any retry. Nothing is held between calls.
|
|
526
1197
|
*/
|
|
527
|
-
declare function executeMachine(args: ExecuteMachineArgs, deps: ExecuteMachineDeps): Promise<ToolCallResult>;
|
|
1198
|
+
export declare function executeMachine(args: ExecuteMachineArgs, deps: ExecuteMachineDeps): Promise<ToolCallResult>;
|
|
528
1199
|
/** Every failure is a normal result with `isError: true` and a structured body (R25). */
|
|
529
|
-
declare function errorResult(error: StructuredError): CallToolResult;
|
|
530
|
-
//#endregion
|
|
531
|
-
//#region src/catalog/call.d.ts
|
|
1200
|
+
export declare function errorResult(error: StructuredError): CallToolResult;
|
|
532
1201
|
/**
|
|
533
1202
|
* `call_tool` arguments plus the MRTR retry envelope.
|
|
534
1203
|
*
|
|
@@ -545,6 +1214,12 @@ interface CallToolArgs extends RetryEnvelope {
|
|
|
545
1214
|
name: string;
|
|
546
1215
|
/** Arguments for the tool; absent means `{}`. */
|
|
547
1216
|
args?: unknown;
|
|
1217
|
+
/**
|
|
1218
|
+
* Host-side options for the upstream call this request runs, if it runs
|
|
1219
|
+
* one: the facade request's cancellation signal and a progress sink.
|
|
1220
|
+
* Never populated from tool arguments.
|
|
1221
|
+
*/
|
|
1222
|
+
upstream?: UpstreamCallOptions;
|
|
548
1223
|
}
|
|
549
1224
|
/** `call_tool` needs exactly the dependencies every gated tool shares (R35, R38). */
|
|
550
1225
|
type CallToolDeps = ApprovalGateDeps;
|
|
@@ -562,6 +1237,12 @@ type CallToolOutcome = {
|
|
|
562
1237
|
truncated?: true;
|
|
563
1238
|
/** Byte length of the full result's JSON, when truncated. */
|
|
564
1239
|
outputBytes?: number;
|
|
1240
|
+
/**
|
|
1241
|
+
* The facade's remarks on the result, when it looks partial or the
|
|
1242
|
+
* server sent a note beside its structured result (R35.6, D29):
|
|
1243
|
+
* single neutralized lines, at most three, printed under the head.
|
|
1244
|
+
*/
|
|
1245
|
+
notes?: string[];
|
|
565
1246
|
} | {
|
|
566
1247
|
status: 'declined';
|
|
567
1248
|
tool: string;
|
|
@@ -572,11 +1253,9 @@ type CallToolOutcome = {
|
|
|
572
1253
|
reason: string;
|
|
573
1254
|
};
|
|
574
1255
|
/** The single inputRequest / pending key of a `call` blob (R36). */
|
|
575
|
-
declare const CALL_KEY = "call";
|
|
1256
|
+
export declare const CALL_KEY = "call";
|
|
576
1257
|
/** Run `call_tool` for one `tools/call`, first attempt or MRTR retry. */
|
|
577
|
-
declare function callTool(args: CallToolArgs, deps: CallToolDeps): Promise<ToolCallResult>;
|
|
578
|
-
//#endregion
|
|
579
|
-
//#region src/catalog/canonical.d.ts
|
|
1258
|
+
export declare function callTool(args: CallToolArgs, deps: CallToolDeps): Promise<ToolCallResult>;
|
|
580
1259
|
/**
|
|
581
1260
|
* Canonical JSON for the `call_tool` binding hash (PRD R36).
|
|
582
1261
|
*
|
|
@@ -598,11 +1277,9 @@ declare function callTool(args: CallToolArgs, deps: CallToolDeps): Promise<ToolC
|
|
|
598
1277
|
* by the blob's `pending` entry alone (R36, D9).
|
|
599
1278
|
*/
|
|
600
1279
|
/** Canonical JSON text of a plain JSON value. Throws `validate` on non-JSON. */
|
|
601
|
-
declare function canonicalJson(value: unknown): string;
|
|
1280
|
+
export declare function canonicalJson(value: unknown): string;
|
|
602
1281
|
/** The text a `call` blob is bound to (R36): `{ kind: "call", name, args }`. */
|
|
603
|
-
declare function canonicalRequest(name: string, args: Record<string, unknown>): string;
|
|
604
|
-
//#endregion
|
|
605
|
-
//#region src/catalog/validate-args.d.ts
|
|
1282
|
+
export declare function canonicalRequest(name: string, args: Record<string, unknown>): string;
|
|
606
1283
|
/**
|
|
607
1284
|
* Structural argument validation for `call_tool` (PRD R35.1).
|
|
608
1285
|
*
|
|
@@ -610,7 +1287,8 @@ declare function canonicalRequest(name: string, args: Record<string, unknown>):
|
|
|
610
1287
|
* types match, enum membership for top-level primitives, and no unknown
|
|
611
1288
|
* top-level properties when the schema forbids them. It exists so a human
|
|
612
1289
|
* is never asked to approve a call the wrapped server would reject on
|
|
613
|
-
* shape, and so the model gets a `validate` hint naming the property
|
|
1290
|
+
* shape, and so the model gets a `validate` hint naming the property and
|
|
1291
|
+
* showing the tool's call signature (D32). Full
|
|
614
1292
|
* JSON Schema validation would run twice — the wrapped server is the
|
|
615
1293
|
* authority — for nothing but a nicer error message (D9).
|
|
616
1294
|
*
|
|
@@ -618,14 +1296,12 @@ declare function canonicalRequest(name: string, args: Record<string, unknown>):
|
|
|
618
1296
|
* argument name is ordinary data (S5/S6).
|
|
619
1297
|
*/
|
|
620
1298
|
/** Throws a `validate`-phase {@link ProjectionError} naming the offending property. */
|
|
621
|
-
declare function validateArgsShape(toolName: string, args: Record<string, unknown>, schema: Record<string, unknown
|
|
622
|
-
//#endregion
|
|
623
|
-
//#region src/typegen/generate.d.ts
|
|
1299
|
+
export declare function validateArgsShape(toolName: string, args: Record<string, unknown>, schema: Record<string, unknown>, caller?: 'call_tool' | 'toolCall'): void;
|
|
624
1300
|
/**
|
|
625
1301
|
* Module specifier authored machines import the tools surface from (R4).
|
|
626
1302
|
* The sandbox component mounts {@link GeneratedToolTypes.bridgeSource} here.
|
|
627
1303
|
*/
|
|
628
|
-
declare const TOOLS_MODULE_SPECIFIER = "@astral-mcp/tools";
|
|
1304
|
+
export declare const TOOLS_MODULE_SPECIFIER = "@astral-mcp/tools";
|
|
629
1305
|
/**
|
|
630
1306
|
* Name of the global the sandbox host MUST inject before authored code runs.
|
|
631
1307
|
*
|
|
@@ -646,7 +1322,7 @@ declare const TOOLS_MODULE_SPECIFIER = "@astral-mcp/tools";
|
|
|
646
1322
|
* - If the hook is missing, every tool function throws, keeping the bridge
|
|
647
1323
|
* inert outside a properly initialized sandbox (S1).
|
|
648
1324
|
*/
|
|
649
|
-
declare const TOOL_CALL_HOST_GLOBAL = "__projection_toolcall";
|
|
1325
|
+
export declare const TOOL_CALL_HOST_GLOBAL = "__projection_toolcall";
|
|
650
1326
|
/** Output of {@link generateToolTypes}. */
|
|
651
1327
|
interface GeneratedToolTypes {
|
|
652
1328
|
/** `.d.ts` text for the `@astral-mcp/tools` surface (serve via discover_apis, R3). */
|
|
@@ -656,36 +1332,45 @@ interface GeneratedToolTypes {
|
|
|
656
1332
|
/** Compact per-tool overview block for inline embedding by discover_apis (R3). */
|
|
657
1333
|
overview: string;
|
|
658
1334
|
/**
|
|
659
|
-
* Per-tool slice of `dts`, keyed by tool name (R34)
|
|
660
|
-
*
|
|
661
|
-
*
|
|
662
|
-
* the `
|
|
1335
|
+
* Per-tool slice of `dts`, keyed by tool name (R34) — the qualified
|
|
1336
|
+
* `<alias>.<name>` on a multi-server view (R45): the alias declarations
|
|
1337
|
+
* and the `tools.<name>` (`tools.<alias>.<name>`) member signature exactly
|
|
1338
|
+
* as they appear in the full file, so `describe_tool` output type-checks
|
|
1339
|
+
* against the `projection://types/tools.d.ts` resource.
|
|
663
1340
|
*/
|
|
664
1341
|
toolTypes: Map<string, ToolTypeText>;
|
|
665
1342
|
}
|
|
666
1343
|
/** The generated types for one tool, as served by `describe_tool` (R34). */
|
|
667
1344
|
interface ToolTypeText {
|
|
668
|
-
/**
|
|
1345
|
+
/**
|
|
1346
|
+
* `<Pascal>Input` alias name, unique within the `.d.ts` —
|
|
1347
|
+
* `<alias>_<Pascal>Input` on a multi-server view (R45).
|
|
1348
|
+
*/
|
|
669
1349
|
inputAlias: string;
|
|
670
1350
|
/** `<Pascal>Output` alias name, when the tool declares an `outputSchema`. */
|
|
671
1351
|
outputAlias?: string;
|
|
672
1352
|
/**
|
|
673
1353
|
* The alias declarations verbatim from the `.d.ts`, then the member
|
|
674
1354
|
* signature verbatim modulo indentation: the `tools` member is re-indented
|
|
675
|
-
* to column 0 under a `// Member of the \`tools\` surface:` line
|
|
1355
|
+
* to column 0 under a `// Member of the \`tools\` surface:` line
|
|
1356
|
+
* (`// Member of tools.<alias>:` on a multi-server view, R45).
|
|
676
1357
|
*/
|
|
677
1358
|
dts: string;
|
|
678
1359
|
}
|
|
679
1360
|
/**
|
|
680
1361
|
* Generate the `@astral-mcp/tools` artifacts for a wrapped server.
|
|
681
1362
|
*
|
|
682
|
-
* Accepts
|
|
683
|
-
*
|
|
684
|
-
*
|
|
1363
|
+
* Accepts the full aliased {@link ToolCatalog} (exactly one server: R30's
|
|
1364
|
+
* single-instance instantiation), a {@link ServerCatalog} directly, or the
|
|
1365
|
+
* multi-server view of a `wrapMcpServers` facade, which gets one
|
|
1366
|
+
* `tools.<alias>` object type per box and qualified names throughout (R45).
|
|
1367
|
+
*/
|
|
1368
|
+
export declare function generateToolTypes(catalog: ToolCatalog | ServerCatalog, options?: GenerateToolTypesOptions): GeneratedToolTypes;
|
|
1369
|
+
/**
|
|
1370
|
+
* The policy options (R14, R49) decide the posture wording in the doc
|
|
1371
|
+
* comments and the overview.
|
|
685
1372
|
*/
|
|
686
|
-
|
|
687
|
-
//#endregion
|
|
688
|
-
//#region src/typegen/json-schema.d.ts
|
|
1373
|
+
type GenerateToolTypesOptions = PolicyOptions;
|
|
689
1374
|
/**
|
|
690
1375
|
* Convert a JSON Schema into TypeScript type text.
|
|
691
1376
|
*
|
|
@@ -693,63 +1378,23 @@ declare function generateToolTypes(catalog: ToolCatalog | ServerCatalog): Genera
|
|
|
693
1378
|
* @param indent - Indentation prefix of the line the type is emitted on;
|
|
694
1379
|
* nested object literals indent relative to it.
|
|
695
1380
|
*/
|
|
696
|
-
declare function schemaToTsType(schema: unknown, indent?: string): string;
|
|
1381
|
+
export declare function schemaToTsType(schema: unknown, indent?: string): string;
|
|
697
1382
|
/**
|
|
698
1383
|
* True when the schema requires at least one argument property — used to
|
|
699
1384
|
* decide whether the generated tool function's `args` parameter is optional.
|
|
700
1385
|
*/
|
|
701
|
-
declare function schemaRequiresArgs(schema: unknown): boolean;
|
|
702
|
-
//#endregion
|
|
703
|
-
//#region src/catalog/search.d.ts
|
|
704
|
-
interface SearchToolsArgs {
|
|
705
|
-
/** Plain substring filter over name and description (case-insensitive). */
|
|
706
|
-
query?: string;
|
|
707
|
-
/** Opaque cursor from a previous page's `nextCursor`. */
|
|
708
|
-
cursor?: string;
|
|
709
|
-
}
|
|
710
|
-
interface SearchToolsOptions {
|
|
711
|
-
/** Per-tool policy overrides (R14); decides the rendered posture. */
|
|
712
|
-
policy?: PolicyOverrides;
|
|
713
|
-
/** Tools per page. Default {@link DEFAULT_SEARCH_PAGE_SIZE}, clamped to [1, 200]. */
|
|
714
|
-
pageSize?: number;
|
|
715
|
-
}
|
|
716
|
-
/** Page size — a transport bound, independent of the R37 inline threshold. */
|
|
717
|
-
declare const DEFAULT_SEARCH_PAGE_SIZE = 50;
|
|
718
|
-
/** `query` is capped (R33): it is matched as a plain substring, never a regex. */
|
|
719
|
-
declare const MAX_QUERY_CHARS = 200;
|
|
720
|
-
/** The three normative posture strings (R33) — models match on these. */
|
|
721
|
-
type ToolPosture = 'runs immediately' | 'asks for approval' | 'blocked by policy';
|
|
722
|
-
/** One entry of a `search_tools` page (R33). */
|
|
723
|
-
interface ToolSummary {
|
|
724
|
-
name: string;
|
|
725
|
-
purpose: string;
|
|
726
|
-
posture: ToolPosture;
|
|
727
|
-
destructive: boolean;
|
|
728
|
-
}
|
|
729
|
-
interface SearchToolsPage {
|
|
730
|
-
tools: ToolSummary[];
|
|
731
|
-
/** Matching tools across all pages. */
|
|
732
|
-
total: number;
|
|
733
|
-
nextCursor?: string;
|
|
734
|
-
}
|
|
735
|
-
/** Map a policy decision to its plain-words posture (R33). */
|
|
736
|
-
declare function postureOf(decision: PolicyDecision): ToolPosture;
|
|
737
|
-
/** Summarize one catalog entry (R33). */
|
|
738
|
-
declare function summarizeTool(entry: ToolCatalogEntry, policy?: PolicyOverrides): ToolSummary;
|
|
739
|
-
/**
|
|
740
|
-
* Run `search_tools`. Throws a `validate`-phase {@link ProjectionError} for a
|
|
741
|
-
* malformed cursor, one issued for a different query, or one pointing past
|
|
742
|
-
* the end of the results (R33).
|
|
743
|
-
*/
|
|
744
|
-
declare function searchTools(catalog: ServerCatalog, args: SearchToolsArgs, options?: SearchToolsOptions): CallToolResult;
|
|
745
|
-
//#endregion
|
|
746
|
-
//#region src/catalog/describe.d.ts
|
|
1386
|
+
export declare function schemaRequiresArgs(schema: unknown): boolean;
|
|
747
1387
|
interface DescribeToolArgs {
|
|
748
1388
|
name: string;
|
|
1389
|
+
/**
|
|
1390
|
+
* Include the generated TypeScript for the tool (R34). Off by default:
|
|
1391
|
+
* the JSON Schema already carries the shape, and the types double the
|
|
1392
|
+
* payload — ask for them when authoring a machine.
|
|
1393
|
+
*/
|
|
1394
|
+
types?: boolean;
|
|
749
1395
|
}
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
policy?: PolicyOverrides;
|
|
1396
|
+
/** The policy options (R14, R49) decide the rendered posture. */
|
|
1397
|
+
interface DescribeToolOptions extends PolicyOptions {
|
|
753
1398
|
/** Per-tool generated types from typegen (R34). */
|
|
754
1399
|
toolTypes?: ReadonlyMap<string, ToolTypeText>;
|
|
755
1400
|
}
|
|
@@ -773,9 +1418,7 @@ interface ToolDescription {
|
|
|
773
1418
|
* Run `describe_tool`. Throws a `validate`-phase {@link ProjectionError}
|
|
774
1419
|
* when the wrapped server declares no such tool (R34).
|
|
775
1420
|
*/
|
|
776
|
-
declare function describeTool(catalog: ServerCatalog, args: DescribeToolArgs, options?: DescribeToolOptions): CallToolResult;
|
|
777
|
-
//#endregion
|
|
778
|
-
//#region src/catalog/schema-safety.d.ts
|
|
1421
|
+
export declare function describeTool(catalog: ServerCatalog, args: DescribeToolArgs, options?: DescribeToolOptions): CallToolResult;
|
|
779
1422
|
/**
|
|
780
1423
|
* JSON Schema sanitizer for `describe_tool` (PRD R34, S5).
|
|
781
1424
|
*
|
|
@@ -786,8 +1429,11 @@ declare function describeTool(catalog: ServerCatalog, args: DescribeToolArgs, op
|
|
|
786
1429
|
* model it passes through:
|
|
787
1430
|
*
|
|
788
1431
|
* 1. a key allowlist — only the keywords a model needs to build arguments
|
|
789
|
-
* survive; everything else (examples,
|
|
790
|
-
*
|
|
1432
|
+
* survive; everything else (examples, comments, vendor keys) is dropped.
|
|
1433
|
+
* `default` survives only as a scalar the model can act on (a number, a
|
|
1434
|
+
* boolean, `null`, or a short plain string — {@link safeDefault}): an
|
|
1435
|
+
* omitted argument silently takes it, and a model that cannot see a
|
|
1436
|
+
* default page size reads a page as the whole set (D29);
|
|
791
1437
|
* 2. string neutralization — every string VALUE has control and bidi
|
|
792
1438
|
* characters replaced and is length-capped. Property NAMES and
|
|
793
1439
|
* `required` entries are kept verbatim: they are the keys the model must
|
|
@@ -802,9 +1448,9 @@ declare function describeTool(catalog: ServerCatalog, args: DescribeToolArgs, op
|
|
|
802
1448
|
* Pure and total: never throws, never returns anything but plain JSON.
|
|
803
1449
|
*/
|
|
804
1450
|
/** Keywords that survive sanitization (R34). */
|
|
805
|
-
declare const SCHEMA_KEY_ALLOWLIST: ReadonlySet<string>;
|
|
1451
|
+
export declare const SCHEMA_KEY_ALLOWLIST: ReadonlySet<string>;
|
|
806
1452
|
/** Rendered-schema cap per tool, in characters of its JSON text (R34). */
|
|
807
|
-
declare const MAX_SCHEMA_CHARS: number;
|
|
1453
|
+
export declare const MAX_SCHEMA_CHARS: number;
|
|
808
1454
|
interface SanitizedSchema {
|
|
809
1455
|
/** The sanitized schema, always a plain JSON object. */
|
|
810
1456
|
schema: Record<string, unknown>;
|
|
@@ -817,31 +1463,311 @@ interface SanitizedSchema {
|
|
|
817
1463
|
* Non-object input (a server declaring `inputSchema: true`, or garbage)
|
|
818
1464
|
* sanitizes to `{}`.
|
|
819
1465
|
*/
|
|
820
|
-
declare function sanitizeSchema(schema: unknown, maxChars?: number): SanitizedSchema;
|
|
821
|
-
//#endregion
|
|
822
|
-
//#region src/errors.d.ts
|
|
1466
|
+
export declare function sanitizeSchema(schema: unknown, maxChars?: number): SanitizedSchema;
|
|
823
1467
|
/**
|
|
824
|
-
*
|
|
1468
|
+
* Compact call signatures (DECISIONS D32): a tool's top-level arguments on
|
|
1469
|
+
* one line, so a model can `call_tool` straight from a search hit or a
|
|
1470
|
+
* `validate` error instead of spending a turn on `describe_tool`.
|
|
1471
|
+
*
|
|
1472
|
+
* {owner: string, repo: string, state?: "open"|"closed", perPage?: number = 30}
|
|
1473
|
+
*
|
|
1474
|
+
* Required arguments first (in declaration order), then optional ones
|
|
1475
|
+
* (`?`), each with its type: JSON Schema types, `T[]` for arrays, the
|
|
1476
|
+
* literal members of an `enum` or `const`, and a scalar default (`= 30`)
|
|
1477
|
+
* when the sanitizer kept one (D29). Only the top level is rendered: an
|
|
1478
|
+
* object or array-of-objects argument shows as `object` / `object[]` and
|
|
1479
|
+
* the signature says `describe_tool` has the full schema; so does one cut
|
|
1480
|
+
* to fit {@link MAX_SIGNATURE_CHARS}.
|
|
1481
|
+
*
|
|
1482
|
+
* S5: built from the SANITIZED schema only ({@link sanitizeSchema}), so
|
|
1483
|
+
* every enum string and default already passed the allowlist and the
|
|
1484
|
+
* default rule. Property names stay server data: a name that is a plain
|
|
1485
|
+
* identifier prints bare, any other is JSON-quoted after neutralization,
|
|
1486
|
+
* and a name too long to print whole is left out (and the signature marked
|
|
1487
|
+
* incomplete) rather than shown cut — a cut name is a wrong key. Type words
|
|
1488
|
+
* outside the JSON Schema vocabulary print as `any`. The result is one
|
|
1489
|
+
* line: nothing a server writes can open a line of its own.
|
|
825
1490
|
*/
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
1491
|
+
/** A tool's compact call signature. */
|
|
1492
|
+
interface CallSignature {
|
|
1493
|
+
/** One line: `{a: string, b?: number = 1}`; `{}` for a tool that takes no arguments. */
|
|
1494
|
+
text: string;
|
|
1495
|
+
/**
|
|
1496
|
+
* True when the line shows the whole input schema's top level: no
|
|
1497
|
+
* nested object fields, no argument cut for length, no enum cut short,
|
|
1498
|
+
* no schema the sanitizer truncated. Copy otherwise says `describe_tool`
|
|
1499
|
+
* has the full schema.
|
|
1500
|
+
*/
|
|
1501
|
+
complete: boolean;
|
|
1502
|
+
/**
|
|
1503
|
+
* True when a call can be built from the line alone: the top level is a
|
|
1504
|
+
* plain object of properties and every required argument is shown with
|
|
1505
|
+
* a flat type. What is missing, if anything, is optional. Copy otherwise
|
|
1506
|
+
* says to call `describe_tool` first.
|
|
1507
|
+
*/
|
|
1508
|
+
callable: boolean;
|
|
838
1509
|
}
|
|
839
|
-
/**
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
1510
|
+
/**
|
|
1511
|
+
* Where optional arguments stop, in characters (required ones go on to
|
|
1512
|
+
* twice this). Sized on the eval
|
|
1513
|
+
* fixtures (D32): it keeps every argument of 93 of the 95 GitHub tools,
|
|
1514
|
+
* all 21 MongoDB and all 14 filesystem tools; the cut ones say so.
|
|
1515
|
+
*/
|
|
1516
|
+
export declare const MAX_SIGNATURE_CHARS = 480;
|
|
1517
|
+
/**
|
|
1518
|
+
* The compact signature of a tool's input schema; never throws. With
|
|
1519
|
+
* `shared` (D34), a required argument the box's shared-args line already
|
|
1520
|
+
* shows with the same type, and that has no default, is written by name
|
|
1521
|
+
* alone (`{connectionId, database, newName: string}`): copy that renders
|
|
1522
|
+
* this text must show that line on the same page. `complete` and
|
|
1523
|
+
* `callable` are unchanged.
|
|
1524
|
+
*/
|
|
1525
|
+
export declare function callSignature(inputSchema: unknown, options?: {
|
|
1526
|
+
shared?: readonly SharedArg[];
|
|
1527
|
+
}): CallSignature;
|
|
1528
|
+
/**
|
|
1529
|
+
* One argument's meaning, from its description (D33): what a signature's
|
|
1530
|
+
* `name: type` cannot say, such as where a `connectionId` comes from or
|
|
1531
|
+
* that a `sha` may also be a branch name.
|
|
1532
|
+
*/
|
|
1533
|
+
interface ArgGloss {
|
|
1534
|
+
/** The argument's name, as the schema declares it. */
|
|
1535
|
+
name: string;
|
|
1536
|
+
/** Its description on one line: whitespace collapsed, invisible characters gone, neutralized, capped. */
|
|
1537
|
+
text: string;
|
|
1538
|
+
}
|
|
1539
|
+
/**
|
|
1540
|
+
* Longest gloss on a search page, in characters, before `…`. Sized on the
|
|
1541
|
+
* eval fixtures (D33): it holds the median informative GitHub description
|
|
1542
|
+
* whole (48 characters) and keeps the instruction in the first sentences
|
|
1543
|
+
* of a longer one (Sentry's median is 124, MongoDB's `connectionId` 173);
|
|
1544
|
+
* past it are option lists and examples that `describe_tool` serves.
|
|
1545
|
+
*/
|
|
1546
|
+
export declare const MAX_GLOSS_CHARS = 120;
|
|
1547
|
+
/**
|
|
1548
|
+
* The glosses of a tool's arguments (D33): by default its required ones (a
|
|
1549
|
+
* call cannot go without them, and a model that cannot tell what one means
|
|
1550
|
+
* invents a value), or the `names` given (the arguments a rejected call
|
|
1551
|
+
* sent). An argument gets a gloss when the SANITIZED schema (S5) gives it a
|
|
1552
|
+
* description that says something its name does not: one made only of
|
|
1553
|
+
* filler words and words of the tool's or its arguments' names is skipped,
|
|
1554
|
+
* and so is an `enum` or `const` argument, whose values the signature
|
|
1555
|
+
* already lists (on the GitHub fixture its descriptions were a third of
|
|
1556
|
+
* the gloss text and said "The method to execute" or listed the options
|
|
1557
|
+
* again). Each gloss is one line cut at a word to `maxChars` (default
|
|
1558
|
+
* {@link MAX_GLOSS_CHARS}), and a tool's glosses stop at twice that.
|
|
1559
|
+
* Never throws.
|
|
1560
|
+
*/
|
|
1561
|
+
export declare function argGlosses(toolName: string, inputSchema: unknown, options?: {
|
|
1562
|
+
names?: readonly string[];
|
|
1563
|
+
maxChars?: number;
|
|
1564
|
+
}): ArgGloss[];
|
|
1565
|
+
/**
|
|
1566
|
+
* A required argument most of a box's tools share (D34): `connectionId` in
|
|
1567
|
+
* the MongoDB box, `owner` and `repo` in GitHub's.
|
|
1568
|
+
*/
|
|
1569
|
+
interface SharedArg {
|
|
1570
|
+
/** The argument's name, as the schemas declare it. */
|
|
1571
|
+
name: string;
|
|
1572
|
+
/** Its type as a signature prints it, the same in every tool counted. */
|
|
1573
|
+
type: string;
|
|
1574
|
+
/** How many of the box's tools require it with that type. */
|
|
1575
|
+
tools: number;
|
|
1576
|
+
/** Its gloss (D33), when most of those tools give it the same one. */
|
|
1577
|
+
gloss?: string;
|
|
1578
|
+
}
|
|
1579
|
+
/**
|
|
1580
|
+
* Share of a box's tools that must require an argument (same name, same
|
|
1581
|
+
* rendered type) for it to be shared (D34). Sized on the offline catalogs:
|
|
1582
|
+
* the arguments that recur are required by 58–95 % of their box's tools
|
|
1583
|
+
* (GitHub `repo` 58 %, `owner` 66 %; MongoDB `collection` 71 %, `database`
|
|
1584
|
+
* 90 %, `connectionId` 95 %; filesystem `path` 79 %; Sentry
|
|
1585
|
+
* `organizationSlug` 70 %; Slack `channel_id` 63 %), and the next ones by at
|
|
1586
|
+
* most 25 % (Notion `page_id`, Slack `thread_ts`, GitHub `method` 16 %).
|
|
1587
|
+
*/
|
|
1588
|
+
export declare const SHARED_ARG_SHARE = 0.5;
|
|
1589
|
+
/** Fewest tools an argument is shared by: two tools agreeing is not a pattern. */
|
|
1590
|
+
export declare const MIN_SHARED_ARG_TOOLS = 3;
|
|
1591
|
+
/** Most shared arguments a box's line shows (MongoDB has three). */
|
|
1592
|
+
export declare const MAX_SHARED_ARGS = 4;
|
|
1593
|
+
/**
|
|
1594
|
+
* The shared arguments of a box's tools (D34): required arguments, with a
|
|
1595
|
+
* flat type (no nested shape, no cut enum) and no default, that at least
|
|
1596
|
+
* {@link SHARED_ARG_SHARE} of `tools` (and {@link MIN_SHARED_ARG_TOOLS})
|
|
1597
|
+
* require under the same name and the same rendered type; most shared
|
|
1598
|
+
* first, then in order of first appearance, at most
|
|
1599
|
+
* {@link MAX_SHARED_ARGS}. An argument whose name differs in type across
|
|
1600
|
+
* tools counts only the tools of its most common type. Each carries the
|
|
1601
|
+
* D33 gloss that at least half of the tools counted give it, if one does.
|
|
1602
|
+
* Built from the sanitized schemas (S5). `tools` should be deduplicated by
|
|
1603
|
+
* name. Never throws.
|
|
1604
|
+
*/
|
|
1605
|
+
export declare function sharedArgs(tools: readonly {
|
|
1606
|
+
name: string;
|
|
1607
|
+
inputSchema: unknown;
|
|
1608
|
+
}[]): SharedArg[];
|
|
1609
|
+
interface SearchToolsArgs {
|
|
1610
|
+
/**
|
|
1611
|
+
* Words to match against tool names, titles and descriptions; any word
|
|
1612
|
+
* matches, name hits weigh more (case-insensitive).
|
|
1613
|
+
*/
|
|
1614
|
+
query?: string;
|
|
1615
|
+
/** Opaque cursor from a previous page's `nextCursor`. */
|
|
1616
|
+
cursor?: string;
|
|
1617
|
+
}
|
|
1618
|
+
/** The policy options (R14, R49) decide the rendered posture. */
|
|
1619
|
+
interface SearchToolsOptions extends PolicyOptions {
|
|
1620
|
+
/**
|
|
1621
|
+
* Tools per page, for listings and queries alike. Default {@link DEFAULT_SEARCH_PAGE_SIZE}
|
|
1622
|
+
* for a listing (no query) and {@link DEFAULT_QUERY_PAGE_SIZE} for a
|
|
1623
|
+
* query; clamped to [1, 200].
|
|
1624
|
+
*/
|
|
1625
|
+
pageSize?: number;
|
|
1626
|
+
}
|
|
1627
|
+
/** Page size of a listing — a transport bound, independent of the R37 inline threshold. */
|
|
1628
|
+
export declare const DEFAULT_SEARCH_PAGE_SIZE = 50;
|
|
1629
|
+
/**
|
|
1630
|
+
* Page size of a ranked (queried) search (D32). On the offline search
|
|
1631
|
+
* corpus (1,173 cases, D22) the gold tool is in the top 15 in 96.6 % of
|
|
1632
|
+
* cases and in the top 50 in 97.7 %, while every row past the 15th costs
|
|
1633
|
+
* about 30 tokens on every search that has it: "create issue" in the
|
|
1634
|
+
* GitHub box matched 25. The rest stay a cursor away.
|
|
1635
|
+
*/
|
|
1636
|
+
export declare const DEFAULT_QUERY_PAGE_SIZE = 15;
|
|
1637
|
+
/**
|
|
1638
|
+
* How many of a query's best hits carry their call signature (D32). On the
|
|
1639
|
+
* offline corpus the gold tool ranks 1st in 75.5 % of cases, top 3 in
|
|
1640
|
+
* 89.9 % (93.4 % of the queries models actually sent) and top 5 in 92.8 %:
|
|
1641
|
+
* the 4th and 5th signatures would buy 2.9 points for two more lines on
|
|
1642
|
+
* every search. A signature's optional arguments are cut at 480
|
|
1643
|
+
* characters (`MAX_SIGNATURE_CHARS`); over the GitHub tools the median is
|
|
1644
|
+
* 109 (~27 tokens), so the three typically cost ~80 tokens, against
|
|
1645
|
+
* ~800–1,400 for one `describe_tool` answer plus the turn that fetches it.
|
|
1646
|
+
*/
|
|
1647
|
+
export declare const SIGNATURE_HITS = 3;
|
|
1648
|
+
/**
|
|
1649
|
+
* How many of a query's best hits also gloss their required arguments
|
|
1650
|
+
* (D33): all of the signed ones. A page shows a gloss once; a later hit
|
|
1651
|
+
* with the same argument and the same words leaves it out.
|
|
1652
|
+
*/
|
|
1653
|
+
export declare const GLOSS_HITS = 3;
|
|
1654
|
+
/** `query` is capped (R33): its words are matched as plain substrings, never a regex. */
|
|
1655
|
+
export declare const MAX_QUERY_CHARS = 200;
|
|
1656
|
+
/** One entry of a `search_tools` page (R33). */
|
|
1657
|
+
interface ToolSummary {
|
|
1658
|
+
name: string;
|
|
1659
|
+
purpose: string;
|
|
1660
|
+
posture: ToolPosture;
|
|
1661
|
+
destructive: boolean;
|
|
1662
|
+
/**
|
|
1663
|
+
* The tool's compact call signature (D32): on the first
|
|
1664
|
+
* {@link SIGNATURE_HITS} hits of a query, unless the tool is blocked.
|
|
1665
|
+
*/
|
|
1666
|
+
signature?: CallSignature;
|
|
1667
|
+
/**
|
|
1668
|
+
* What the tool's required arguments mean, where the name alone does not
|
|
1669
|
+
* say (D33): on the first {@link GLOSS_HITS} signed hits, when any
|
|
1670
|
+
* required argument has such a description.
|
|
1671
|
+
*/
|
|
1672
|
+
glosses?: ArgGloss[];
|
|
1673
|
+
}
|
|
1674
|
+
interface SearchToolsPage {
|
|
1675
|
+
tools: ToolSummary[];
|
|
1676
|
+
/** Matching tools across all pages. */
|
|
1677
|
+
total: number;
|
|
1678
|
+
nextCursor?: string;
|
|
1679
|
+
}
|
|
1680
|
+
/**
|
|
1681
|
+
* Summarize one catalog entry (R33); `context` decides its posture (R14,
|
|
1682
|
+
* R49). With `withSignature`, a tool that is not blocked also carries its
|
|
1683
|
+
* call signature (D32): there is no point showing how to call a tool the
|
|
1684
|
+
* gate refuses. With `withGlosses` too, it carries its required
|
|
1685
|
+
* arguments' glosses (D33), when it has any.
|
|
1686
|
+
*/
|
|
1687
|
+
export declare function summarizeTool(entry: ToolCatalogEntry, context?: PolicyContext, withSignature?: boolean, withGlosses?: boolean): ToolSummary;
|
|
1688
|
+
/**
|
|
1689
|
+
* Run `search_tools`. Throws a `validate`-phase {@link ProjectionError} for a
|
|
1690
|
+
* malformed cursor, one issued for a different query, or one pointing past
|
|
1691
|
+
* the end of the results (R33).
|
|
1692
|
+
*/
|
|
1693
|
+
export declare function searchTools(catalog: ServerCatalog, args: SearchToolsArgs, options?: SearchToolsOptions): CallToolResult;
|
|
1694
|
+
/** One entry of a multi-server search page (R43): the R33 entry plus its box. */
|
|
1695
|
+
interface BoxedToolSummary extends ToolSummary {
|
|
1696
|
+
/** The box (alias) the tool lives in; `name` is qualified with it. */
|
|
1697
|
+
box: string;
|
|
1698
|
+
}
|
|
1699
|
+
interface BoxedSearchPage {
|
|
1700
|
+
/**
|
|
1701
|
+
* The box overview (R43): the server's full `instructions`, neutralized
|
|
1702
|
+
* and capped. Only on the first page of an empty-query `search_<alias>`
|
|
1703
|
+
* whose server sends instructions.
|
|
1704
|
+
*/
|
|
1705
|
+
overview?: string;
|
|
1706
|
+
tools: BoxedToolSummary[];
|
|
1707
|
+
/** Matching tools across all pages. */
|
|
1708
|
+
total: number;
|
|
1709
|
+
nextCursor?: string;
|
|
1710
|
+
}
|
|
1711
|
+
/** The policy options (R14, R49) are decided at the gate and on resume. */
|
|
1712
|
+
interface RunSegmentOptions extends PolicyOptions {
|
|
1713
|
+
/** Submitted module source (R2). */
|
|
1714
|
+
code: string;
|
|
1715
|
+
lang?: 'ts' | 'js';
|
|
1716
|
+
/** Machine input, passed as the machine's `input` on a fresh run. */
|
|
1717
|
+
input?: unknown;
|
|
1718
|
+
/** Catalog of the wrapped server whose tools the machine may call. */
|
|
1719
|
+
catalog: ServerCatalog;
|
|
1720
|
+
/** Generated `@astral-mcp/tools` bridge source (from typegen). */
|
|
1721
|
+
bridgeSource: string;
|
|
1722
|
+
/** Executes approved/auto tool calls against the wrapped server. */
|
|
1723
|
+
executor: ToolExecutor;
|
|
1724
|
+
/** Resource caps; clamped at the hard maxima (R12). */
|
|
1725
|
+
caps?: Partial<ResourceCaps>;
|
|
1726
|
+
/** Zero-based MRTR segment index — stamps logs (R31). */
|
|
1727
|
+
segment?: number;
|
|
1728
|
+
/**
|
|
1729
|
+
* Resume state. Present on every segment after the first: the verified
|
|
1730
|
+
* snapshot and the pending calls sealed alongside it, plus the verdicts
|
|
1731
|
+
* the client returned for those calls.
|
|
1732
|
+
*/
|
|
1733
|
+
resume?: {
|
|
1734
|
+
snapshot: unknown;
|
|
1735
|
+
/** Gated calls as sealed into the blob — the approved name+args (S2). */
|
|
1736
|
+
pending: Record<string, PendingToolCall>;
|
|
1737
|
+
/** Verdict per pending key; a missing entry counts as declined (R17). */
|
|
1738
|
+
verdicts: Record<string, ApprovalVerdict>;
|
|
1739
|
+
/** Audit entries accumulated in earlier segments. */
|
|
1740
|
+
audit?: AuditEntry[];
|
|
1741
|
+
/** Round-trip counter carried in the blob (R18). */
|
|
1742
|
+
trip: number;
|
|
1743
|
+
/**
|
|
1744
|
+
* Calls held on their server's question (G1), keyed by call id, with
|
|
1745
|
+
* the server's answers to send back, keyed like the hold's questions'
|
|
1746
|
+
* own keys. Re-issued as recorded; no verdict applies to them.
|
|
1747
|
+
*/
|
|
1748
|
+
held?: Record<string, {
|
|
1749
|
+
hold: UpstreamHold;
|
|
1750
|
+
responses: Record<string, unknown>;
|
|
1751
|
+
}>;
|
|
1752
|
+
};
|
|
1753
|
+
}
|
|
1754
|
+
/**
|
|
1755
|
+
* Run one execution segment to completion, suspension, or error.
|
|
1756
|
+
*
|
|
1757
|
+
* A segment is one MRTR round trip: fresh sandbox, fresh wall-clock budget,
|
|
1758
|
+
* fresh logs. On the first segment the module is validated (R24) before the
|
|
1759
|
+
* actor starts; on a resume the module is re-evaluated from the re-sent code
|
|
1760
|
+
* and the actor is restored from the sealed snapshot.
|
|
1761
|
+
*
|
|
1762
|
+
* Note: `runSegment` interpolates `input` into driver source without a size
|
|
1763
|
+
* check of its own — `executeMachine` enforces `maxInputBytes` before calling
|
|
1764
|
+
* it. Callers invoking `runSegment` directly must bound `input` themselves.
|
|
1765
|
+
*/
|
|
1766
|
+
export declare function runSegment(options: RunSegmentOptions): Promise<SegmentOutcome>;
|
|
1767
|
+
/** The single-server authoring contract (R3). */
|
|
1768
|
+
export declare const AUTHORING_CONTRACT: string;
|
|
843
1769
|
/** Stable URIs the server serves via `resources/read` (R3). */
|
|
844
|
-
declare const RESOURCE_URIS: {
|
|
1770
|
+
export declare const RESOURCE_URIS: {
|
|
845
1771
|
readonly toolTypes: "projection://types/tools.d.ts";
|
|
846
1772
|
readonly contract: "projection://docs/authoring-contract.md";
|
|
847
1773
|
readonly happyPath: "projection://examples/happy-path.ts";
|
|
@@ -857,12 +1783,18 @@ interface ProjectionResource {
|
|
|
857
1783
|
/** Resource-level metadata (e.g. the MCP Apps `ui` object). */
|
|
858
1784
|
_meta?: Record<string, unknown>;
|
|
859
1785
|
}
|
|
860
|
-
declare const EXAMPLE_HAPPY_PATH
|
|
861
|
-
declare const EXAMPLE_DENIAL_FALLBACK
|
|
862
|
-
/**
|
|
863
|
-
|
|
1786
|
+
export declare const EXAMPLE_HAPPY_PATH: string;
|
|
1787
|
+
export declare const EXAMPLE_DENIAL_FALLBACK: string;
|
|
1788
|
+
/**
|
|
1789
|
+
* Build the code-mode resources Projection serves for a wrapped server (R3).
|
|
1790
|
+
* On a multi-server view the descriptions name the boxes and the contract
|
|
1791
|
+
* and the examples call qualified names from its first box, picked under
|
|
1792
|
+
* `policy` (R45, {@link toolSamples}); the box overview resources are the
|
|
1793
|
+
* multi-server facade's own (`catalog/box-guidance.ts`).
|
|
1794
|
+
*/
|
|
1795
|
+
export declare function buildResources(catalog: ServerCatalog, dts: string, policy?: PolicyOptions, maxRoundTrips?: number): ProjectionResource[];
|
|
864
1796
|
/** R37: above this many tools, `discover_apis` defers the per-tool list to the catalog tools. */
|
|
865
|
-
declare const DEFAULT_INLINE_TOOL_THRESHOLD = 40;
|
|
1797
|
+
export declare const DEFAULT_INLINE_TOOL_THRESHOLD = 40;
|
|
866
1798
|
interface DiscoverApisOptions {
|
|
867
1799
|
/** Whether the catalog projection (`search_tools` / `describe_tool`) is served (R37). */
|
|
868
1800
|
catalog?: boolean;
|
|
@@ -872,6 +1804,32 @@ interface DiscoverApisOptions {
|
|
|
872
1804
|
* clamped at 0, anything else falls back to the default.
|
|
873
1805
|
*/
|
|
874
1806
|
inlineThreshold?: number;
|
|
1807
|
+
/**
|
|
1808
|
+
* How the R37 pointer names the search tools a multi-server facade serves
|
|
1809
|
+
* (R43, R45): `searchToolsPhrase` of the view. Default `search_tools`.
|
|
1810
|
+
*/
|
|
1811
|
+
searchPhrase?: string;
|
|
1812
|
+
/**
|
|
1813
|
+
* Direct listing (`projections.direct`): the full sentence(s) telling the
|
|
1814
|
+
* model where tools are, replacing the R37 pointer's "Call <search tools>
|
|
1815
|
+
* to find tools …" — the served search tools and the boxes listed
|
|
1816
|
+
* directly, so the pointer never names a search tool the facade does not
|
|
1817
|
+
* serve.
|
|
1818
|
+
*/
|
|
1819
|
+
findTools?: string;
|
|
1820
|
+
/**
|
|
1821
|
+
* The resources the facade serves, when they go beyond
|
|
1822
|
+
* {@link buildResources} (a multi-server facade's box overviews, R45):
|
|
1823
|
+
* every one but the `ui://` dashboard is linked.
|
|
1824
|
+
*/
|
|
1825
|
+
resources?: readonly ProjectionResource[];
|
|
1826
|
+
/**
|
|
1827
|
+
* The facade's policy (R14, R49), which picks the sample tool names on a
|
|
1828
|
+
* multi-server view (R45, {@link toolSamples}).
|
|
1829
|
+
*/
|
|
1830
|
+
policy?: PolicyOptions;
|
|
1831
|
+
/** The facade's clamped `caps.maxRoundTrips` (R12), quoted by the contract's rule 5. */
|
|
1832
|
+
maxRoundTrips?: number;
|
|
875
1833
|
}
|
|
876
1834
|
/**
|
|
877
1835
|
* The `discover_apis` result: an inline overview sufficient to author a
|
|
@@ -881,37 +1839,79 @@ interface DiscoverApisOptions {
|
|
|
881
1839
|
* per-tool overview is replaced by the tool count and a pointer to
|
|
882
1840
|
* `search_tools` / `describe_tool` — the `.d.ts` resource stays complete.
|
|
883
1841
|
*/
|
|
884
|
-
declare function discoverApis(catalog: ServerCatalog, overview: string, options?: DiscoverApisOptions): CallToolResult;
|
|
1842
|
+
export declare function discoverApis(catalog: ServerCatalog, overview: string, options?: DiscoverApisOptions): CallToolResult;
|
|
885
1843
|
/** Which projections a facade serves (R1, R32). */
|
|
886
1844
|
interface ProjectionSelection {
|
|
887
1845
|
codeMode: boolean;
|
|
888
1846
|
catalog: boolean;
|
|
1847
|
+
/**
|
|
1848
|
+
* Whether the wrapped tools are also listed directly, as mediated aliases
|
|
1849
|
+
* of `call_tool` (`projections.direct`, resolved at wrap time). Absent
|
|
1850
|
+
* means not listed.
|
|
1851
|
+
*/
|
|
1852
|
+
direct?: boolean;
|
|
1853
|
+
/**
|
|
1854
|
+
* Multi-server facade under direct listing: the boxes listed directly, in
|
|
1855
|
+
* sorted alias order (the others keep the catalog path).
|
|
1856
|
+
*/
|
|
1857
|
+
directBoxes?: readonly string[];
|
|
889
1858
|
}
|
|
890
1859
|
/**
|
|
891
1860
|
* The wrapped server's `instructions`, followed by the preamble for the
|
|
892
1861
|
* projections in service (R29). The preamble is library copy the
|
|
893
1862
|
* integrating developer can replace wholesale.
|
|
894
1863
|
*/
|
|
895
|
-
declare function buildInstructions(catalog: ServerCatalog, preamble?: string, projections?: ProjectionSelection): string;
|
|
1864
|
+
export declare function buildInstructions(catalog: ServerCatalog, preamble?: string, projections?: ProjectionSelection): string;
|
|
896
1865
|
/** Preamble when only code mode is on (R29). */
|
|
897
|
-
declare const DEFAULT_CODE_MODE_PREAMBLE = "This server runs in code mode: instead of calling its tools directly, author an XState v5 state machine and run it with execute_machine, which orchestrates the tool calls for you.
|
|
1866
|
+
export declare const DEFAULT_CODE_MODE_PREAMBLE = "This server runs in code mode: instead of calling its tools directly, author an XState v5 state machine and run it with execute_machine, which orchestrates the tool calls for you. Tools that change something may pause the machine and ask the user to approve that specific action before it runs; each tool's listing says whether it runs immediately or asks for approval. Call discover_apis first for the tool types, the authoring contract, and worked examples.";
|
|
898
1867
|
/** Preamble when only the catalog projection is on (R29, R32). */
|
|
899
|
-
declare const DEFAULT_CATALOG_PREAMBLE = "This server exposes its tools through a catalog: call search_tools to find a tool,
|
|
1868
|
+
export declare const DEFAULT_CATALOG_PREAMBLE = "This server exposes its tools through a catalog: call search_tools to find a tool, then call_tool to run it, with the args its search hit shows (describe_tool gives any other tool's). Tools that change something may pause and ask the user to approve that specific action before it runs; each tool's listing says whether it runs immediately or asks for approval.";
|
|
900
1869
|
/** Preamble when both projections are on: the routing rule (R29). */
|
|
901
|
-
declare const DEFAULT_ROUTING_PREAMBLE = "This server offers two ways to use its tools. For a single action, call search_tools to find the tool,
|
|
1870
|
+
export declare const DEFAULT_ROUTING_PREAMBLE = "This server offers two ways to use its tools. For a single action, call search_tools to find the tool, then call_tool to run it, with the args its search hit shows (describe_tool gives any other tool's). For several steps, branching on results, computing over a list (counting, filtering, summing, comparing or checking every item), or a workflow worth saving, author an XState v5 state machine and run it with execute_machine (call discover_apis first for the tool types, the authoring contract, and worked examples). Either way, tools that change something may pause and ask the user to approve that specific action before it runs; each tool's listing says whether it runs immediately or asks for approval.";
|
|
902
1871
|
/** The R29 preamble variant for a projection selection. */
|
|
903
|
-
declare function defaultPreamble(projections: ProjectionSelection): string;
|
|
904
|
-
//#endregion
|
|
905
|
-
//#region src/wrap.d.ts
|
|
1872
|
+
export declare function defaultPreamble(projections: ProjectionSelection): string;
|
|
906
1873
|
interface WrapOptions {
|
|
907
1874
|
/** Blob sealing configuration; required for any gated flow (R22). */
|
|
908
1875
|
blob: BlobSealerConfig;
|
|
909
1876
|
/** Resource cap overrides, clamped at the hard maxima (R12). */
|
|
910
1877
|
caps?: Partial<ResourceCaps>;
|
|
911
|
-
/**
|
|
1878
|
+
/**
|
|
1879
|
+
* Per-tool policy overrides (R14). Read once, at wrap time: the facade
|
|
1880
|
+
* keeps its own frozen copy, so later changes to this object are not seen.
|
|
1881
|
+
*/
|
|
912
1882
|
policy?: PolicyOverrides;
|
|
913
|
-
/**
|
|
1883
|
+
/**
|
|
1884
|
+
* Approvals mode (R49). `'every-write'` (default) asks before every tool
|
|
1885
|
+
* that is not read-only. `'destructive-only'` also runs, without approval,
|
|
1886
|
+
* a non-read-only tool whose annotations declare `destructiveHint: false`
|
|
1887
|
+
* exactly; a missing or any other `destructiveHint` still asks. A per-tool
|
|
1888
|
+
* `policy` override wins over the mode. This extends the trust placed in
|
|
1889
|
+
* `readOnlyHint` to `destructiveHint: false` — a hostile server can
|
|
1890
|
+
* mislabel a damaging tool, so enable it only for servers you trust.
|
|
1891
|
+
* Read once, at wrap time; any other value fails the wrap (`validate`).
|
|
1892
|
+
*/
|
|
1893
|
+
approvals?: ApprovalsMode;
|
|
1894
|
+
/**
|
|
1895
|
+
* Masks sensitive argument fields before they reach an approval prompt
|
|
1896
|
+
* (R16). It receives the tool name and a copy of the arguments. On a
|
|
1897
|
+
* `wrapMcpServers` facade the name is the qualified `<alias>.<tool>`
|
|
1898
|
+
* (`fs.write_file`), so one hook can tell the boxes' same-named tools
|
|
1899
|
+
* apart; on `wrapMcpServer` it is the server's own tool name.
|
|
1900
|
+
*/
|
|
914
1901
|
redact?: RedactionHook;
|
|
1902
|
+
/**
|
|
1903
|
+
* How a machine suspension holding several gated calls is put to the
|
|
1904
|
+
* person (R15 amendment; DECISIONS D25). `'per-call'` (default) sends one
|
|
1905
|
+
* elicitation per call. `'batched'` sends one elicitation listing every
|
|
1906
|
+
* call in its full approval copy, with one unticked boolean per call (no
|
|
1907
|
+
* default): each call still runs only if its own box comes back ticked,
|
|
1908
|
+
* on the arguments sealed for it (S2). A client that answers the form
|
|
1909
|
+
* without the booleans declines every call. The layout is sealed in the
|
|
1910
|
+
* blob; a batch is capped at 10 calls and 20 000 characters, falling back
|
|
1911
|
+
* to per-call prompts above that. Read once, at wrap time; any other
|
|
1912
|
+
* value fails the wrap (`validate`).
|
|
1913
|
+
*/
|
|
1914
|
+
approvalPrompts?: ApprovalPrompts;
|
|
915
1915
|
/**
|
|
916
1916
|
* Replaces the whole R29 preamble, whichever projections are on (the
|
|
917
1917
|
* code-mode, catalog or routing copy).
|
|
@@ -949,12 +1949,68 @@ interface WrapOptions {
|
|
|
949
1949
|
* disabling both is an error at wrap time.
|
|
950
1950
|
*/
|
|
951
1951
|
projections?: ProjectionsOptions;
|
|
1952
|
+
/**
|
|
1953
|
+
* Where upstream tokens for sources given by URL live (R59). Default: the
|
|
1954
|
+
* OS credential store through the optional `@napi-rs/keyring` dependency,
|
|
1955
|
+
* falling back to memory (with one stderr warning) when it is absent or
|
|
1956
|
+
* the store is unavailable. A horizontally scaled deployment supplies a
|
|
1957
|
+
* shared store (R27). Unused by pre-built `McpServer` / `Client` sources.
|
|
1958
|
+
*/
|
|
1959
|
+
tokenStore?: TokenStore;
|
|
1960
|
+
/**
|
|
1961
|
+
* Whether the wrapped server's own questions reach the human (G1,
|
|
1962
|
+
* DECISIONS D24). `'refuse'` (default): Projection declares no
|
|
1963
|
+
* elicitation capability to the server, and a tool that asks anyway fails
|
|
1964
|
+
* with a result saying it needs the user's input. `'forward'`: the
|
|
1965
|
+
* capability is declared, and each question — a form, or a link to open —
|
|
1966
|
+
* is relayed to the human over MRTR in Projection's own prompt, headed
|
|
1967
|
+
* `Question from <box> (not an approval)` with the server's words quoted,
|
|
1968
|
+
* after the call itself passed the approval gate. Enable it only for
|
|
1969
|
+
* servers you trust to ask your users things: a hostile server can still
|
|
1970
|
+
* word a phishing question. On `wrapMcpServers` this is the default for
|
|
1971
|
+
* every box, and a source configuration can override it per box.
|
|
1972
|
+
* Read once, at wrap time; any other value fails the wrap (`validate`).
|
|
1973
|
+
*/
|
|
1974
|
+
upstreamElicitation?: UpstreamElicitationMode;
|
|
1975
|
+
/**
|
|
1976
|
+
* Bound, in milliseconds, on each wrapped source's connect and `tools/list`
|
|
1977
|
+
* introspection at wrap time (R47). A source that has not finished in time
|
|
1978
|
+
* fails the wrap with a `protocol` error, and every connection Projection
|
|
1979
|
+
* opened is closed (a `Client` you passed in stays open). Default 30 000.
|
|
1980
|
+
*/
|
|
1981
|
+
introspectTimeoutMs?: number;
|
|
952
1982
|
}
|
|
953
1983
|
/** Per-projection on/off switches (R1, R32). */
|
|
954
1984
|
interface ProjectionsOptions {
|
|
955
1985
|
codeMode?: boolean;
|
|
956
1986
|
catalog?: boolean;
|
|
1987
|
+
/**
|
|
1988
|
+
* Direct listing (see `facade/direct.ts`): also register one
|
|
1989
|
+
* **mediated** facade tool per wrapped tool, a thin alias of
|
|
1990
|
+
* `call_tool({ name, args })` under exactly the same gate, so the model
|
|
1991
|
+
* sees every tool's name, description and schema without searching.
|
|
1992
|
+
* `true` always lists them; `'auto'` lists them when their estimated
|
|
1993
|
+
* definitions fit `directBudgetTokens` (on a multi-server facade box by
|
|
1994
|
+
* box, smallest box first; the other boxes keep the catalog path);
|
|
1995
|
+
* `false` never. Default `'auto'` (DECISIONS D28). Needs the
|
|
1996
|
+
* catalog projection: `true` with `catalog: false` fails the wrap
|
|
1997
|
+
* (`validate`); `'auto'` then stays off.
|
|
1998
|
+
*/
|
|
1999
|
+
direct?: 'auto' | boolean;
|
|
2000
|
+
/**
|
|
2001
|
+
* `direct: 'auto'`: the largest estimated size, in tokens, of all mediated
|
|
2002
|
+
* tool definitions (name, description, input schema; characters / 3.5)
|
|
2003
|
+
* that is still listed directly. Default {@link DEFAULT_DIRECT_BUDGET_TOKENS}.
|
|
2004
|
+
*/
|
|
2005
|
+
directBudgetTokens?: number;
|
|
957
2006
|
}
|
|
2007
|
+
/**
|
|
2008
|
+
* Default `projections.directBudgetTokens`: about 8k estimated tokens of
|
|
2009
|
+
* tool definitions. Measured (explore/adaptive-surface.md): listing wins
|
|
2010
|
+
* clearly at 2k and 5.4k estimated tokens and breaks even on Sonnet 5.5 at
|
|
2011
|
+
* about 11k, whose tokenizer counts ~1.3–1.4× the estimate.
|
|
2012
|
+
*/
|
|
2013
|
+
export declare const DEFAULT_DIRECT_BUDGET_TOKENS = 8000;
|
|
958
2014
|
/** Options for registering the projections on a server the caller built. */
|
|
959
2015
|
interface RegisterOptions {
|
|
960
2016
|
/**
|
|
@@ -982,9 +2038,11 @@ interface ProjectionFacade {
|
|
|
982
2038
|
/**
|
|
983
2039
|
* Run `execute_machine` for one `tools/call`, first attempt or retry.
|
|
984
2040
|
* `clientCanElicit` reports whether the connected client declared the
|
|
985
|
-
* `elicitation` capability (legacy compat only; omit when unknown)
|
|
2041
|
+
* `elicitation` capability (legacy compat only; omit when unknown), and
|
|
2042
|
+
* `clientUrlElicitation` whether it declared URL mode (a forwarded
|
|
2043
|
+
* server link, G1, is otherwise shown as text in a form).
|
|
986
2044
|
*/
|
|
987
|
-
executeMachine(args: ExecuteMachineArgs, era?: ProtocolEra, clientCanElicit?: boolean): Promise<ToolCallResult>;
|
|
2045
|
+
executeMachine(args: ExecuteMachineArgs, era?: ProtocolEra, clientCanElicit?: boolean, clientUrlElicitation?: boolean): Promise<ToolCallResult>;
|
|
988
2046
|
/** Run `discover_apis`. */
|
|
989
2047
|
discoverApis(): CallToolResult;
|
|
990
2048
|
/** Run `search_tools` (R33). Failures come back as R25 structured error results. */
|
|
@@ -995,95 +2053,111 @@ interface ProjectionFacade {
|
|
|
995
2053
|
/** Run `describe_tool` (R34). Failures come back as R25 structured error results. */
|
|
996
2054
|
describeTool(args: {
|
|
997
2055
|
name: string;
|
|
2056
|
+
types?: boolean;
|
|
998
2057
|
}): CallToolResult;
|
|
999
2058
|
/**
|
|
1000
2059
|
* Run `call_tool` (R35) for one `tools/call`, first attempt or retry.
|
|
1001
|
-
* `era`
|
|
2060
|
+
* `era`, `clientCanElicit` and `clientUrlElicitation` as for `executeMachine`.
|
|
1002
2061
|
*/
|
|
1003
|
-
callTool(args: CallToolArgs, era?: ProtocolEra, clientCanElicit?: boolean): Promise<ToolCallResult>;
|
|
2062
|
+
callTool(args: CallToolArgs, era?: ProtocolEra, clientCanElicit?: boolean, clientUrlElicitation?: boolean): Promise<ToolCallResult>;
|
|
1004
2063
|
/** The projections this facade serves (R32). Frozen: fixed at wrap time. */
|
|
1005
2064
|
readonly projections: Readonly<ProjectionSelection>;
|
|
1006
2065
|
/** Build an `McpServer` carrying the instructions, the enabled projections' tools and the resources. */
|
|
1007
2066
|
createServer(ctx?: RegisterOptions): McpServer;
|
|
1008
2067
|
/** Register the enabled projections' tools and the resources on an `McpServer` the caller built. */
|
|
1009
2068
|
registerOn(server: McpServer, options?: RegisterOptions): void;
|
|
2069
|
+
/**
|
|
2070
|
+
* Delete the stored upstream tokens (R59) of the sources given by URL:
|
|
2071
|
+
* on a single-server facade, its source (call it without an alias); on a
|
|
2072
|
+
* multi-server facade, the box named by `alias`, or every such box
|
|
2073
|
+
* without one. No revocation yet. A facade or box not built from an
|
|
2074
|
+
* `UpstreamSpec`, or an unknown alias, is a `validate` error.
|
|
2075
|
+
*/
|
|
2076
|
+
signOut(alias?: string): Promise<void>;
|
|
1010
2077
|
/** Close the connection to the wrapped server. */
|
|
1011
2078
|
close(): Promise<void>;
|
|
1012
2079
|
}
|
|
1013
2080
|
/**
|
|
1014
|
-
* Wrap an MCP server
|
|
1015
|
-
* Projection
|
|
2081
|
+
* Wrap an MCP server — an in-process instance, a connected client, or a
|
|
2082
|
+
* remote server given by URL (`{ url, oauth? }`, R51) — into a Projection
|
|
2083
|
+
* facade.
|
|
1016
2084
|
*/
|
|
1017
|
-
declare function wrapMcpServer(source: McpServer | Client, options: WrapOptions): Promise<ProjectionFacade>;
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
/**
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
* A static, library-authored HTML app that renders an `execute_machine`
|
|
1024
|
-
* result — output, approval audit trail, console logs, or the structured
|
|
1025
|
-
* error — inside the host's sandboxed iframe, per the MCP Apps extension
|
|
1026
|
-
* (SEP-1865, spec 2026-01-26).
|
|
1027
|
-
*
|
|
1028
|
-
* Security posture (the §12.1 design constraint, satisfied by construction):
|
|
1029
|
-
* - The HTML is a compile-time constant. No model-authored code, no
|
|
1030
|
-
* template interpolation, no VM: UI rendering can never reach the tools
|
|
1031
|
-
* bridge because nothing dynamic executes server-side at all.
|
|
1032
|
-
* - Model-controlled data (outputs, audit args, log lines, error text)
|
|
1033
|
-
* enters the DOM exclusively through `textContent` — the app never uses
|
|
1034
|
-
* innerHTML — so hostile strings render inert (S6 extended to the app).
|
|
1035
|
-
* - The app requests no host capabilities: `appCapabilities: {}`. It cannot
|
|
1036
|
-
* call tools, open links, or write context; it only listens.
|
|
1037
|
-
*
|
|
1038
|
-
* Wire contract implemented (app side):
|
|
1039
|
-
* - `ui/initialize` request → await result → `ui/notifications/initialized`.
|
|
1040
|
-
* - Listens for `ui/notifications/tool-input`, `ui/notifications/tool-result`
|
|
1041
|
-
* and `ui/notifications/tool-cancelled`; ignores everything else.
|
|
1042
|
-
* - The result payload is the standard `CallToolResult` from
|
|
1043
|
-
* `execute_machine`: `structuredContent` is either
|
|
1044
|
-
* `{ output, approvals, logs }` or (with `isError`) the structured error
|
|
1045
|
-
* `{ phase, message, hint, logs }`.
|
|
1046
|
-
*/
|
|
1047
|
-
/** URI the dashboard is served under. MCP Apps mandates the ui:// scheme. */
|
|
1048
|
-
declare const DASHBOARD_RESOURCE_URI = "ui://projection/dashboard.html";
|
|
1049
|
-
/** MIME type the MCP Apps spec mandates for app HTML resources. */
|
|
1050
|
-
declare const MCP_APP_MIME_TYPE = "text/html;profile=mcp-app";
|
|
1051
|
-
declare const DASHBOARD_HTML = "<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\" />\n<title>Projection workflow dashboard</title>\n<style>\n :root {\n color-scheme: light dark;\n --bg: #ffffff; --fg: #1a1a2e; --muted: #6b7280; --line: #e5e7eb;\n --ok: #047857; --ok-bg: #ecfdf5; --err: #b91c1c; --err-bg: #fef2f2;\n --chip: #eef2ff; --mono: ui-monospace, SFMono-Regular, Menlo, monospace;\n }\n @media (prefers-color-scheme: dark) {\n :root {\n --bg: #111318; --fg: #e5e7eb; --muted: #9ca3af; --line: #2a2f3a;\n --ok: #34d399; --ok-bg: #06281f; --err: #f87171; --err-bg: #2d1215;\n --chip: #1e2433;\n }\n }\n html, body { margin: 0; padding: 0; background: var(--bg); color: var(--fg);\n font: 14px/1.5 system-ui, sans-serif; }\n main { padding: 16px; max-width: 720px; }\n h1 { font-size: 15px; margin: 0 0 12px; display: flex; gap: 8px; align-items: center; }\n .chip { font-size: 12px; padding: 2px 10px; border-radius: 999px; background: var(--chip); }\n .chip.ok { color: var(--ok); background: var(--ok-bg); }\n .chip.err { color: var(--err); background: var(--err-bg); }\n section { margin: 14px 0; }\n section > h2 { font-size: 12px; text-transform: uppercase; letter-spacing: .06em;\n color: var(--muted); margin: 0 0 6px; }\n pre { margin: 0; padding: 10px; background: var(--chip); border-radius: 8px;\n font-family: var(--mono); font-size: 12px; overflow-x: auto; white-space: pre-wrap; }\n table { border-collapse: collapse; width: 100%; font-size: 13px; }\n th, td { text-align: left; padding: 6px 8px; border-bottom: 1px solid var(--line);\n vertical-align: top; }\n th { color: var(--muted); font-weight: 500; }\n td.args { font-family: var(--mono); font-size: 12px; }\n .log { font-family: var(--mono); font-size: 12px; padding: 2px 0; }\n .log .level { color: var(--muted); margin-right: 6px; }\n .hint { color: var(--muted); font-size: 13px; margin-top: 6px; }\n .hidden { display: none; }\n #waiting { color: var(--muted); }\n</style>\n</head>\n<body>\n<main>\n <h1>Projection workflow <span id=\"status\" class=\"chip\">waiting…</span></h1>\n <p id=\"waiting\">Waiting for the workflow result from the host…</p>\n <section id=\"output-section\" class=\"hidden\"><h2>Output</h2><pre id=\"output\"></pre></section>\n <section id=\"error-section\" class=\"hidden\"><h2>Error</h2>\n <pre id=\"error\"></pre><p id=\"error-hint\" class=\"hint\"></p></section>\n <section id=\"approvals-section\" class=\"hidden\"><h2>Approval audit trail</h2>\n <table><thead><tr><th>Tool</th><th>Decision</th><th>Arguments</th></tr></thead>\n <tbody id=\"approvals\"></tbody></table></section>\n <section id=\"logs-section\" class=\"hidden\"><h2>Console</h2><div id=\"logs\"></div></section>\n</main>\n<script>\n'use strict';\n(function () {\n var nextId = 1;\n var pending = new Map();\n\n function post(message) { window.parent.postMessage(message, '*'); }\n function request(method, params) {\n return new Promise(function (resolve, reject) {\n var id = nextId++;\n pending.set(id, { resolve: resolve, reject: reject });\n post({ jsonrpc: '2.0', id: id, method: method, params: params });\n });\n }\n function notify(method, params) {\n var message = { jsonrpc: '2.0', method: method };\n if (params !== undefined) message.params = params;\n post(message);\n }\n\n function byId(id) { return document.getElementById(id); }\n function show(id) { byId(id).classList.remove('hidden'); }\n function setStatus(text, cls) {\n var chip = byId('status');\n chip.textContent = text;\n chip.className = 'chip' + (cls ? ' ' + cls : '');\n byId('waiting').classList.add('hidden');\n }\n function pretty(value) {\n try { return JSON.stringify(value, null, 2); } catch (e) { return String(value); }\n }\n\n // All model-controlled data enters the DOM via textContent only.\n function renderResult(result) {\n var structured = (result && result.structuredContent) || {};\n if (result && result.isError) {\n setStatus('failed: ' + String(structured.phase || 'error'), 'err');\n byId('error').textContent =\n String(structured.message || 'unknown error');\n byId('error-hint').textContent = String(structured.hint || '');\n show('error-section');\n } else {\n setStatus('completed', 'ok');\n byId('output').textContent = pretty(\n structured.output === undefined ? null : structured.output,\n );\n show('output-section');\n }\n var approvals = Array.isArray(structured.approvals) ? structured.approvals : [];\n if (approvals.length > 0) {\n var tbody = byId('approvals');\n approvals.forEach(function (entry) {\n var row = document.createElement('tr');\n var tool = document.createElement('td');\n tool.textContent = String(entry.tool || '');\n var decision = document.createElement('td');\n decision.textContent = String(entry.decision || '');\n var args = document.createElement('td');\n args.className = 'args';\n args.textContent = pretty(entry.args || {});\n row.appendChild(tool); row.appendChild(decision); row.appendChild(args);\n tbody.appendChild(row);\n });\n show('approvals-section');\n }\n var logs = Array.isArray(structured.logs) ? structured.logs : [];\n if (logs.length > 0) {\n var list = byId('logs');\n logs.forEach(function (entry) {\n var line = document.createElement('div');\n line.className = 'log';\n var level = document.createElement('span');\n level.className = 'level';\n level.textContent = '[' + String(entry.level || 'log') + ']';\n line.appendChild(level);\n line.appendChild(document.createTextNode(String(entry.message || '')));\n list.appendChild(line);\n });\n show('logs-section');\n }\n }\n\n window.addEventListener('message', function (event) {\n // Only the embedding host may speak: forged tool-results from a hostile\n // embedder could otherwise spoof the displayed audit trail.\n if (event.source !== window.parent) return;\n var message = event.data;\n if (!message || message.jsonrpc !== '2.0') return;\n if (message.id !== undefined && message.method === undefined) {\n var waiter = pending.get(message.id);\n if (waiter) {\n pending.delete(message.id);\n if (message.error) waiter.reject(message.error);\n else waiter.resolve(message.result);\n }\n return;\n }\n if (message.method === 'ui/notifications/tool-input') {\n setStatus('running…');\n return;\n }\n if (message.method === 'ui/notifications/tool-result') {\n renderResult(message.params);\n return;\n }\n if (message.method === 'ui/notifications/tool-cancelled') {\n var reason = message.params && message.params.reason;\n setStatus('cancelled', 'err');\n byId('error').textContent = String(reason || 'cancelled by the host');\n show('error-section');\n return;\n }\n // Host -> app REQUESTS (id + method) get an empty result: answers ping\n // and ui/resource-teardown so hosts don't stall on their SHOULD-wait.\n if (message.id !== undefined && message.method !== undefined) {\n post({ jsonrpc: '2.0', id: message.id, result: {} });\n }\n });\n\n // Handshake: initialize, then signal readiness. The host MUST NOT push\n // tool data before ui/notifications/initialized.\n request('ui/initialize', { appCapabilities: {} }).then(function () {\n notify('ui/notifications/initialized');\n });\n})();\n</script>\n</body>\n</html>\n";
|
|
1052
|
-
//#endregion
|
|
1053
|
-
//#region src/prompts/workflow-prompts.d.ts
|
|
1054
|
-
interface WorkflowPromptDefinition {
|
|
2085
|
+
export declare function wrapMcpServer(source: McpServer | Client | UpstreamSpec, options: WrapOptions): Promise<ProjectionFacade>;
|
|
2086
|
+
/** Separator between a box alias and its tool in a mapped name (`fs__read_file`). */
|
|
2087
|
+
export declare const DIRECT_BOX_SEPARATOR = "__";
|
|
2088
|
+
/** One mediated tool, as listed. */
|
|
2089
|
+
interface DirectTool {
|
|
2090
|
+
/** The facade tool name, `[A-Za-z0-9_-]{1,64}`. */
|
|
1055
2091
|
name: string;
|
|
1056
|
-
|
|
2092
|
+
/** The catalog name `call_tool` is called with (qualified on a multi-server facade). */
|
|
2093
|
+
target: string;
|
|
2094
|
+
/** The box the tool lives in (multi-server facade only). */
|
|
2095
|
+
alias?: string;
|
|
2096
|
+
title?: string;
|
|
1057
2097
|
description: string;
|
|
1058
|
-
/**
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
2098
|
+
/** The R34-sanitized input schema, always `type: "object"` at the root. */
|
|
2099
|
+
inputSchema: Record<string, unknown>;
|
|
2100
|
+
/** What the listing advertises: Projection's own, see {@link advertisedAnnotations}. */
|
|
2101
|
+
annotations: ToolAnnotations;
|
|
2102
|
+
posture: ToolPosture;
|
|
2103
|
+
}
|
|
2104
|
+
/** What {@link planDirectTools} needs besides the catalog. */
|
|
2105
|
+
interface DirectPlanOptions extends PolicyOptions {
|
|
2106
|
+
/** Names the facade already serves (Projection's own tools): never shadowed. */
|
|
2107
|
+
reserved: ReadonlySet<string>;
|
|
1064
2108
|
}
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
2109
|
+
/**
|
|
2110
|
+
* The mediated tools of a catalog, in catalog order. On a multi-server view
|
|
2111
|
+
* each is named `<alias>__<tool>`; on a single-server facade, the tool's own
|
|
2112
|
+
* name when it is legal and free. A name that is not legal is mapped
|
|
2113
|
+
* (illegal characters become `_`, an over-long name is cut and hashed); a
|
|
2114
|
+
* name that collides with a reserved one or an earlier mapped one gets
|
|
2115
|
+
* `_<8 hex of the catalog name>`. A tool whose name still collides is not
|
|
2116
|
+
* listed — it stays reachable through `call_tool`.
|
|
2117
|
+
*/
|
|
2118
|
+
export declare function planDirectTools(catalog: ServerCatalog, options: DirectPlanOptions): DirectTool[];
|
|
2119
|
+
/** Map `raw` onto the host name rule, deterministically. */
|
|
2120
|
+
export declare function directToolName(raw: string): string;
|
|
2121
|
+
/**
|
|
2122
|
+
* Estimated tokens of the listed definitions: name + title + description +
|
|
2123
|
+
* input schema JSON, at {@link CHARS_PER_TOKEN} characters per token.
|
|
2124
|
+
*/
|
|
2125
|
+
export declare function estimateDirectTokens(tools: readonly DirectTool[]): number;
|
|
1070
2126
|
/** A wrapped-server source: an in-process instance or a connected client. */
|
|
1071
2127
|
type ServerSource = McpServer | Client;
|
|
1072
2128
|
/**
|
|
1073
|
-
* Aliased server sources (R30)
|
|
1074
|
-
*
|
|
1075
|
-
*
|
|
2129
|
+
* Aliased server sources (R30): one per alias. An {@link UpstreamConnector}
|
|
2130
|
+
* is a source given by URL (R51), validated by the facade: the introspector
|
|
2131
|
+
* opens it, owns the resulting client, and records its `url:` identity
|
|
2132
|
+
* (R60).
|
|
1076
2133
|
*/
|
|
1077
|
-
type IntrospectorSources = Record<ServerAlias, ServerSource>;
|
|
2134
|
+
type IntrospectorSources = Record<ServerAlias, ServerSource | UpstreamConnector>;
|
|
2135
|
+
/** How {@link Introspector.create} connects. */
|
|
2136
|
+
interface IntrospectOptions {
|
|
2137
|
+
/**
|
|
2138
|
+
* Bound on each source's connect and introspection (R47). A source that
|
|
2139
|
+
* has not finished in time fails the whole introspection with a
|
|
2140
|
+
* `protocol` error. Default {@link DEFAULT_INTROSPECT_TIMEOUT_MS}.
|
|
2141
|
+
*/
|
|
2142
|
+
timeoutMs?: number;
|
|
2143
|
+
/** How the timeout error names a source. Default: `wrapped server "<alias>"`. */
|
|
2144
|
+
describe?: (alias: ServerAlias) => string;
|
|
2145
|
+
/**
|
|
2146
|
+
* The aliases whose boxes forward their server's questions to the human
|
|
2147
|
+
* (`upstreamElicitation: 'forward'`, G1). Their owned clients declare the
|
|
2148
|
+
* elicitation capability; every other box declares none.
|
|
2149
|
+
*/
|
|
2150
|
+
forwardElicitation?: ReadonlySet<ServerAlias>;
|
|
2151
|
+
}
|
|
1078
2152
|
/**
|
|
1079
2153
|
* Holds one MCP client per wrapped-server alias, exposes the aggregated
|
|
1080
2154
|
* {@link ToolCatalog}, and executes tool calls (implements
|
|
1081
2155
|
* {@link ToolExecutor}). Construct with {@link createIntrospector}.
|
|
1082
2156
|
*/
|
|
1083
|
-
declare class Introspector implements ToolExecutor {
|
|
2157
|
+
export declare class Introspector implements ToolExecutor {
|
|
1084
2158
|
#private;
|
|
1085
2159
|
private constructor();
|
|
1086
|
-
static create(sources: IntrospectorSources): Promise<Introspector>;
|
|
2160
|
+
static create(sources: IntrospectorSources, options?: IntrospectOptions): Promise<Introspector>;
|
|
1087
2161
|
/** The aggregated tool catalog, keyed by server alias (R30). */
|
|
1088
2162
|
get catalog(): ToolCatalog;
|
|
1089
2163
|
/** Re-read `tools/list` from every wrapped server and rebuild the catalog. */
|
|
@@ -1098,8 +2172,12 @@ declare class Introspector implements ToolExecutor {
|
|
|
1098
2172
|
* - `isError` result or transport/protocol failure → `{ ok: false, error }`.
|
|
1099
2173
|
*
|
|
1100
2174
|
* An unknown alias is a wiring bug, not a tool failure, and throws.
|
|
2175
|
+
*
|
|
2176
|
+
* `options` are host-side: `signal` cancels the upstream request, and
|
|
2177
|
+
* `onProgress` asks the upstream for progress and receives each report
|
|
2178
|
+
* (redacted first for a source given by URL, S1).
|
|
1101
2179
|
*/
|
|
1102
|
-
callTool(alias: ServerAlias, name: string, args: Record<string, unknown
|
|
2180
|
+
callTool(alias: ServerAlias, name: string, args: Record<string, unknown>, options?: UpstreamCallOptions): Promise<ToolCallOutcome>;
|
|
1103
2181
|
/**
|
|
1104
2182
|
* Close connections the introspector created (in-process sources).
|
|
1105
2183
|
* Caller-provided remote clients are left untouched.
|
|
@@ -1111,128 +2189,133 @@ declare class Introspector implements ToolExecutor {
|
|
|
1111
2189
|
*
|
|
1112
2190
|
* v1 usage: `createIntrospector({ [DEFAULT_SERVER_ALIAS]: serverOrClient })`.
|
|
1113
2191
|
*/
|
|
1114
|
-
declare function createIntrospector(sources: IntrospectorSources): Promise<Introspector>;
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
/**
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
type ResolvedCallPolicy = {
|
|
1158
|
-
ok: true;
|
|
1159
|
-
decision: PolicyDecision;
|
|
1160
|
-
entry: ToolCatalogEntry;
|
|
1161
|
-
} | {
|
|
1162
|
-
ok: false;
|
|
1163
|
-
error: ProjectionError;
|
|
1164
|
-
};
|
|
1165
|
-
/**
|
|
1166
|
-
* Resolve a machine-requested tool call against the server catalog and
|
|
1167
|
-
* decide its policy.
|
|
1168
|
-
*
|
|
1169
|
-
* Returns the decision together with the matching catalog entry (the entry
|
|
1170
|
-
* is what elicitation copy must be built from — S6), or a structured
|
|
1171
|
-
* `validate`-phase {@link ProjectionError} when the machine names a tool the
|
|
1172
|
-
* wrapped server does not declare.
|
|
1173
|
-
*/
|
|
1174
|
-
declare function resolveCallPolicy(call: PendingToolCall, catalog: ServerCatalog, overrides?: PolicyOverrides): ResolvedCallPolicy;
|
|
1175
|
-
/**
|
|
1176
|
-
* Canonical TOOL_DENIED reason for calls blocked by a `deny` policy
|
|
1177
|
-
* decision. A `deny` decision never produces an elicitation — the machine
|
|
1178
|
-
* host injects `TOOL_DENIED` with this reason instead.
|
|
1179
|
-
*/
|
|
1180
|
-
declare const POLICY_DENIED_REASON = "denied by server policy";
|
|
2192
|
+
export declare function createIntrospector(sources: IntrospectorSources, options?: IntrospectOptions): Promise<Introspector>;
|
|
2193
|
+
/** One source with its per-box settings (R39). */
|
|
2194
|
+
interface ServerSourceConfig {
|
|
2195
|
+
/**
|
|
2196
|
+
* The server to wrap: an in-process `McpServer`, a connected `Client`, or
|
|
2197
|
+
* a remote server given by URL (`{ url, oauth? }`, R51).
|
|
2198
|
+
*/
|
|
2199
|
+
source: McpServer | Client | UpstreamSpec;
|
|
2200
|
+
/**
|
|
2201
|
+
* Host-trusted text naming the box's purpose, shown to the model on the
|
|
2202
|
+
* shelf (R44). Neutralized (control and bidi characters) and capped at 200
|
|
2203
|
+
* characters.
|
|
2204
|
+
*/
|
|
2205
|
+
description?: string;
|
|
2206
|
+
/**
|
|
2207
|
+
* Approvals mode for this box (R49); takes precedence over
|
|
2208
|
+
* `WrapServersOptions.approvals`. A per-tool `policy` override still wins.
|
|
2209
|
+
*/
|
|
2210
|
+
approvals?: ApprovalsMode;
|
|
2211
|
+
/**
|
|
2212
|
+
* Whether this box's server may ask the human questions (G1, DECISIONS
|
|
2213
|
+
* D24); takes precedence over `WrapServersOptions.upstreamElicitation`.
|
|
2214
|
+
* Default `'refuse'`.
|
|
2215
|
+
*/
|
|
2216
|
+
upstreamElicitation?: UpstreamElicitationMode;
|
|
2217
|
+
/**
|
|
2218
|
+
* R47 (skip this source instead of failing the wrap) is not supported yet:
|
|
2219
|
+
* `true` fails the wrap with a `validate` error. Wrapping is fail-closed.
|
|
2220
|
+
*/
|
|
2221
|
+
optional?: boolean;
|
|
2222
|
+
}
|
|
2223
|
+
/** Options of a multi-server facade (R39). */
|
|
2224
|
+
interface WrapServersOptions extends WrapOptions {
|
|
2225
|
+
catalog?: {
|
|
2226
|
+
/**
|
|
2227
|
+
* Serve the global `search_tools` across every box next to the per-box
|
|
2228
|
+
* `search_<alias>` tools (R43). Default `true`; `false` leaves only the
|
|
2229
|
+
* per-box tools, and every piece of copy that names a search tool names
|
|
2230
|
+
* those instead.
|
|
2231
|
+
*/
|
|
2232
|
+
globalSearch?: boolean;
|
|
2233
|
+
};
|
|
2234
|
+
}
|
|
1181
2235
|
/**
|
|
1182
|
-
*
|
|
1183
|
-
*
|
|
1184
|
-
*
|
|
2236
|
+
* A multi-server facade (R39): every {@link ProjectionFacade} member, with
|
|
2237
|
+
* `catalog` the alias-keyed catalog of the wrapped boxes (bare names),
|
|
2238
|
+
* `skipped` the optional sources that were skipped (R47), `searchTools`
|
|
2239
|
+
* taking an optional alias, and `describeTool` / `callTool` taking
|
|
2240
|
+
* qualified names.
|
|
1185
2241
|
*/
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
/** Structured approval copy for one gated call (input to the MRTR adapter). */
|
|
1190
|
-
interface ElicitationCopy {
|
|
1191
|
-
/** Short action title, e.g. `Approve: delete file`. */
|
|
1192
|
-
title: string;
|
|
1193
|
-
/** Full human-readable summary (plain text with an indented JSON block). */
|
|
1194
|
-
message: string;
|
|
1195
|
-
/** Tool name, taken from the catalog entry. */
|
|
1196
|
-
toolName: string;
|
|
1197
|
-
/** Machine state path that requested the call (raw; render with care). */
|
|
1198
|
-
stateName: string;
|
|
1199
|
-
/** The redacted arguments as rendered in `message` (JSON text, possibly truncated). */
|
|
1200
|
-
args: string;
|
|
1201
|
-
/** True when the catalog entry carries `destructiveHint: true`. */
|
|
1202
|
-
destructive: boolean;
|
|
1203
|
-
}
|
|
1204
|
-
interface ElicitationCopyOptions {
|
|
1205
|
-
/** Redaction hook applied to args before any rendering (R16). */
|
|
1206
|
-
redact?: RedactionHook;
|
|
2242
|
+
interface MultiProjectionFacade extends Omit<ProjectionFacade, 'catalog' | 'searchTools'> {
|
|
2243
|
+
/** The wrapped boxes' catalogs, keyed by alias, with the servers' own tool names (R30). */
|
|
2244
|
+
readonly catalog: ToolCatalog;
|
|
1207
2245
|
/**
|
|
1208
|
-
*
|
|
1209
|
-
*
|
|
2246
|
+
* Optional sources skipped at wrap time (R47). Always empty for now:
|
|
2247
|
+
* optional sources are not supported yet, so every source is wrapped or
|
|
2248
|
+
* the wrap fails.
|
|
1210
2249
|
*/
|
|
1211
|
-
|
|
2250
|
+
readonly skipped: readonly {
|
|
2251
|
+
alias: string;
|
|
2252
|
+
error: StructuredError;
|
|
2253
|
+
}[];
|
|
1212
2254
|
/**
|
|
1213
|
-
*
|
|
1214
|
-
*
|
|
2255
|
+
* Run `search_<alias>` when `alias` is given, `search_tools` otherwise
|
|
2256
|
+
* (R43). A search tool the facade does not serve — an unknown alias, or
|
|
2257
|
+
* `search_tools` under `catalog.globalSearch: false` — comes back as an
|
|
2258
|
+
* R25 `validate` error result.
|
|
1215
2259
|
*/
|
|
1216
|
-
|
|
2260
|
+
searchTools(args: {
|
|
2261
|
+
query?: string;
|
|
2262
|
+
cursor?: string;
|
|
2263
|
+
}, alias?: string): CallToolResult;
|
|
1217
2264
|
}
|
|
1218
2265
|
/**
|
|
1219
|
-
*
|
|
1220
|
-
*
|
|
1221
|
-
*
|
|
1222
|
-
*
|
|
1223
|
-
*
|
|
2266
|
+
* Wrap several MCP servers (in-process instances, connected clients, or
|
|
2267
|
+
* `{ source, … }` configurations) into one Projection facade, each under
|
|
2268
|
+
* its alias (R39). Configuration mistakes are `validate` errors raised
|
|
2269
|
+
* before anything connects; a source that fails to connect or introspect
|
|
2270
|
+
* fails the wrap and closes every connection Projection opened (R47).
|
|
1224
2271
|
*/
|
|
1225
|
-
declare function
|
|
2272
|
+
export declare function wrapMcpServers(sources: Record<string, McpServer | Client | UpstreamSpec | ServerSourceConfig>, options: WrapServersOptions): Promise<MultiProjectionFacade>;
|
|
1226
2273
|
/**
|
|
1227
|
-
*
|
|
1228
|
-
*
|
|
1229
|
-
*
|
|
1230
|
-
*
|
|
1231
|
-
*
|
|
2274
|
+
* MCP Apps workflow dashboard (PRD §12.1, second fenced use case).
|
|
2275
|
+
*
|
|
2276
|
+
* A static, library-authored HTML app that renders an `execute_machine`
|
|
2277
|
+
* result — output, approval audit trail, console logs, or the structured
|
|
2278
|
+
* error — inside the host's sandboxed iframe, per the MCP Apps extension
|
|
2279
|
+
* (SEP-1865, spec 2026-01-26).
|
|
2280
|
+
*
|
|
2281
|
+
* Security posture (the §12.1 design constraint, satisfied by construction):
|
|
2282
|
+
* - The HTML is a compile-time constant. No model-authored code, no
|
|
2283
|
+
* template interpolation, no VM: UI rendering can never reach the tools
|
|
2284
|
+
* bridge because nothing dynamic executes server-side at all.
|
|
2285
|
+
* - Model-controlled data (outputs, audit args, log lines, error text)
|
|
2286
|
+
* enters the DOM exclusively through `textContent` — the app never uses
|
|
2287
|
+
* innerHTML — so hostile strings render inert (S6 extended to the app).
|
|
2288
|
+
* - The app requests no host capabilities: `appCapabilities: {}`. It cannot
|
|
2289
|
+
* call tools, open links, or write context; it only listens.
|
|
2290
|
+
*
|
|
2291
|
+
* Wire contract implemented (app side):
|
|
2292
|
+
* - `ui/initialize` request → await result → `ui/notifications/initialized`.
|
|
2293
|
+
* - Listens for `ui/notifications/tool-input`, `ui/notifications/tool-result`
|
|
2294
|
+
* and `ui/notifications/tool-cancelled`; ignores everything else.
|
|
2295
|
+
* - The result payload is the standard `CallToolResult` from
|
|
2296
|
+
* `execute_machine`: `structuredContent` is either
|
|
2297
|
+
* `{ output, approvals, logs }` or (with `isError`) the structured error
|
|
2298
|
+
* `{ phase, message, hint, logs }`.
|
|
1232
2299
|
*/
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
2300
|
+
/** URI the dashboard is served under. MCP Apps mandates the ui:// scheme. */
|
|
2301
|
+
export declare const DASHBOARD_RESOURCE_URI = "ui://projection/dashboard.html";
|
|
2302
|
+
/** MIME type the MCP Apps spec mandates for app HTML resources. */
|
|
2303
|
+
export declare const MCP_APP_MIME_TYPE = "text/html;profile=mcp-app";
|
|
2304
|
+
export declare const DASHBOARD_HTML = "<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\" />\n<title>Projection workflow dashboard</title>\n<style>\n :root {\n color-scheme: light dark;\n --bg: #ffffff; --fg: #1a1a2e; --muted: #6b7280; --line: #e5e7eb;\n --ok: #047857; --ok-bg: #ecfdf5; --err: #b91c1c; --err-bg: #fef2f2;\n --chip: #eef2ff; --mono: ui-monospace, SFMono-Regular, Menlo, monospace;\n }\n @media (prefers-color-scheme: dark) {\n :root {\n --bg: #111318; --fg: #e5e7eb; --muted: #9ca3af; --line: #2a2f3a;\n --ok: #34d399; --ok-bg: #06281f; --err: #f87171; --err-bg: #2d1215;\n --chip: #1e2433;\n }\n }\n html, body { margin: 0; padding: 0; background: var(--bg); color: var(--fg);\n font: 14px/1.5 system-ui, sans-serif; }\n main { padding: 16px; max-width: 720px; }\n h1 { font-size: 15px; margin: 0 0 12px; display: flex; gap: 8px; align-items: center; }\n .chip { font-size: 12px; padding: 2px 10px; border-radius: 999px; background: var(--chip); }\n .chip.ok { color: var(--ok); background: var(--ok-bg); }\n .chip.err { color: var(--err); background: var(--err-bg); }\n section { margin: 14px 0; }\n section > h2 { font-size: 12px; text-transform: uppercase; letter-spacing: .06em;\n color: var(--muted); margin: 0 0 6px; }\n pre { margin: 0; padding: 10px; background: var(--chip); border-radius: 8px;\n font-family: var(--mono); font-size: 12px; overflow-x: auto; white-space: pre-wrap; }\n table { border-collapse: collapse; width: 100%; font-size: 13px; }\n th, td { text-align: left; padding: 6px 8px; border-bottom: 1px solid var(--line);\n vertical-align: top; }\n th { color: var(--muted); font-weight: 500; }\n td.args { font-family: var(--mono); font-size: 12px; }\n .log { font-family: var(--mono); font-size: 12px; padding: 2px 0; }\n .log .level { color: var(--muted); margin-right: 6px; }\n .hint { color: var(--muted); font-size: 13px; margin-top: 6px; }\n .hidden { display: none; }\n #waiting { color: var(--muted); }\n</style>\n</head>\n<body>\n<main>\n <h1>Projection workflow <span id=\"status\" class=\"chip\">waiting…</span></h1>\n <p id=\"waiting\">Waiting for the workflow result from the host…</p>\n <section id=\"output-section\" class=\"hidden\"><h2>Output</h2><pre id=\"output\"></pre></section>\n <section id=\"error-section\" class=\"hidden\"><h2>Error</h2>\n <pre id=\"error\"></pre><p id=\"error-hint\" class=\"hint\"></p></section>\n <section id=\"approvals-section\" class=\"hidden\"><h2>Approval audit trail</h2>\n <table><thead><tr><th>Tool</th><th>Decision</th><th>Arguments</th></tr></thead>\n <tbody id=\"approvals\"></tbody></table></section>\n <section id=\"logs-section\" class=\"hidden\"><h2>Console</h2><div id=\"logs\"></div></section>\n</main>\n<script>\n'use strict';\n(function () {\n var nextId = 1;\n var pending = new Map();\n\n function post(message) { window.parent.postMessage(message, '*'); }\n function request(method, params) {\n return new Promise(function (resolve, reject) {\n var id = nextId++;\n pending.set(id, { resolve: resolve, reject: reject });\n post({ jsonrpc: '2.0', id: id, method: method, params: params });\n });\n }\n function notify(method, params) {\n var message = { jsonrpc: '2.0', method: method };\n if (params !== undefined) message.params = params;\n post(message);\n }\n\n function byId(id) { return document.getElementById(id); }\n function show(id) { byId(id).classList.remove('hidden'); }\n function setStatus(text, cls) {\n var chip = byId('status');\n chip.textContent = text;\n chip.className = 'chip' + (cls ? ' ' + cls : '');\n byId('waiting').classList.add('hidden');\n }\n function pretty(value) {\n try { return JSON.stringify(value, null, 2); } catch (e) { return String(value); }\n }\n\n // All model-controlled data enters the DOM via textContent only.\n function renderResult(result) {\n var structured = (result && result.structuredContent) || {};\n if (result && result.isError) {\n setStatus('failed: ' + String(structured.phase || 'error'), 'err');\n byId('error').textContent =\n String(structured.message || 'unknown error');\n byId('error-hint').textContent = String(structured.hint || '');\n show('error-section');\n } else {\n setStatus('completed', 'ok');\n byId('output').textContent = pretty(\n structured.output === undefined ? null : structured.output,\n );\n show('output-section');\n }\n var approvals = Array.isArray(structured.approvals) ? structured.approvals : [];\n if (approvals.length > 0) {\n var tbody = byId('approvals');\n approvals.forEach(function (entry) {\n var row = document.createElement('tr');\n var tool = document.createElement('td');\n tool.textContent = String(entry.tool || '');\n var decision = document.createElement('td');\n decision.textContent = String(entry.decision || '');\n var args = document.createElement('td');\n args.className = 'args';\n args.textContent = pretty(entry.args || {});\n row.appendChild(tool); row.appendChild(decision); row.appendChild(args);\n tbody.appendChild(row);\n });\n show('approvals-section');\n }\n var logs = Array.isArray(structured.logs) ? structured.logs : [];\n if (logs.length > 0) {\n var list = byId('logs');\n logs.forEach(function (entry) {\n var line = document.createElement('div');\n line.className = 'log';\n var level = document.createElement('span');\n level.className = 'level';\n level.textContent = '[' + String(entry.level || 'log') + ']';\n line.appendChild(level);\n line.appendChild(document.createTextNode(String(entry.message || '')));\n list.appendChild(line);\n });\n show('logs-section');\n }\n }\n\n window.addEventListener('message', function (event) {\n // Only the embedding host may speak: forged tool-results from a hostile\n // embedder could otherwise spoof the displayed audit trail.\n if (event.source !== window.parent) return;\n var message = event.data;\n if (!message || message.jsonrpc !== '2.0') return;\n if (message.id !== undefined && message.method === undefined) {\n var waiter = pending.get(message.id);\n if (waiter) {\n pending.delete(message.id);\n if (message.error) waiter.reject(message.error);\n else waiter.resolve(message.result);\n }\n return;\n }\n if (message.method === 'ui/notifications/tool-input') {\n setStatus('running…');\n return;\n }\n if (message.method === 'ui/notifications/tool-result') {\n renderResult(message.params);\n return;\n }\n if (message.method === 'ui/notifications/tool-cancelled') {\n var reason = message.params && message.params.reason;\n setStatus('cancelled', 'err');\n byId('error').textContent = String(reason || 'cancelled by the host');\n show('error-section');\n return;\n }\n // Host -> app REQUESTS (id + method) get an empty result: answers ping\n // and ui/resource-teardown so hosts don't stall on their SHOULD-wait.\n if (message.id !== undefined && message.method !== undefined) {\n post({ jsonrpc: '2.0', id: message.id, result: {} });\n }\n });\n\n // Handshake: initialize, then signal readiness. The host MUST NOT push\n // tool data before ui/notifications/initialized.\n request('ui/initialize', { appCapabilities: {} }).then(function () {\n notify('ui/notifications/initialized');\n });\n})();\n</script>\n</body>\n</html>\n";
|
|
2305
|
+
interface WorkflowPromptDefinition {
|
|
2306
|
+
name: string;
|
|
2307
|
+
title: string;
|
|
2308
|
+
description: string;
|
|
2309
|
+
/** Argument names → { description, required }. All MCP prompt args are strings. */
|
|
2310
|
+
args: Record<string, {
|
|
2311
|
+
description: string;
|
|
2312
|
+
required: boolean;
|
|
2313
|
+
}>;
|
|
2314
|
+
build(args: Record<string, string | undefined>, catalog: ServerCatalog): string;
|
|
2315
|
+
}
|
|
2316
|
+
export declare const SAVE_WORKFLOW_PROMPT: WorkflowPromptDefinition;
|
|
2317
|
+
export declare const VISUALIZE_WORKFLOW_PROMPT: WorkflowPromptDefinition;
|
|
2318
|
+
export declare const WORKFLOW_PROMPTS: readonly WorkflowPromptDefinition[];
|
|
1236
2319
|
/** A tool call surfaced from the `@astral-mcp/tools` bridge inside the VM. */
|
|
1237
2320
|
interface SandboxToolCall {
|
|
1238
2321
|
name: string;
|
|
@@ -1293,64 +2376,6 @@ interface SandboxSession {
|
|
|
1293
2376
|
* bridge, evaluate the authored module, and return a handle for driving it.
|
|
1294
2377
|
* See ./index.ts for the full boundary contract.
|
|
1295
2378
|
*/
|
|
1296
|
-
declare function createSandboxSession(options: SandboxSessionOptions): Promise<SandboxSession>;
|
|
1297
|
-
|
|
1298
|
-
//#region src/machine-host/host.d.ts
|
|
1299
|
-
interface RunSegmentOptions {
|
|
1300
|
-
/** Submitted module source (R2). */
|
|
1301
|
-
code: string;
|
|
1302
|
-
lang?: 'ts' | 'js';
|
|
1303
|
-
/** Machine input, passed as the machine's `input` on a fresh run. */
|
|
1304
|
-
input?: unknown;
|
|
1305
|
-
/** Catalog of the wrapped server whose tools the machine may call. */
|
|
1306
|
-
catalog: ServerCatalog;
|
|
1307
|
-
/** Generated `@astral-mcp/tools` bridge source (from typegen). */
|
|
1308
|
-
bridgeSource: string;
|
|
1309
|
-
/** Executes approved/auto tool calls against the wrapped server. */
|
|
1310
|
-
executor: ToolExecutor;
|
|
1311
|
-
/** Resource caps; clamped at the hard maxima (R12). */
|
|
1312
|
-
caps?: Partial<ResourceCaps>;
|
|
1313
|
-
/** Per-tool policy overrides (R14). */
|
|
1314
|
-
policy?: PolicyOverrides;
|
|
1315
|
-
/** Zero-based MRTR segment index — stamps logs (R31). */
|
|
1316
|
-
segment?: number;
|
|
1317
|
-
/**
|
|
1318
|
-
* Resume state. Present on every segment after the first: the verified
|
|
1319
|
-
* snapshot and the pending calls sealed alongside it, plus the verdicts
|
|
1320
|
-
* the client returned for those calls.
|
|
1321
|
-
*/
|
|
1322
|
-
resume?: {
|
|
1323
|
-
snapshot: unknown;
|
|
1324
|
-
/** Gated calls as sealed into the blob — the approved name+args (S2). */
|
|
1325
|
-
pending: Record<string, PendingToolCall>;
|
|
1326
|
-
/** Verdict per pending key; a missing entry counts as declined (R17). */
|
|
1327
|
-
verdicts: Record<string, ApprovalVerdict>;
|
|
1328
|
-
/** Audit entries accumulated in earlier segments. */
|
|
1329
|
-
audit?: AuditEntry[];
|
|
1330
|
-
/** Round-trip counter carried in the blob (R18). */
|
|
1331
|
-
trip: number;
|
|
1332
|
-
};
|
|
1333
|
-
}
|
|
1334
|
-
/**
|
|
1335
|
-
* Run one execution segment to completion, suspension, or error.
|
|
1336
|
-
*
|
|
1337
|
-
* A segment is one MRTR round trip: fresh sandbox, fresh wall-clock budget,
|
|
1338
|
-
* fresh logs. On the first segment the module is validated (R24) before the
|
|
1339
|
-
* actor starts; on a resume the module is re-evaluated from the re-sent code
|
|
1340
|
-
* and the actor is restored from the sealed snapshot.
|
|
1341
|
-
*
|
|
1342
|
-
* Note: `runSegment` interpolates `input` into driver source without a size
|
|
1343
|
-
* check of its own — `executeMachine` enforces `maxInputBytes` before calling
|
|
1344
|
-
* it. Callers invoking `runSegment` directly must bound `input` themselves.
|
|
1345
|
-
*/
|
|
1346
|
-
declare function runSegment(options: RunSegmentOptions): Promise<SegmentOutcome>;
|
|
1347
|
-
//#endregion
|
|
1348
|
-
//#region src/machine-host/contract.d.ts
|
|
1349
|
-
/**
|
|
1350
|
-
* Human-readable authoring contract. `discover_apis` publishes this inline
|
|
1351
|
-
* (R3) so a model can author a valid machine without fetching resources.
|
|
1352
|
-
*/
|
|
1353
|
-
declare const AUTHORING_CONTRACT = "Projection authoring contract (execute_machine)\n\nWrite ONE module whose only top-level statements are imports and a single\ndefault-exported XState v5 machine. The module is re-evaluated on every\napproval round trip, so top-level side effects are rejected.\n\n import { setup } from 'xstate';\n import { toolCall } from '@astral-mcp/tools';\n\n export default setup({}).createMachine({ /* ... */ });\n\nRules:\n1. Default-export a machine built with XState v5 setup(...).createMachine(...).\n XState v4 Machine(...) is rejected.\n2. Machine context must be JSON-serializable at all times. No Map, Set, Date,\n class instances, functions, undefined, bigint or symbols in context.\n3. Request tools with the toolCall action. NEVER wrap a tool call in\n fromPromise/invoke — restored snapshots restart invoked actors, which\n would re-execute an approved write. Machines using invoke are rejected.\n4. After a toolCall, wait in a state for one of these events:\n TOOL_RESULT { callId, result } the call succeeded\n TOOL_ERROR { callId, error } the call failed\n TOOL_DENIED { callId, reason } the user declined, or policy denies it\n Denial is not an abort: handle TOOL_DENIED and continue or finish.\n IMPORTANT: if your machine has more than one call outstanding at the same\n time (parallel regions), give each toolCall an explicit id and guard every\n waiting state with whenCall(id) — events reach every region that can\n handle them, so an unguarded region will consume another call's result:\n\n import { toolCall, whenCall } from '@astral-mcp/tools';\n entry: toolCall('write_file', () => ({ path: '/a' }), { id: 'a' }),\n on: { TOOL_RESULT: { guard: whenCall('a'), target: 'done' } },\n5. Read-only tools run immediately. Every other tool pauses the machine for\n user approval; the arguments captured at that moment are the arguments\n that execute.\n6. Only 'xstate' and '@astral-mcp/tools' resolve as imports. There is no\n filesystem, no network, no fetch. All working state lives in context.\n7. Finish by reaching a final state. Use the machine's output for the result.\n8. TOOL_RESULT.result is the tool's structuredContent when the server returns\n one (see its Output type in tools.d.ts); otherwise the text of its single\n text block, null for an empty result, or the raw content array for\n anything else. A tool that returns isError arrives as TOOL_ERROR. Tools\n without an Output type are typed unknown — usually a string.\n9. Tool inputs such as paths, ids and names are server-specific. Do not guess\n them: take them from the machine's input, or discover them first with a\n read-only call (a list/search tool) and keep what you learn in context.\n\nExample — happy path with a read-only call (run with input: { path }):\n\n import { setup, assign } from 'xstate';\n import { toolCall } from '@astral-mcp/tools';\n\n export default setup({}).createMachine({\n id: 'readOne',\n context: ({ input }) => ({ path: input.path, result: null }),\n initial: 'reading',\n states: {\n reading: {\n entry: toolCall('read_file', ({ context }) => ({ path: context.path })),\n on: {\n TOOL_RESULT: { target: 'done', actions: assign({ result: ({ event }) => event.result }) },\n TOOL_ERROR: 'failed',\n },\n },\n done: { type: 'final' },\n failed: { type: 'final' },\n },\n output: ({ context }) => ({ result: context.result }),\n });\n\nExample — handling a denied approval (run with input: { path }):\n\n import { setup, assign } from 'xstate';\n import { toolCall } from '@astral-mcp/tools';\n\n export default setup({}).createMachine({\n id: 'writeOne',\n context: ({ input }) => ({ path: input.path, status: 'pending' }),\n initial: 'writing',\n states: {\n writing: {\n entry: toolCall('write_file', ({ context }) => ({ path: context.path, content: 'hello' })),\n on: {\n TOOL_RESULT: { target: 'done', actions: assign({ status: 'written' }) },\n TOOL_DENIED: { target: 'done', actions: assign({ status: 'skipped' }) },\n TOOL_ERROR: { target: 'done', actions: assign({ status: 'failed' }) },\n },\n },\n done: { type: 'final' },\n },\n output: ({ context }) => ({ status: context.status }),\n });\n";
|
|
1354
|
-
//#endregion
|
|
1355
|
-
export { APPROVAL_SCHEMA, AUTHORING_CONTRACT, type ApprovalGateDeps, ApprovalVerdict, AuditEntry, BLOB_SCHEMA_VERSION, BlobKind, BlobPayload, BlobSealerConfig, CALL_KEY, type CallToolArgs, type CallToolDeps, type CallToolOutcome, type CallToolResult, type ContentBlock, DASHBOARD_HTML, DASHBOARD_RESOURCE_URI, DEFAULT_CALL_TTL_MS, DEFAULT_CAPS, DEFAULT_CATALOG_PREAMBLE, DEFAULT_CODE_MODE_PREAMBLE, DEFAULT_INLINE_TOOL_THRESHOLD, DEFAULT_MAX_BLOB_BYTES, DEFAULT_ROUTING_PREAMBLE, DEFAULT_SEARCH_PAGE_SIZE, DEFAULT_SERVER_ALIAS, DEFAULT_TTL_MS, type DescribeToolArgs, type DescribeToolOptions, Diagnostic, type DiscoverApisOptions, ENVELOPE_PREFIX, EXAMPLE_DENIAL_FALLBACK, EXAMPLE_HAPPY_PATH, type ElicitationCopy, type ElicitationCopyOptions, ErrorPhase, type ExecuteMachineArgs, type ExecuteMachineDeps, type GeneratedToolTypes, HostEvent, type InputRequest, type InputRequests, type InputRequiredResult, type InputResponse, type InputResponseView, type InputResponses, Introspector, LIB_VERSION, LogEntry, LogLevel, MAX_CAPS, MAX_QUERY_CHARS, MAX_SCHEMA_CHARS, MCP_APP_MIME_TYPE, MRTR_PROTOCOL_REVISION, type OpenBlobOptions, POLICY_DENIED_REASON, PendingToolCall, PolicyDecision, PolicyOverrides, ProjectionConfig, ProjectionError, type ProjectionFacade, type ProjectionResource, type ProjectionSelection, type ProjectionsOptions, type ProtocolEra, RESOURCE_URIS, RedactionHook, type RegisterOptions, ReplayCache, type ResolvedCallPolicy, ResourceCaps, type RetryEnvelope, type RunSegmentOptions, SAVE_WORKFLOW_PROMPT, SCHEMA_KEY_ALLOWLIST, type SandboxSession, type SandboxSessionOptions, type SandboxToolCall, type SandboxToolCallHandler, type SanitizedSchema, type SearchToolsArgs, type SearchToolsOptions, type SearchToolsPage, SegmentOutcome, ServerAlias, ServerCatalog, StructuredError, TOOLS_MODULE_SPECIFIER, TOOL_CALL_HOST_GLOBAL, ToolAnnotations, ToolCallOutcome, type ToolCallResult, ToolCatalog, ToolCatalogEntry, type ToolDescription, ToolExecutor, type ToolPosture, type ToolSummary, type ToolTypeText, VISUALIZE_WORKFLOW_PROMPT, WORKFLOW_PROMPTS, type WorkflowPromptDefinition, type WrapOptions, XSTATE_VERSION, acceptedContent, buildBlobPayload, buildElicitationCopy, buildInstructions, buildResources, callTool, canonicalJson, canonicalRequest, capLength, clampCaps, createIntrospector, createSandboxSession, decidePolicy, dedupeTools, defaultPreamble, describeTool, discoverApis, errorResult, executeMachine, findTool, generateToolTypes, hashCode, inputRequired, inputResponse, isInputRequiredResult, isRecord, neutralizeControlChars, openBlob, policyDeniedReason, postureOf, purposeSentence, resolveCallPolicy, runSegment, sanitizeSchema, schemaRequiresArgs, schemaToTsType, sealBlob, searchTools, summarizeTool, supportsMrtr, toStructuredError, validateArgsShape, wrapMcpServer };
|
|
2379
|
+
export declare function createSandboxSession(options: SandboxSessionOptions): Promise<SandboxSession>;
|
|
2380
|
+
export { type ApprovalGateDeps, type ApprovalPrompts, type ArgGloss, type BlobKeyDomain, type BoxedSearchPage, type BoxedToolSummary, type CallSignature, type CallToolArgs, type CallToolDeps, type CallToolOutcome, type CallToolResult, type ContentBlock, type DescribeToolArgs, type DescribeToolOptions, type DirectTool, type DiscoverApisOptions, type ElicitationCopy, type ElicitationCopyOptions, type ExecuteMachineArgs, type ExecuteMachineDeps, type GeneratedToolTypes, type InputRequest, type InputRequests, type InputRequiredResult, type InputResponse, type InputResponseView, type InputResponses, type MultiProjectionFacade, type OpenBlobOptions, type ProjectionFacade, type ProjectionResource, type ProjectionSelection, type ProjectionsOptions, type ProtocolEra, type RegisterOptions, type ResolvedCallPolicy, type RetryEnvelope, type RunSegmentOptions, type SandboxSession, type SandboxSessionOptions, type SandboxToolCall, type SandboxToolCallHandler, type SanitizedSchema, type SearchToolsArgs, type SearchToolsOptions, type SearchToolsPage, type ServerSourceConfig, type SharedArg, type TokenStore, type ToolCallResult, type ToolDescription, type ToolPosture, type ToolSummary, type ToolTypeText, type UpstreamOAuthOptions, type UpstreamSpec, type WorkflowPromptDefinition, type WrapOptions, type WrapServersOptions, acceptedContent, inputRequired, inputResponse, isInputRequiredResult };
|
|
1356
2381
|
//# sourceMappingURL=index.d.mts.map
|