ai-runtime-engine 1.2.0 → 2.7.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/CHANGELOG.md +602 -0
- package/dist/agents/admit.d.ts +69 -0
- package/dist/agents/admit.js +129 -0
- package/dist/agents/definition.d.ts +36 -0
- package/dist/agents/definition.js +9 -0
- package/dist/agents/envelope.d.ts +53 -0
- package/dist/agents/envelope.js +68 -0
- package/dist/agents/finding.d.ts +79 -0
- package/dist/agents/finding.js +80 -0
- package/dist/agents/task.d.ts +60 -0
- package/dist/agents/task.js +32 -0
- package/dist/agents/worker.d.ts +68 -0
- package/dist/agents/worker.js +256 -0
- package/dist/capabilities/capability.d.ts +117 -0
- package/dist/capabilities/capability.js +66 -0
- package/dist/capabilities/registry.d.ts +139 -0
- package/dist/capabilities/registry.js +413 -0
- package/dist/capabilities/vocabulary.d.ts +32 -0
- package/dist/capabilities/vocabulary.js +34 -0
- package/dist/cli/cli.js +56 -4
- package/dist/cli/commands/cleanup.js +29 -27
- package/dist/cli/commands/doctor.d.ts +14 -0
- package/dist/cli/commands/doctor.js +38 -8
- package/dist/cli/commands/executions.js +34 -25
- package/dist/cli/commands/info.d.ts +1 -0
- package/dist/cli/commands/info.js +11 -9
- package/dist/cli/commands/init.js +19 -0
- package/dist/cli/commands/inspect.d.ts +40 -1
- package/dist/cli/commands/inspect.js +157 -2
- package/dist/cli/commands/mcp.d.ts +45 -0
- package/dist/cli/commands/mcp.js +148 -0
- package/dist/cli/commands/route.js +21 -0
- package/dist/cli/commands/run.d.ts +2 -0
- package/dist/cli/commands/run.js +36 -4
- package/dist/cli/commands/skills.d.ts +2 -0
- package/dist/cli/commands/skills.js +29 -7
- package/dist/cli/interactive/ansi.d.ts +41 -0
- package/dist/cli/interactive/ansi.js +43 -0
- package/dist/cli/interactive/complete.d.ts +10 -0
- package/dist/cli/interactive/complete.js +19 -0
- package/dist/cli/interactive/repl.d.ts +3 -0
- package/dist/cli/interactive/repl.js +105 -16
- package/dist/cli/interactive/session.d.ts +12 -1
- package/dist/cli/interactive/session.js +83 -5
- package/dist/cli/render.d.ts +13 -0
- package/dist/cli/render.js +18 -0
- package/dist/cli/runtimeSession.d.ts +11 -0
- package/dist/cli/runtimeSession.js +17 -0
- package/dist/config/defaults.d.ts +3 -1
- package/dist/config/defaults.js +2 -0
- package/dist/config/schema.d.ts +1 -0
- package/dist/config/schema.js +2 -2
- package/dist/context/lossVerifier.d.ts +24 -0
- package/dist/context/lossVerifier.js +45 -0
- package/dist/context/summarize.d.ts +19 -0
- package/dist/context/summarize.js +53 -0
- package/dist/core/fallback/fallback.d.ts +8 -0
- package/dist/core/fallback/fallback.js +3 -1
- package/dist/core/router/executor.d.ts +6 -1
- package/dist/core/router/executor.js +9 -2
- package/dist/core/router/normalize.d.ts +2 -0
- package/dist/core/router/request.js +2 -0
- package/dist/core/router/router.d.ts +3 -0
- package/dist/core/router/router.js +7 -0
- package/dist/executions/execution.d.ts +13 -2
- package/dist/generation/generateAdapter.d.ts +14 -0
- package/dist/generation/generateAdapter.js +38 -0
- package/dist/generation/generateSkill.d.ts +26 -0
- package/dist/generation/generateSkill.js +51 -0
- package/dist/index.d.ts +44 -5
- package/dist/index.js +26 -2
- package/dist/mcp/client.d.ts +70 -0
- package/dist/mcp/client.js +221 -0
- package/dist/mcp/manager.d.ts +151 -0
- package/dist/mcp/manager.js +493 -0
- package/dist/mcp/protocol.d.ts +216 -0
- package/dist/mcp/protocol.js +149 -0
- package/dist/mcp/toolAdapter.d.ts +44 -0
- package/dist/mcp/toolAdapter.js +94 -0
- package/dist/mcp/transport.d.ts +109 -0
- package/dist/mcp/transport.js +383 -0
- package/dist/memory/embedders/hash.d.ts +12 -0
- package/dist/memory/embedders/hash.js +31 -0
- package/dist/memory/embedders/http.d.ts +25 -0
- package/dist/memory/embedders/http.js +48 -0
- package/dist/memory/memory.d.ts +19 -2
- package/dist/memory/memory.js +75 -11
- package/dist/memory/semantic.d.ts +17 -0
- package/dist/memory/semantic.js +29 -0
- package/dist/orchestration/budget.d.ts +30 -0
- package/dist/orchestration/budget.js +40 -0
- package/dist/orchestration/executor.d.ts +39 -1
- package/dist/orchestration/executor.js +64 -4
- package/dist/orchestration/orchestrator.d.ts +29 -1
- package/dist/orchestration/orchestrator.js +89 -8
- package/dist/orchestration/plan.d.ts +15 -1
- package/dist/orchestration/plan.js +23 -4
- package/dist/orchestration/planner.d.ts +19 -1
- package/dist/orchestration/planner.js +25 -5
- package/dist/plugin/ai.d.ts +4 -0
- package/dist/plugin/ai.js +9 -0
- package/dist/providers/httpClient.d.ts +25 -1
- package/dist/providers/httpClient.js +93 -0
- package/dist/providers/httpProvider.d.ts +1 -0
- package/dist/providers/httpProvider.js +67 -1
- package/dist/providers/mock/mockProvider.d.ts +3 -0
- package/dist/providers/mock/mockProvider.js +54 -0
- package/dist/providers/mock/scenarios.d.ts +7 -0
- package/dist/providers/provider.d.ts +6 -0
- package/dist/providers/wire/anthropicWire.js +34 -0
- package/dist/providers/wire/openaiWire.js +30 -0
- package/dist/providers/wire/types.d.ts +16 -0
- package/dist/runtime/config.js +50 -6
- package/dist/runtime/intent/aiClassifier.d.ts +19 -0
- package/dist/runtime/intent/aiClassifier.js +74 -0
- package/dist/runtime/models/modelProfile.d.ts +61 -0
- package/dist/runtime/models/modelProfile.js +139 -0
- package/dist/runtime/planning/deriveCapabilities.d.ts +95 -0
- package/dist/runtime/planning/deriveCapabilities.js +146 -0
- package/dist/runtime/policy.d.ts +10 -0
- package/dist/runtime/policy.js +9 -2
- package/dist/runtime/runtime.d.ts +173 -0
- package/dist/runtime/runtime.js +723 -50
- package/dist/runtime/types.d.ts +94 -2
- package/dist/skills/manifest.d.ts +3 -0
- package/dist/skills/manifest.js +24 -0
- package/dist/skills/registry.d.ts +16 -1
- package/dist/skills/registry.js +21 -1
- package/dist/skills/skill.d.ts +6 -1
- package/dist/store/area.d.ts +15 -1
- package/dist/store/area.js +19 -8
- package/dist/store/crypto.d.ts +21 -0
- package/dist/store/crypto.js +49 -0
- package/dist/store/paths.d.ts +5 -1
- package/dist/store/paths.js +6 -0
- package/dist/store/store.d.ts +15 -3
- package/dist/store/store.js +28 -7
- package/dist/telemetry/sinks/otlp.d.ts +31 -0
- package/dist/telemetry/sinks/otlp.js +76 -0
- package/dist/tools/builtins/filesystem.js +1 -0
- package/dist/tools/builtins/git.js +1 -0
- package/dist/tools/builtins/shell.js +1 -0
- package/dist/tools/permissions.d.ts +28 -0
- package/dist/tools/permissions.js +72 -0
- package/dist/tools/registry.d.ts +18 -2
- package/dist/tools/registry.js +22 -2
- package/dist/tools/tool.d.ts +4 -0
- package/dist/types.d.ts +11 -1
- package/dist/util/flatten.d.ts +11 -0
- package/dist/util/flatten.js +18 -0
- package/dist/util/semaphore.d.ts +19 -0
- package/dist/util/semaphore.js +60 -0
- package/package.json +24 -9
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP wire protocol (Phase 3.2) - JSON-RPC 2.0 shapes plus TOLERANT zod validators for the small
|
|
3
|
+
* client-side subset we speak: `initialize`, `tools/list`, `tools/call`, `ping`. Result objects are
|
|
4
|
+
* validated only on the fields we consume (`.passthrough()`), so a server that adds fields or drifts
|
|
5
|
+
* ahead of our pinned protocol version degrades gracefully instead of breaking the run.
|
|
6
|
+
*
|
|
7
|
+
* SECURITY - MCP metadata (tool names, descriptions, schemas) is attacker-controlled text that reaches
|
|
8
|
+
* model prompts (the planner catalog, the `mcp: true` declaration path). `normalizeToolDecl` is the
|
|
9
|
+
* DISCOVERY-TIME gate: it validates the name charset, strips control characters, and clamps length, so a
|
|
10
|
+
* server can neither forge catalog rows nor smuggle instructions through its own metadata.
|
|
11
|
+
*/
|
|
12
|
+
import { z } from 'zod';
|
|
13
|
+
/** The protocol version this client speaks. A server may counter-propose; see `McpClient.initialize`. */
|
|
14
|
+
export declare const MCP_PROTOCOL_VERSION = "2025-06-18";
|
|
15
|
+
/**
|
|
16
|
+
* What we tell a server we are (the spec's `Implementation`). Versioned for THIS client implementation,
|
|
17
|
+
* deliberately NOT the product version: a third-party server has no business fingerprinting the host,
|
|
18
|
+
* and it keeps the release version to its three canonical sites.
|
|
19
|
+
*/
|
|
20
|
+
export declare const MCP_CLIENT_INFO: {
|
|
21
|
+
readonly name: "ai-runtime";
|
|
22
|
+
readonly version: "1.0.0";
|
|
23
|
+
};
|
|
24
|
+
export interface JsonRpcRequest {
|
|
25
|
+
jsonrpc: '2.0';
|
|
26
|
+
id: number;
|
|
27
|
+
method: string;
|
|
28
|
+
params?: unknown;
|
|
29
|
+
}
|
|
30
|
+
export interface JsonRpcNotification {
|
|
31
|
+
jsonrpc: '2.0';
|
|
32
|
+
method: string;
|
|
33
|
+
params?: unknown;
|
|
34
|
+
}
|
|
35
|
+
export interface JsonRpcResponse {
|
|
36
|
+
jsonrpc: '2.0';
|
|
37
|
+
id: number;
|
|
38
|
+
result?: unknown;
|
|
39
|
+
error?: {
|
|
40
|
+
code: number;
|
|
41
|
+
message: string;
|
|
42
|
+
data?: unknown;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export type JsonRpcMessage = JsonRpcResponse | JsonRpcNotification;
|
|
46
|
+
/**
|
|
47
|
+
* Application error codes (-32000..-32099) are implementation-defined by JSON-RPC and NOT standardized
|
|
48
|
+
* by MCP, so they are deliberately NOT read as an auth signal: several SDKs use -32002 for "resource not
|
|
49
|
+
* found" and -32001 for a server-side timeout, and mapping those to AUTHENTICATION would park a healthy
|
|
50
|
+
* server in `auth_failed` for the whole session (which is never silently retried). Authentication is
|
|
51
|
+
* detected where it is unambiguous: the transport's HTTP 401/403.
|
|
52
|
+
*/
|
|
53
|
+
export declare const JSONRPC_AUTH_CODES: ReadonlySet<number>;
|
|
54
|
+
export declare const initializeResult: z.ZodObject<{
|
|
55
|
+
protocolVersion: z.ZodOptional<z.ZodString>;
|
|
56
|
+
serverInfo: z.ZodOptional<z.ZodObject<{
|
|
57
|
+
name: z.ZodOptional<z.ZodString>;
|
|
58
|
+
version: z.ZodOptional<z.ZodString>;
|
|
59
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
60
|
+
name: z.ZodOptional<z.ZodString>;
|
|
61
|
+
version: z.ZodOptional<z.ZodString>;
|
|
62
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
63
|
+
name: z.ZodOptional<z.ZodString>;
|
|
64
|
+
version: z.ZodOptional<z.ZodString>;
|
|
65
|
+
}, z.ZodTypeAny, "passthrough">>>;
|
|
66
|
+
capabilities: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
67
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
68
|
+
protocolVersion: z.ZodOptional<z.ZodString>;
|
|
69
|
+
serverInfo: z.ZodOptional<z.ZodObject<{
|
|
70
|
+
name: z.ZodOptional<z.ZodString>;
|
|
71
|
+
version: z.ZodOptional<z.ZodString>;
|
|
72
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
73
|
+
name: z.ZodOptional<z.ZodString>;
|
|
74
|
+
version: z.ZodOptional<z.ZodString>;
|
|
75
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
76
|
+
name: z.ZodOptional<z.ZodString>;
|
|
77
|
+
version: z.ZodOptional<z.ZodString>;
|
|
78
|
+
}, z.ZodTypeAny, "passthrough">>>;
|
|
79
|
+
capabilities: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
80
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
81
|
+
protocolVersion: z.ZodOptional<z.ZodString>;
|
|
82
|
+
serverInfo: z.ZodOptional<z.ZodObject<{
|
|
83
|
+
name: z.ZodOptional<z.ZodString>;
|
|
84
|
+
version: z.ZodOptional<z.ZodString>;
|
|
85
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
86
|
+
name: z.ZodOptional<z.ZodString>;
|
|
87
|
+
version: z.ZodOptional<z.ZodString>;
|
|
88
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
89
|
+
name: z.ZodOptional<z.ZodString>;
|
|
90
|
+
version: z.ZodOptional<z.ZodString>;
|
|
91
|
+
}, z.ZodTypeAny, "passthrough">>>;
|
|
92
|
+
capabilities: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
93
|
+
}, z.ZodTypeAny, "passthrough">>;
|
|
94
|
+
export declare const mcpToolDecl: z.ZodObject<{
|
|
95
|
+
name: z.ZodString;
|
|
96
|
+
description: z.ZodOptional<z.ZodString>;
|
|
97
|
+
inputSchema: z.ZodOptional<z.ZodObject<{
|
|
98
|
+
type: z.ZodLiteral<"object">;
|
|
99
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
100
|
+
type: z.ZodLiteral<"object">;
|
|
101
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
102
|
+
type: z.ZodLiteral<"object">;
|
|
103
|
+
}, z.ZodTypeAny, "passthrough">>>;
|
|
104
|
+
annotations: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
105
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
106
|
+
name: z.ZodString;
|
|
107
|
+
description: z.ZodOptional<z.ZodString>;
|
|
108
|
+
inputSchema: z.ZodOptional<z.ZodObject<{
|
|
109
|
+
type: z.ZodLiteral<"object">;
|
|
110
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
111
|
+
type: z.ZodLiteral<"object">;
|
|
112
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
113
|
+
type: z.ZodLiteral<"object">;
|
|
114
|
+
}, z.ZodTypeAny, "passthrough">>>;
|
|
115
|
+
annotations: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
116
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
117
|
+
name: z.ZodString;
|
|
118
|
+
description: z.ZodOptional<z.ZodString>;
|
|
119
|
+
inputSchema: z.ZodOptional<z.ZodObject<{
|
|
120
|
+
type: z.ZodLiteral<"object">;
|
|
121
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
122
|
+
type: z.ZodLiteral<"object">;
|
|
123
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
124
|
+
type: z.ZodLiteral<"object">;
|
|
125
|
+
}, z.ZodTypeAny, "passthrough">>>;
|
|
126
|
+
annotations: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
127
|
+
}, z.ZodTypeAny, "passthrough">>;
|
|
128
|
+
export type McpToolDecl = z.infer<typeof mcpToolDecl>;
|
|
129
|
+
export declare const listToolsResult: z.ZodObject<{
|
|
130
|
+
tools: z.ZodOptional<z.ZodArray<z.ZodUnknown, "many">>;
|
|
131
|
+
nextCursor: z.ZodOptional<z.ZodString>;
|
|
132
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
133
|
+
tools: z.ZodOptional<z.ZodArray<z.ZodUnknown, "many">>;
|
|
134
|
+
nextCursor: z.ZodOptional<z.ZodString>;
|
|
135
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
136
|
+
tools: z.ZodOptional<z.ZodArray<z.ZodUnknown, "many">>;
|
|
137
|
+
nextCursor: z.ZodOptional<z.ZodString>;
|
|
138
|
+
}, z.ZodTypeAny, "passthrough">>;
|
|
139
|
+
export declare const callToolResult: z.ZodObject<{
|
|
140
|
+
content: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
141
|
+
type: z.ZodString;
|
|
142
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
143
|
+
type: z.ZodString;
|
|
144
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
145
|
+
type: z.ZodString;
|
|
146
|
+
}, z.ZodTypeAny, "passthrough">>, "many">>;
|
|
147
|
+
structuredContent: z.ZodOptional<z.ZodUnknown>;
|
|
148
|
+
isError: z.ZodOptional<z.ZodBoolean>;
|
|
149
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
150
|
+
content: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
151
|
+
type: z.ZodString;
|
|
152
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
153
|
+
type: z.ZodString;
|
|
154
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
155
|
+
type: z.ZodString;
|
|
156
|
+
}, z.ZodTypeAny, "passthrough">>, "many">>;
|
|
157
|
+
structuredContent: z.ZodOptional<z.ZodUnknown>;
|
|
158
|
+
isError: z.ZodOptional<z.ZodBoolean>;
|
|
159
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
160
|
+
content: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
161
|
+
type: z.ZodString;
|
|
162
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
163
|
+
type: z.ZodString;
|
|
164
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
165
|
+
type: z.ZodString;
|
|
166
|
+
}, z.ZodTypeAny, "passthrough">>, "many">>;
|
|
167
|
+
structuredContent: z.ZodOptional<z.ZodUnknown>;
|
|
168
|
+
isError: z.ZodOptional<z.ZodBoolean>;
|
|
169
|
+
}, z.ZodTypeAny, "passthrough">>;
|
|
170
|
+
/** What a tool call returned, already flattened to text + optional structured payload. */
|
|
171
|
+
export interface McpCallResult {
|
|
172
|
+
text: string;
|
|
173
|
+
structured?: unknown;
|
|
174
|
+
isError: boolean;
|
|
175
|
+
}
|
|
176
|
+
/** Tool names must be a safe, addressable identifier - this also becomes the Tool id suffix. */
|
|
177
|
+
export declare const MCP_TOOL_NAME_RE: RegExp;
|
|
178
|
+
/** Descriptions reach model prompts; clamp hard. */
|
|
179
|
+
export declare const MCP_DESCRIPTION_MAX = 300;
|
|
180
|
+
/** A schema's own strings (property descriptions, titles, enum values) reach model prompts too. */
|
|
181
|
+
export declare const MCP_SCHEMA_STRING_MAX = 200;
|
|
182
|
+
/** Bounds on a single tool's schema: an unbounded one inflates EVERY planning call that declares it. */
|
|
183
|
+
export declare const MCP_SCHEMA_MAX_DEPTH = 12;
|
|
184
|
+
export declare const MCP_SCHEMA_MAX_NODES = 400;
|
|
185
|
+
/** Flatten and clamp server-controlled text: no control chars, no newlines, bounded length. */
|
|
186
|
+
export declare function clampText(raw: unknown, max: number): string;
|
|
187
|
+
/**
|
|
188
|
+
* Deep-sanitize a server-supplied JSON Schema. A schema is not inert data: it is handed to the model as
|
|
189
|
+
* part of the tool definition, so every string inside it (`description`, `title`, `enum`, `$comment`, …)
|
|
190
|
+
* is untrusted prompt text and gets the same flatten-and-clamp treatment as the tool description.
|
|
191
|
+
* Structure is bounded too — depth, node count, and key count — because a 500-tool server with
|
|
192
|
+
* multi-megabyte schemas would otherwise inflate every request that declares them. Returns `undefined`
|
|
193
|
+
* when the schema is unusable, in which case the tool registers with NO parameters (honest: the model is
|
|
194
|
+
* told nothing rather than something forged).
|
|
195
|
+
*/
|
|
196
|
+
export declare function sanitizeSchema(raw: unknown, budget?: {
|
|
197
|
+
nodes: number;
|
|
198
|
+
}, depth?: number): unknown;
|
|
199
|
+
export interface NormalizedTool {
|
|
200
|
+
name: string;
|
|
201
|
+
description: string;
|
|
202
|
+
inputSchema?: unknown;
|
|
203
|
+
/** From `annotations.readOnlyHint === true` ONLY; anything else is write-like (fail-closed). */
|
|
204
|
+
readOnly: boolean;
|
|
205
|
+
/** `annotations.destructiveHint === true` implies always-confirm, even when granted `full`. */
|
|
206
|
+
destructive: boolean;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Validate + normalize one declaration. Returns `undefined` when the declaration is unusable (bad shape
|
|
210
|
+
* or an unsafe name) - the caller counts it as rejected and NEVER registers it. Annotations are the
|
|
211
|
+
* server's own claims: honored only in the safe direction (`readOnlyHint` narrows what we allow,
|
|
212
|
+
* `destructiveHint` adds friction), never to widen permission.
|
|
213
|
+
*/
|
|
214
|
+
export declare function normalizeToolDecl(raw: unknown): NormalizedTool | undefined;
|
|
215
|
+
/** Flatten a `tools/call` result into text + structured payload (text blocks only; other kinds dropped). */
|
|
216
|
+
export declare function flattenCallResult(raw: unknown): McpCallResult;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP wire protocol (Phase 3.2) - JSON-RPC 2.0 shapes plus TOLERANT zod validators for the small
|
|
3
|
+
* client-side subset we speak: `initialize`, `tools/list`, `tools/call`, `ping`. Result objects are
|
|
4
|
+
* validated only on the fields we consume (`.passthrough()`), so a server that adds fields or drifts
|
|
5
|
+
* ahead of our pinned protocol version degrades gracefully instead of breaking the run.
|
|
6
|
+
*
|
|
7
|
+
* SECURITY - MCP metadata (tool names, descriptions, schemas) is attacker-controlled text that reaches
|
|
8
|
+
* model prompts (the planner catalog, the `mcp: true` declaration path). `normalizeToolDecl` is the
|
|
9
|
+
* DISCOVERY-TIME gate: it validates the name charset, strips control characters, and clamps length, so a
|
|
10
|
+
* server can neither forge catalog rows nor smuggle instructions through its own metadata.
|
|
11
|
+
*/
|
|
12
|
+
import { z } from 'zod';
|
|
13
|
+
/** Control characters and DEL - stripped before any server text is rendered or prompted. */
|
|
14
|
+
const CONTROL_CHARS = /[\u0000-\u001f\u007f]+/g;
|
|
15
|
+
/** The protocol version this client speaks. A server may counter-propose; see `McpClient.initialize`. */
|
|
16
|
+
export const MCP_PROTOCOL_VERSION = '2025-06-18';
|
|
17
|
+
/**
|
|
18
|
+
* What we tell a server we are (the spec's `Implementation`). Versioned for THIS client implementation,
|
|
19
|
+
* deliberately NOT the product version: a third-party server has no business fingerprinting the host,
|
|
20
|
+
* and it keeps the release version to its three canonical sites.
|
|
21
|
+
*/
|
|
22
|
+
export const MCP_CLIENT_INFO = { name: 'ai-runtime', version: '1.0.0' };
|
|
23
|
+
/**
|
|
24
|
+
* Application error codes (-32000..-32099) are implementation-defined by JSON-RPC and NOT standardized
|
|
25
|
+
* by MCP, so they are deliberately NOT read as an auth signal: several SDKs use -32002 for "resource not
|
|
26
|
+
* found" and -32001 for a server-side timeout, and mapping those to AUTHENTICATION would park a healthy
|
|
27
|
+
* server in `auth_failed` for the whole session (which is never silently retried). Authentication is
|
|
28
|
+
* detected where it is unambiguous: the transport's HTTP 401/403.
|
|
29
|
+
*/
|
|
30
|
+
export const JSONRPC_AUTH_CODES = new Set();
|
|
31
|
+
// -- result validators (tolerant on purpose) ---------------------------------
|
|
32
|
+
export const initializeResult = z
|
|
33
|
+
.object({
|
|
34
|
+
protocolVersion: z.string().optional(),
|
|
35
|
+
serverInfo: z.object({ name: z.string().optional(), version: z.string().optional() }).passthrough().optional(),
|
|
36
|
+
capabilities: z.record(z.unknown()).optional(),
|
|
37
|
+
})
|
|
38
|
+
.passthrough();
|
|
39
|
+
export const mcpToolDecl = z
|
|
40
|
+
.object({
|
|
41
|
+
name: z.string().min(1),
|
|
42
|
+
description: z.string().optional(),
|
|
43
|
+
inputSchema: z.object({ type: z.literal('object') }).passthrough().optional(),
|
|
44
|
+
// Deliberately untyped: a server sending `readOnlyHint: 'yes'` must not invalidate an otherwise
|
|
45
|
+
// usable tool — the claim is simply not `true`, so the tool stays write-like.
|
|
46
|
+
annotations: z.record(z.unknown()).optional(),
|
|
47
|
+
})
|
|
48
|
+
.passthrough();
|
|
49
|
+
export const listToolsResult = z.object({ tools: z.array(z.unknown()).optional(), nextCursor: z.string().optional() }).passthrough();
|
|
50
|
+
export const callToolResult = z
|
|
51
|
+
.object({
|
|
52
|
+
content: z.array(z.object({ type: z.string() }).passthrough()).optional(),
|
|
53
|
+
structuredContent: z.unknown().optional(),
|
|
54
|
+
isError: z.boolean().optional(),
|
|
55
|
+
})
|
|
56
|
+
.passthrough();
|
|
57
|
+
// -- discovery-time normalization (the second untrusted-content choke point) --
|
|
58
|
+
/** Tool names must be a safe, addressable identifier - this also becomes the Tool id suffix. */
|
|
59
|
+
export const MCP_TOOL_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$/;
|
|
60
|
+
/** Descriptions reach model prompts; clamp hard. */
|
|
61
|
+
export const MCP_DESCRIPTION_MAX = 300;
|
|
62
|
+
/** A schema's own strings (property descriptions, titles, enum values) reach model prompts too. */
|
|
63
|
+
export const MCP_SCHEMA_STRING_MAX = 200;
|
|
64
|
+
/** Bounds on a single tool's schema: an unbounded one inflates EVERY planning call that declares it. */
|
|
65
|
+
export const MCP_SCHEMA_MAX_DEPTH = 12;
|
|
66
|
+
export const MCP_SCHEMA_MAX_NODES = 400;
|
|
67
|
+
/** Flatten and clamp server-controlled text: no control chars, no newlines, bounded length. */
|
|
68
|
+
export function clampText(raw, max) {
|
|
69
|
+
const flat = String(raw ?? '')
|
|
70
|
+
.replace(CONTROL_CHARS, ' ')
|
|
71
|
+
.replace(/\s+/g, ' ')
|
|
72
|
+
.trim();
|
|
73
|
+
return flat.length > max ? `${flat.slice(0, max)}...` : flat;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Deep-sanitize a server-supplied JSON Schema. A schema is not inert data: it is handed to the model as
|
|
77
|
+
* part of the tool definition, so every string inside it (`description`, `title`, `enum`, `$comment`, …)
|
|
78
|
+
* is untrusted prompt text and gets the same flatten-and-clamp treatment as the tool description.
|
|
79
|
+
* Structure is bounded too — depth, node count, and key count — because a 500-tool server with
|
|
80
|
+
* multi-megabyte schemas would otherwise inflate every request that declares them. Returns `undefined`
|
|
81
|
+
* when the schema is unusable, in which case the tool registers with NO parameters (honest: the model is
|
|
82
|
+
* told nothing rather than something forged).
|
|
83
|
+
*/
|
|
84
|
+
export function sanitizeSchema(raw, budget = { nodes: MCP_SCHEMA_MAX_NODES }, depth = 0) {
|
|
85
|
+
if (depth > MCP_SCHEMA_MAX_DEPTH || budget.nodes <= 0)
|
|
86
|
+
return undefined;
|
|
87
|
+
budget.nodes -= 1;
|
|
88
|
+
if (typeof raw === 'string')
|
|
89
|
+
return clampText(raw, MCP_SCHEMA_STRING_MAX);
|
|
90
|
+
if (typeof raw === 'number' || typeof raw === 'boolean' || raw === null)
|
|
91
|
+
return raw;
|
|
92
|
+
if (Array.isArray(raw)) {
|
|
93
|
+
const out = [];
|
|
94
|
+
for (const v of raw.slice(0, 64)) {
|
|
95
|
+
const s = sanitizeSchema(v, budget, depth + 1);
|
|
96
|
+
if (s !== undefined)
|
|
97
|
+
out.push(s);
|
|
98
|
+
}
|
|
99
|
+
return out;
|
|
100
|
+
}
|
|
101
|
+
if (typeof raw !== 'object')
|
|
102
|
+
return undefined;
|
|
103
|
+
const out = {};
|
|
104
|
+
for (const [k, v] of Object.entries(raw).slice(0, 64)) {
|
|
105
|
+
const key = clampText(k, 64);
|
|
106
|
+
if (!key)
|
|
107
|
+
continue;
|
|
108
|
+
const s = sanitizeSchema(v, budget, depth + 1);
|
|
109
|
+
if (s !== undefined)
|
|
110
|
+
out[key] = s;
|
|
111
|
+
}
|
|
112
|
+
return out;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Validate + normalize one declaration. Returns `undefined` when the declaration is unusable (bad shape
|
|
116
|
+
* or an unsafe name) - the caller counts it as rejected and NEVER registers it. Annotations are the
|
|
117
|
+
* server's own claims: honored only in the safe direction (`readOnlyHint` narrows what we allow,
|
|
118
|
+
* `destructiveHint` adds friction), never to widen permission.
|
|
119
|
+
*/
|
|
120
|
+
export function normalizeToolDecl(raw) {
|
|
121
|
+
const parsed = mcpToolDecl.safeParse(raw);
|
|
122
|
+
if (!parsed.success)
|
|
123
|
+
return undefined;
|
|
124
|
+
const decl = parsed.data;
|
|
125
|
+
const name = clampText(decl.name, 64);
|
|
126
|
+
if (!MCP_TOOL_NAME_RE.test(name))
|
|
127
|
+
return undefined;
|
|
128
|
+
const schema = decl.inputSchema ? sanitizeSchema(decl.inputSchema) : undefined;
|
|
129
|
+
return {
|
|
130
|
+
name,
|
|
131
|
+
description: clampText(decl.description ?? name, MCP_DESCRIPTION_MAX),
|
|
132
|
+
// The schema is prompt text too — sanitized and bounded, never passed through (see sanitizeSchema).
|
|
133
|
+
...(schema !== undefined ? { inputSchema: schema } : {}),
|
|
134
|
+
readOnly: decl.annotations?.readOnlyHint === true,
|
|
135
|
+
destructive: decl.annotations?.destructiveHint === true,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
/** Flatten a `tools/call` result into text + structured payload (text blocks only; other kinds dropped). */
|
|
139
|
+
export function flattenCallResult(raw) {
|
|
140
|
+
const parsed = callToolResult.safeParse(raw);
|
|
141
|
+
if (!parsed.success)
|
|
142
|
+
return { text: '', isError: true };
|
|
143
|
+
const r = parsed.data;
|
|
144
|
+
const text = (r.content ?? [])
|
|
145
|
+
.filter((c) => c.type === 'text' && typeof c.text === 'string')
|
|
146
|
+
.map((c) => c.text)
|
|
147
|
+
.join('\n');
|
|
148
|
+
return { text, ...(r.structuredContent !== undefined ? { structured: r.structuredContent } : {}), isError: r.isError === true };
|
|
149
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP → Runtime Tool adapter (Phase 3.2). Every discovered MCP tool is wrapped as an ORDINARY `Tool` and
|
|
3
|
+
* registered in the Runtime `ToolRegistry`, so `Runtime.runTool`'s existing machinery applies unchanged:
|
|
4
|
+
* resolved `ExecutionPolicy` permissions, host approval via `authorize()`, the abort signal, structured
|
|
5
|
+
* denials, and output redaction. There is deliberately NO parallel execution path for MCP.
|
|
6
|
+
*
|
|
7
|
+
* The four rules this file enforces:
|
|
8
|
+
* 1. PERMISSION — deny-by-default per server (`off` | `read` | `full`); a write-like tool needs `full`.
|
|
9
|
+
* 2. APPROVAL — routed through `authorize()`; a `destructiveHint` tool always confirms, even at `full`.
|
|
10
|
+
* 3. UNTRUSTED — the call's text output is `wrapUntrusted`-fenced and redacted before it can reach a
|
|
11
|
+
* prompt; a server-controlled ERROR message is mapped to a fixed taxonomy, never interpolated raw.
|
|
12
|
+
* 4. EFFECTS come from the LIVE handshake's annotations, never from a cached tool list — a cached
|
|
13
|
+
* `readOnlyHint` must not be able to lower the gate for a server that has since changed.
|
|
14
|
+
*/
|
|
15
|
+
import type { Tool } from '../tools/tool.js';
|
|
16
|
+
import type { NormalizedTool } from './protocol.js';
|
|
17
|
+
/** What the adapter needs from the manager — kept narrow so the manager stays swappable in tests. */
|
|
18
|
+
export interface McpCallable {
|
|
19
|
+
call(serverId: string, tool: string, args: unknown, opts?: {
|
|
20
|
+
signal?: AbortSignal;
|
|
21
|
+
}): Promise<{
|
|
22
|
+
text: string;
|
|
23
|
+
structured?: unknown;
|
|
24
|
+
isError: boolean;
|
|
25
|
+
}>;
|
|
26
|
+
/** Whether the server is usable right now; false ⇒ a structured UNAVAILABLE denial (never a crash). */
|
|
27
|
+
usable(serverId: string): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* The annotations from the CURRENT handshake, or undefined when there is no live connection. The gates
|
|
30
|
+
* below read this and never the registration-time snapshot: the snapshot may have come from a cache
|
|
31
|
+
* written before the server changed its annotations.
|
|
32
|
+
*/
|
|
33
|
+
liveTool(serverId: string, tool: string): {
|
|
34
|
+
readOnly: boolean;
|
|
35
|
+
destructive: boolean;
|
|
36
|
+
} | undefined;
|
|
37
|
+
}
|
|
38
|
+
/** The Tool id for an MCP tool: `<serverId>.<toolName>` (matches the shipped namespacing convention). */
|
|
39
|
+
export declare function mcpToolId(serverId: string, toolName: string): string;
|
|
40
|
+
/**
|
|
41
|
+
* Build the `Tool` for one discovered MCP tool. `readOnly`/`destructive` are the LIVE annotation values
|
|
42
|
+
* captured at handshake time by the caller.
|
|
43
|
+
*/
|
|
44
|
+
export declare function mcpTool(manager: McpCallable, serverId: string, decl: NormalizedTool): Tool;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP → Runtime Tool adapter (Phase 3.2). Every discovered MCP tool is wrapped as an ORDINARY `Tool` and
|
|
3
|
+
* registered in the Runtime `ToolRegistry`, so `Runtime.runTool`'s existing machinery applies unchanged:
|
|
4
|
+
* resolved `ExecutionPolicy` permissions, host approval via `authorize()`, the abort signal, structured
|
|
5
|
+
* denials, and output redaction. There is deliberately NO parallel execution path for MCP.
|
|
6
|
+
*
|
|
7
|
+
* The four rules this file enforces:
|
|
8
|
+
* 1. PERMISSION — deny-by-default per server (`off` | `read` | `full`); a write-like tool needs `full`.
|
|
9
|
+
* 2. APPROVAL — routed through `authorize()`; a `destructiveHint` tool always confirms, even at `full`.
|
|
10
|
+
* 3. UNTRUSTED — the call's text output is `wrapUntrusted`-fenced and redacted before it can reach a
|
|
11
|
+
* prompt; a server-controlled ERROR message is mapped to a fixed taxonomy, never interpolated raw.
|
|
12
|
+
* 4. EFFECTS come from the LIVE handshake's annotations, never from a cached tool list — a cached
|
|
13
|
+
* `readOnlyHint` must not be able to lower the gate for a server that has since changed.
|
|
14
|
+
*/
|
|
15
|
+
import { authorize, denied } from '../tools/tool.js';
|
|
16
|
+
import { wrapUntrusted } from '../tools/untrusted.js';
|
|
17
|
+
import { redactString } from '../security/redact.js';
|
|
18
|
+
import { AIError } from '../core/fallback/errors.js';
|
|
19
|
+
import { namespacedId } from '../capabilities/capability.js';
|
|
20
|
+
/** The Tool id for an MCP tool: `<serverId>.<toolName>` (matches the shipped namespacing convention). */
|
|
21
|
+
export function mcpToolId(serverId, toolName) {
|
|
22
|
+
return `${serverId}.${toolName}`;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Build the `Tool` for one discovered MCP tool. `readOnly`/`destructive` are the LIVE annotation values
|
|
26
|
+
* captured at handshake time by the caller.
|
|
27
|
+
*/
|
|
28
|
+
export function mcpTool(manager, serverId, decl) {
|
|
29
|
+
const capabilityId = namespacedId('mcp', mcpToolId(serverId, decl.name));
|
|
30
|
+
return {
|
|
31
|
+
id: mcpToolId(serverId, decl.name),
|
|
32
|
+
description: `${decl.description} (MCP server: ${serverId})`,
|
|
33
|
+
...(decl.inputSchema ? { parameters: decl.inputSchema } : {}),
|
|
34
|
+
// Action capability: namespaced to its server so it can never claim a curated id (Phase 3.1 rule).
|
|
35
|
+
// Built with the SAME helper the registry uses, so the declared id is exactly the one it resolves to.
|
|
36
|
+
provides: [{ id: capabilityId, effects: decl.readOnly ? ['read'] : ['write'], description: decl.description }],
|
|
37
|
+
async execute(input, ctx) {
|
|
38
|
+
// (0) Annotations come from the LIVE handshake. With no live connection the registration-time
|
|
39
|
+
// snapshot is not trusted for GATING (it may be a cache written before the server changed), so
|
|
40
|
+
// both gates fail closed: write-like AND destructive.
|
|
41
|
+
const live = manager.liveTool(serverId, decl.name);
|
|
42
|
+
const writeLike = live ? !live.readOnly : true;
|
|
43
|
+
const destructive = live ? live.destructive : true;
|
|
44
|
+
// (1) permission — deny-by-default, per server, read/full split. `hasOwnProperty` because an
|
|
45
|
+
// unconfigured server must answer "no grant", never inherit something from a prototype.
|
|
46
|
+
const servers = ctx.permissions.mcp.servers;
|
|
47
|
+
const grant = Object.prototype.hasOwnProperty.call(servers, serverId) ? servers[serverId] : 'off';
|
|
48
|
+
if (grant !== 'read' && grant !== 'full') {
|
|
49
|
+
return denied('PERMISSION', `MCP server '${serverId}' is not permitted — grant permissions.mcp.servers.${serverId}: read (or full)`);
|
|
50
|
+
}
|
|
51
|
+
if (writeLike && grant !== 'full') {
|
|
52
|
+
return denied('PERMISSION', `'${decl.name}' is not declared read-only${live ? '' : ' by a live connection'}; server '${serverId}' is granted '${grant}' (needs full)`);
|
|
53
|
+
}
|
|
54
|
+
// (2) approval — the grant checked above IS the standing permission (the house convention:
|
|
55
|
+
// `authorize(ctx, <standing permission>, …)`), so a granted call proceeds headlessly. Passing
|
|
56
|
+
// `false` here would make `full` useless without an interactive host AND would let a host widen
|
|
57
|
+
// past a grant the server was never given.
|
|
58
|
+
const ok = await authorize(ctx, true, {
|
|
59
|
+
action: `mcp:${serverId}.${decl.name}`,
|
|
60
|
+
risk: writeLike ? 'high' : 'low',
|
|
61
|
+
...(destructive ? { alwaysConfirm: true } : {}),
|
|
62
|
+
});
|
|
63
|
+
if (!ok)
|
|
64
|
+
return denied('PERMISSION', `approval denied for mcp:${serverId}.${decl.name}`);
|
|
65
|
+
// Availability — honest at call time (a cached tool whose server is unreachable says so).
|
|
66
|
+
if (!manager.usable(serverId)) {
|
|
67
|
+
return denied('NOT_FOUND', `MCP server '${serverId}' is not connected — run 'ai-runtime mcp ${serverId} test' or re-enable it`);
|
|
68
|
+
}
|
|
69
|
+
let res;
|
|
70
|
+
try {
|
|
71
|
+
res = await manager.call(serverId, decl.name, input ?? {}, { ...(ctx.signal ? { signal: ctx.signal } : {}) });
|
|
72
|
+
}
|
|
73
|
+
catch (e) {
|
|
74
|
+
// (3b) a transport/protocol failure maps to a taxonomy — never the server's raw text.
|
|
75
|
+
const category = e instanceof AIError ? e.category : 'UNKNOWN';
|
|
76
|
+
return denied('EXEC_FAILED', `MCP call failed (${category}) on '${serverId}.${decl.name}'`);
|
|
77
|
+
}
|
|
78
|
+
// (3a) a tool-level error: fixed taxonomy + the server's text only as fenced diagnostics
|
|
79
|
+
if (res.isError) {
|
|
80
|
+
return {
|
|
81
|
+
ok: false,
|
|
82
|
+
error: { code: 'EXEC_FAILED', message: `MCP tool '${serverId}.${decl.name}' returned an error` },
|
|
83
|
+
data: { diagnostic: wrapUntrusted(`mcp:${serverId}.${decl.name}`, redactString(res.text).slice(0, 500)) },
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
// Success: the text output is UNTRUSTED data — fenced + redacted at this single boundary.
|
|
87
|
+
return {
|
|
88
|
+
ok: true,
|
|
89
|
+
output: wrapUntrusted(`mcp:${serverId}.${decl.name}`, redactString(res.text)),
|
|
90
|
+
...(res.structured !== undefined ? { data: res.structured } : {}),
|
|
91
|
+
};
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP transports (Phase 3.2) — zero-dependency, injectable, offline-testable.
|
|
3
|
+
*
|
|
4
|
+
* `StdioTransport` spawns a server process and frames JSON-RPC as newline-delimited JSON over stdin/
|
|
5
|
+
* stdout (stderr is captured, redacted, and ring-buffered for diagnostics). `HttpTransport` speaks
|
|
6
|
+
* streamable HTTP: it POSTs JSON-RPC and accepts either a single JSON response or an SSE stream, reusing
|
|
7
|
+
* the `data:`-line parsing approach already proven in `providers/httpClient.callHttpStream`, and carries
|
|
8
|
+
* the `Mcp-Session-Id` handed out at initialize. `InProcessTransport` is the test seam.
|
|
9
|
+
*
|
|
10
|
+
* SECURITY: a request URL or spawn command NEVER appears in a thrown error (undici embeds the URL in its
|
|
11
|
+
* message, and a URL can carry a token) — only the error NAME. Credentials arrive already resolved from
|
|
12
|
+
* an env-var NAME via `Credential`, are used solely to build a header (or a single env var for stdio),
|
|
13
|
+
* and are never stored on the transport, logged, or serialized.
|
|
14
|
+
*/
|
|
15
|
+
import type { FetchLike } from '../providers/httpClient.js';
|
|
16
|
+
import type { JsonRpcMessage, JsonRpcNotification, JsonRpcRequest } from './protocol.js';
|
|
17
|
+
export interface McpTransport {
|
|
18
|
+
/** Establish the connection (spawn / probe). Throws an AIError with a redacted message on failure. */
|
|
19
|
+
start(): Promise<void>;
|
|
20
|
+
send(msg: JsonRpcRequest | JsonRpcNotification): Promise<void>;
|
|
21
|
+
onMessage(cb: (msg: JsonRpcMessage) => void): void;
|
|
22
|
+
close(): Promise<void>;
|
|
23
|
+
/** Recent stderr / diagnostic lines (redacted, bounded) for `mcp <id>`. */
|
|
24
|
+
diagnostics(): string[];
|
|
25
|
+
/**
|
|
26
|
+
* True when the peer invalidated our session and the ONLY recovery is a fresh handshake (HTTP 404 on a
|
|
27
|
+
* session-bearing request). The manager reads this to force a reconnect from any state — a retry that
|
|
28
|
+
* reuses the dropped session would fail forever.
|
|
29
|
+
*/
|
|
30
|
+
sessionExpired?: boolean;
|
|
31
|
+
}
|
|
32
|
+
export interface StdioTransportOptions {
|
|
33
|
+
command: string;
|
|
34
|
+
args?: string[];
|
|
35
|
+
cwd?: string;
|
|
36
|
+
/** Resolved credential value, injected as a single env var by NAME (never logged, never stored). */
|
|
37
|
+
tokenEnvName?: string;
|
|
38
|
+
tokenValue?: string;
|
|
39
|
+
/** Extra env passthrough by NAME only (values come from the parent env). */
|
|
40
|
+
env?: NodeJS.ProcessEnv;
|
|
41
|
+
}
|
|
42
|
+
export declare class StdioTransport implements McpTransport {
|
|
43
|
+
private readonly opts;
|
|
44
|
+
private child?;
|
|
45
|
+
private handler?;
|
|
46
|
+
private buffer;
|
|
47
|
+
private readonly diag;
|
|
48
|
+
private closed;
|
|
49
|
+
constructor(opts: StdioTransportOptions);
|
|
50
|
+
start(): Promise<void>;
|
|
51
|
+
private onChunk;
|
|
52
|
+
send(msg: JsonRpcRequest | JsonRpcNotification): Promise<void>;
|
|
53
|
+
onMessage(cb: (msg: JsonRpcMessage) => void): void;
|
|
54
|
+
/** SIGTERM, then a process-group SIGKILL — the same escalation the shell runner uses. */
|
|
55
|
+
close(): Promise<void>;
|
|
56
|
+
diagnostics(): string[];
|
|
57
|
+
}
|
|
58
|
+
export interface HttpTransportOptions {
|
|
59
|
+
url: string;
|
|
60
|
+
/** Resolved credential value → `authorization: Bearer …`. Never stored beyond this closure. */
|
|
61
|
+
token?: string;
|
|
62
|
+
fetchImpl?: FetchLike;
|
|
63
|
+
timeoutMs?: number;
|
|
64
|
+
}
|
|
65
|
+
export declare class HttpTransport implements McpTransport {
|
|
66
|
+
private readonly opts;
|
|
67
|
+
private handler?;
|
|
68
|
+
private sessionId?;
|
|
69
|
+
/** Set once the handshake settles; the spec requires it on every subsequent request. */
|
|
70
|
+
private negotiatedVersion?;
|
|
71
|
+
private readonly diag;
|
|
72
|
+
private readonly fetchImpl;
|
|
73
|
+
constructor(opts: HttpTransportOptions);
|
|
74
|
+
start(): Promise<void>;
|
|
75
|
+
onMessage(cb: (msg: JsonRpcMessage) => void): void;
|
|
76
|
+
/**
|
|
77
|
+
* Whether the session was invalidated (HTTP 404 on a session-bearing request). The manager reads this
|
|
78
|
+
* to force a full reconnect instead of retrying against a session the server has forgotten.
|
|
79
|
+
*/
|
|
80
|
+
sessionExpired: boolean;
|
|
81
|
+
/** Record the version the handshake settled on so later requests can carry the required header. */
|
|
82
|
+
setProtocolVersion(version: string): void;
|
|
83
|
+
send(msg: JsonRpcRequest | JsonRpcNotification): Promise<void>;
|
|
84
|
+
private readJson;
|
|
85
|
+
/**
|
|
86
|
+
* Consume an SSE body. Events are separated by a BLANK line and a single event may carry several
|
|
87
|
+
* `data:` lines that concatenate with newlines — parsing each line on its own loses any multi-line
|
|
88
|
+
* event, and treating a lone CR as no separator makes a CR-only stream unparseable. Both are handled
|
|
89
|
+
* here. We stop once the frame we are waiting for arrives: a RESPONSE (an id plus result/error and no
|
|
90
|
+
* method), not merely anything carrying an id — a server-initiated request would otherwise abandon the
|
|
91
|
+
* stream before our own reply showed up.
|
|
92
|
+
*/
|
|
93
|
+
private readSse;
|
|
94
|
+
/** Parse one SSE event block; returns true when it carried a JSON-RPC RESPONSE to one of our requests. */
|
|
95
|
+
private dispatchSseEvent;
|
|
96
|
+
close(): Promise<void>;
|
|
97
|
+
diagnostics(): string[];
|
|
98
|
+
}
|
|
99
|
+
/** A scripted server for offline tests: a plain function from request to response. */
|
|
100
|
+
export declare class InProcessTransport implements McpTransport {
|
|
101
|
+
private readonly server;
|
|
102
|
+
private handler?;
|
|
103
|
+
constructor(server: (req: JsonRpcRequest) => JsonRpcMessage | undefined | Promise<JsonRpcMessage | undefined>);
|
|
104
|
+
start(): Promise<void>;
|
|
105
|
+
onMessage(cb: (msg: JsonRpcMessage) => void): void;
|
|
106
|
+
send(msg: JsonRpcRequest | JsonRpcNotification): Promise<void>;
|
|
107
|
+
close(): Promise<void>;
|
|
108
|
+
diagnostics(): string[];
|
|
109
|
+
}
|