@nylorun/runtime 0.23.0-beta → 0.25.0-beta
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 +252 -0
- package/README.md +87 -28
- package/dist/adapters/execution/restate.js +1 -1
- package/dist/adapters/replication/pgoutput.d.ts +2 -0
- package/dist/adapters/replication/pgoutput.js +6 -3
- package/dist/adapters/sandbox/local.d.ts +9 -1
- package/dist/adapters/sandbox/local.js +16 -4
- package/dist/adapters/sandbox/virtual.js +4 -1
- package/dist/api/a2a/card.d.ts +11 -1
- package/dist/api/a2a/card.js +26 -2
- package/dist/api/a2a/endpoint.js +9 -3
- package/dist/api/a2a/protocol.d.ts +25 -2
- package/dist/api/a2a/protocol.js +20 -3
- package/dist/api/a2a/routes.d.ts +6 -1
- package/dist/api/a2a/routes.js +67 -9
- package/dist/api/a2a/tasks.js +2 -1
- package/dist/api/ag-ui/endpoint.js +8 -3
- package/dist/api/ag-ui/history.d.ts +6 -1
- package/dist/api/ag-ui/history.js +39 -3
- package/dist/api/ag-ui/routes.d.ts +4 -1
- package/dist/api/ag-ui/routes.js +63 -20
- package/dist/api/ag-ui/translate.js +3 -2
- package/dist/api/components.d.ts +5327 -1091
- package/dist/api/components.js +40 -16
- package/dist/api/gate/keys.d.ts +2 -1
- package/dist/api/gate/keys.js +14 -1
- package/dist/api/gate/routes.d.ts +6 -0
- package/dist/api/gate/routes.js +52 -3
- package/dist/api/http/app.js +17 -6
- package/dist/api/http/body.d.ts +3 -3
- package/dist/api/http/body.js +7 -7
- package/dist/api/http/define.d.ts +6 -0
- package/dist/api/http/define.js +13 -1
- package/dist/api/http/respond.js +1 -1
- package/dist/api/http/routes/artifacts.js +35 -8
- package/dist/api/http/routes/coverage.d.ts +9 -0
- package/dist/api/http/routes/coverage.js +34 -0
- package/dist/api/http/routes/environment-manifests.d.ts +9 -0
- package/dist/api/http/routes/environment-manifests.js +118 -0
- package/dist/api/http/routes/environments.d.ts +11 -0
- package/dist/api/http/routes/environments.js +332 -0
- package/dist/api/http/routes/mcp.d.ts +1 -1
- package/dist/api/http/routes/mcp.js +2 -2
- package/dist/api/http/routes/me.js +1 -1
- package/dist/api/http/routes/reads.d.ts +94 -9
- package/dist/api/http/routes/reads.js +54 -5
- package/dist/api/http/routes/sessions.js +24 -9
- package/dist/api/http/routes/tenant.js +65 -15
- package/dist/api/inbound-files.d.ts +65 -0
- package/dist/api/inbound-files.js +115 -0
- package/dist/api/openapi.js +48 -28
- package/dist/artifacts/disk.d.ts +59 -0
- package/dist/artifacts/disk.js +569 -0
- package/dist/artifacts/export.d.ts +1 -1
- package/dist/artifacts/export.js +2 -3
- package/dist/artifacts/folders.d.ts +6 -3
- package/dist/artifacts/folders.js +17 -12
- package/dist/artifacts/media-types.d.ts +15 -0
- package/dist/artifacts/media-types.js +56 -4
- package/dist/artifacts/parts.d.ts +12 -3
- package/dist/artifacts/parts.js +15 -4
- package/dist/artifacts/service.d.ts +2 -1
- package/dist/artifacts/service.js +6 -1
- package/dist/artifacts/workspace.d.ts +5 -5
- package/dist/artifacts/workspace.js +7 -19
- package/dist/core/flow-host.d.ts +3 -4
- package/dist/core/flow-host.js +5 -2
- package/dist/core/provider.d.ts +3 -2
- package/dist/core/provider.js +5 -37
- package/dist/core/sandbox-routes.d.ts +2 -21
- package/dist/core/sandbox-routes.js +11 -44
- package/dist/core/turn-manifest.d.ts +3 -2
- package/dist/core/turn-manifest.js +29 -5
- package/dist/execution/types.d.ts +1 -1
- package/dist/gates/contract.d.ts +3 -24
- package/dist/gates/contract.js +1 -36
- package/dist/gates/egress-credentials.d.ts +9 -0
- package/dist/gates/egress-credentials.js +11 -4
- package/dist/gates/egress.d.ts +22 -1
- package/dist/gates/egress.js +116 -10
- package/dist/gates/fixture-model.d.ts +28 -0
- package/dist/gates/fixture-model.js +92 -0
- package/dist/gates/handler.js +4 -4
- package/dist/gates/http-tool.d.ts +9 -6
- package/dist/gates/http-tool.js +11 -17
- package/dist/gates/in-process.d.ts +5 -0
- package/dist/gates/in-process.js +12 -1
- package/dist/gates/journal.d.ts +30 -0
- package/dist/gates/journal.js +51 -0
- package/dist/gates/mcp-handler.d.ts +4 -0
- package/dist/gates/mcp-handler.js +7 -4
- package/dist/gates/meter.d.ts +10 -2
- package/dist/gates/meter.js +20 -5
- package/dist/gates/policy.d.ts +49 -0
- package/dist/gates/policy.js +132 -0
- package/dist/gates/tenant-vaults.d.ts +7 -7
- package/dist/gates/tenant-vaults.js +5 -5
- package/dist/gates/tool-calls.d.ts +10 -5
- package/dist/gates/tool-calls.js +28 -10
- package/dist/gates/tool-client.d.ts +8 -0
- package/dist/gates/tool-client.js +27 -11
- package/dist/gates/tool-contract.d.ts +11 -69
- package/dist/gates/tool-contract.js +1 -85
- package/dist/gates/tool-gate.d.ts +1 -1
- package/dist/harness/calls.d.ts +14 -7
- package/dist/harness/calls.js +30 -24
- package/dist/harness/executors.js +12 -9
- package/dist/harness/main.js +24 -5
- package/dist/harness/pod.d.ts +39 -0
- package/dist/harness/pod.js +46 -0
- package/dist/harness/service.d.ts +0 -1
- package/dist/harness/service.js +17 -9
- package/dist/harness/setup.d.ts +29 -0
- package/dist/harness/setup.js +174 -0
- package/dist/harness/ws-client.js +3 -2
- package/dist/harness/ws-port.d.ts +1 -5
- package/dist/harness/ws-port.js +1 -5
- package/dist/harness-api/in-process.js +3 -3
- package/dist/harness-api/record.d.ts +15 -1
- package/dist/harness-api/record.js +180 -50
- package/dist/harness-api/redact.d.ts +3 -0
- package/dist/harness-api/redact.js +45 -0
- package/dist/harness-api/server.d.ts +35 -4
- package/dist/harness-api/server.js +130 -17
- package/dist/harness-api/start.d.ts +10 -3
- package/dist/harness-api/start.js +40 -10
- package/dist/harness-api/tool-journal.d.ts +105 -0
- package/dist/harness-api/tool-journal.js +132 -0
- package/dist/harness-api/tool-status.d.ts +13 -0
- package/dist/harness-api/tool-status.js +26 -0
- package/dist/harness-api/workspace.d.ts +12 -7
- package/dist/harness-api/workspace.js +47 -25
- package/dist/harness-api/ws-server.d.ts +11 -3
- package/dist/harness-api/ws-server.js +94 -15
- package/dist/host/gates.d.ts +2 -1
- package/dist/host/gates.js +10 -2
- package/dist/host/main.js +54 -5
- package/dist/host/operate.js +67 -5
- package/dist/host/stack-config.d.ts +64 -0
- package/dist/host/stack-config.js +62 -2
- package/dist/keys/client.js +4 -1
- package/dist/keys/keys.d.ts +13 -6
- package/dist/keys/keys.js +9 -8
- package/dist/management-openapi.json +1264 -2171
- package/dist/mcp/connect.d.ts +26 -22
- package/dist/mcp/connect.js +30 -34
- package/dist/mcp/pool.d.ts +3 -6
- package/dist/mcp/pool.js +14 -28
- package/dist/mcp/preview.d.ts +3 -3
- package/dist/mcp/preview.js +10 -21
- package/dist/mcp/snapshot.d.ts +16 -24
- package/dist/mcp/snapshot.js +33 -45
- package/dist/model/capabilities.d.ts +26 -0
- package/dist/model/capabilities.js +44 -0
- package/dist/model/catalog.d.ts +34 -3
- package/dist/model/catalog.js +38 -2
- package/dist/model/models.d.ts +2 -0
- package/dist/model/models.js +1 -1
- package/dist/model/native-blocks.d.ts +35 -0
- package/dist/model/native-blocks.js +170 -0
- package/dist/model/pi-model.js +114 -94
- package/dist/openapi.json +12822 -3943
- package/dist/reads/types.d.ts +16 -3
- package/dist/record/append.d.ts +3 -1
- package/dist/record/append.js +1 -0
- package/dist/record/envelope.d.ts +5 -1
- package/dist/record/envelope.js +7 -3
- package/dist/record/environment.d.ts +25 -0
- package/dist/record/environment.js +44 -0
- package/dist/record/index.d.ts +1 -1
- package/dist/record/index.js +1 -1
- package/dist/sandbox/disk-fs.d.ts +95 -0
- package/dist/sandbox/disk-fs.js +206 -0
- package/dist/sandbox/join.d.ts +17 -18
- package/dist/sandbox/join.js +46 -15
- package/dist/sandbox/manager.d.ts +47 -21
- package/dist/sandbox/manager.js +162 -107
- package/dist/sandbox/placement.d.ts +14 -13
- package/dist/sandbox/placement.js +31 -14
- package/dist/sandbox/pods/client.d.ts +9 -2
- package/dist/sandbox/pods/lifecycle.d.ts +36 -27
- package/dist/sandbox/pods/lifecycle.js +29 -26
- package/dist/sandbox/pods/reconcile.js +18 -12
- package/dist/sandbox/pods/spec.d.ts +15 -13
- package/dist/sandbox/pods/spec.js +17 -20
- package/dist/sandbox/records.d.ts +2 -2
- package/dist/sandbox/records.js +3 -3
- package/dist/sandbox/resolve.d.ts +37 -27
- package/dist/sandbox/resolve.js +73 -41
- package/dist/sandbox/runtime-tools.d.ts +9 -0
- package/dist/sandbox/runtime-tools.js +16 -0
- package/dist/sandbox/session-sandbox.d.ts +4 -6
- package/dist/sandbox/session-sandbox.js +47 -94
- package/dist/sandbox/setup.d.ts +64 -0
- package/dist/sandbox/setup.js +160 -0
- package/dist/sandbox/share.d.ts +21 -26
- package/dist/sandbox/share.js +36 -63
- package/dist/sandbox/skills.d.ts +8 -6
- package/dist/sandbox/skills.js +24 -23
- package/dist/sandbox/tenant-config.d.ts +12 -14
- package/dist/sandbox/tenant-config.js +12 -17
- package/dist/sandbox/types.d.ts +6 -0
- package/dist/store/postgres/drizzle/0020_environments.sql +63 -0
- package/dist/store/postgres/drizzle/0021_disk_versions.sql +7 -0
- package/dist/store/postgres/drizzle/0022_model_usage_timing.sql +5 -0
- package/dist/store/postgres/drizzle/0023_artifact_pages.sql +1 -0
- package/dist/store/postgres/drizzle/0024_flat_manifest.sql +7 -0
- package/dist/store/postgres/drizzle/0025_environment_manifests.sql +14 -0
- package/dist/store/postgres/drizzle/0026_environment_forks.sql +1 -0
- package/dist/store/postgres/drizzle/0027_environment_image_digest.sql +1 -0
- package/dist/store/postgres/drizzle/meta/_journal.json +56 -0
- package/dist/store/postgres/reads.js +123 -17
- package/dist/store/postgres/record-writer.d.ts +4 -4
- package/dist/store/postgres/record-writer.js +9 -9
- package/dist/store/postgres/record.d.ts +7 -0
- package/dist/store/postgres/record.js +23 -2
- package/dist/store/postgres/schema.d.ts +1126 -736
- package/dist/store/postgres/schema.js +136 -73
- package/dist/store/postgres/store.js +195 -67
- package/dist/store/types.d.ts +165 -50
- package/dist/tenant/advance.d.ts +7 -6
- package/dist/tenant/advance.js +118 -42
- package/dist/tenant/artifact-tool.d.ts +1 -1
- package/dist/tenant/artifact-tool.js +46 -26
- package/dist/tenant/commands.d.ts +15 -2
- package/dist/tenant/commands.js +115 -14
- package/dist/tenant/context.d.ts +56 -24
- package/dist/tenant/context.js +4 -15
- package/dist/tenant/effects.d.ts +1 -1
- package/dist/tenant/effects.js +44 -29
- package/dist/tenant/environment-manifests.d.ts +57 -0
- package/dist/tenant/environment-manifests.js +200 -0
- package/dist/tenant/environments.d.ts +174 -0
- package/dist/tenant/environments.js +1044 -0
- package/dist/tenant/harness-tokens.d.ts +21 -0
- package/dist/tenant/harness-tokens.js +61 -0
- package/dist/tenant/host-token.d.ts +1 -1
- package/dist/tenant/host-token.js +2 -4
- package/dist/tenant/identity-file.d.ts +4 -4
- package/dist/tenant/identity-file.js +17 -8
- package/dist/tenant/issuers.js +3 -3
- package/dist/tenant/model-setting.d.ts +14 -0
- package/dist/tenant/model-setting.js +51 -0
- package/dist/tenant/principals.js +1 -1
- package/dist/tenant/reset.js +3 -3
- package/dist/tenant/runtime.d.ts +6 -1
- package/dist/tenant/runtime.js +7 -1
- package/dist/tenant/session.d.ts +3 -3
- package/dist/tenant/session.js +0 -14
- package/dist/tenant/sessions.d.ts +18 -14
- package/dist/tenant/sessions.js +99 -143
- package/dist/tenant/signing-keys.d.ts +5 -1
- package/dist/tenant/skill-tool.d.ts +3 -3
- package/dist/tenant/skill-tool.js +6 -11
- package/dist/tenant/status.js +3 -3
- package/dist/tenant/sweep.js +13 -7
- package/dist/tenant/tool-results.js +1 -1
- package/dist/tenant/tool-search.d.ts +7 -11
- package/dist/tenant/tool-search.js +8 -5
- package/dist/tenant/transcript.d.ts +245 -36
- package/dist/tenant/transcript.js +64 -12
- package/dist/tenant/turn-timing.d.ts +43 -0
- package/dist/tenant/turn-timing.js +39 -0
- package/dist/tenant/types.d.ts +2 -0
- package/dist/traces/ids.d.ts +41 -0
- package/dist/traces/ids.js +81 -0
- package/dist/traces/key.d.ts +6 -0
- package/dist/traces/key.js +37 -0
- package/dist/traces/map.d.ts +113 -0
- package/dist/traces/map.js +289 -0
- package/dist/traces/options.d.ts +7 -0
- package/dist/traces/options.js +1 -0
- package/dist/traces/otlp.d.ts +42 -0
- package/dist/traces/otlp.js +84 -0
- package/dist/traces/projector.d.ts +58 -0
- package/dist/traces/projector.js +200 -0
- package/dist/traces/read.d.ts +23 -0
- package/dist/traces/read.js +16 -0
- package/dist/vault/coverage.d.ts +26 -0
- package/dist/vault/coverage.js +62 -0
- package/dist/vault/error.d.ts +11 -1
- package/dist/vault/error.js +5 -1
- package/dist/vault/host-model.d.ts +4 -1
- package/dist/vault/host-model.js +10 -2
- package/dist/vault/service.d.ts +92 -48
- package/dist/vault/service.js +462 -376
- package/dist/vault/sources.d.ts +5 -16
- package/dist/vault/sources.js +6 -8
- package/dist/version.d.ts +1 -1
- package/dist/version.js +2 -2
- package/package.json +13 -13
- package/dist/api/http/routes/sandboxes.d.ts +0 -9
- package/dist/api/http/routes/sandboxes.js +0 -153
- package/dist/api/http/routes/vaults.d.ts +0 -10
- package/dist/api/http/routes/vaults.js +0 -139
- package/dist/record/sandbox.d.ts +0 -25
- package/dist/record/sandbox.js +0 -43
- package/dist/sandbox/capability.d.ts +0 -3
- package/dist/sandbox/capability.js +0 -10
- package/dist/tenant/sandboxes.d.ts +0 -123
- package/dist/tenant/sandboxes.js +0 -566
package/dist/mcp/connect.d.ts
CHANGED
|
@@ -12,10 +12,16 @@ export interface McpRequestOptions {
|
|
|
12
12
|
/** Options of one tool call. */
|
|
13
13
|
export interface McpCallOptions extends McpRequestOptions {
|
|
14
14
|
/**
|
|
15
|
-
* The call's effect id. A gate
|
|
16
|
-
*
|
|
15
|
+
* The call's effect id. A gate keeps a keyed call's outcome, so a re-send after a takeover
|
|
16
|
+
* joins it (F4.1), and the server gets it in `_meta` (R5.5).
|
|
17
17
|
*/
|
|
18
18
|
readonly key?: string;
|
|
19
|
+
/**
|
|
20
|
+
* The call's span context (R7.4), sent as the `traceparent` header on the call's requests where
|
|
21
|
+
* this process holds the connection. A client of the gates service leaves it out: the gate reads
|
|
22
|
+
* it from the call's journal row.
|
|
23
|
+
*/
|
|
24
|
+
readonly traceparent?: string;
|
|
19
25
|
}
|
|
20
26
|
/** A page of `tools/list`, as the MCP SDK returns it. */
|
|
21
27
|
export interface McpToolPage {
|
|
@@ -73,9 +79,8 @@ export type McpToolFailure = {
|
|
|
73
79
|
readonly message: string;
|
|
74
80
|
/** True when calling again may work (R2b C7). */
|
|
75
81
|
readonly retryable?: boolean;
|
|
76
|
-
/** With `credential_rejected`: the server
|
|
82
|
+
/** With `credential_rejected`: the server. */
|
|
77
83
|
readonly server?: string;
|
|
78
|
-
readonly vault?: "installation" | "user";
|
|
79
84
|
/** How many credential values the message had in it, replaced (R2b C8). */
|
|
80
85
|
readonly redacted?: number;
|
|
81
86
|
};
|
|
@@ -93,8 +98,8 @@ export type McpToolOutcome = {
|
|
|
93
98
|
* - `mcp.forbidden`: the server answered `403`.
|
|
94
99
|
* - `mcp.error`: the server answered a JSON-RPC error; the message has its code.
|
|
95
100
|
* - `mcp.status`: the server answered another HTTP error status, without a JSON-RPC error.
|
|
96
|
-
* - `mcp.lost`: sent, and the answer was lost, so the tool may have run. The model sees it
|
|
97
|
-
*
|
|
101
|
+
* - `mcp.lost`: sent, and the answer was lost, so the tool may have run. The model sees it, and
|
|
102
|
+
* decides whether to call again (R5; it replaced R2b's read-only and idempotent exception).
|
|
98
103
|
* - `mcp.too-large`: the answer passed `MCP_RESULT_MAX_BYTES` (R2b C11), as an HTTP tool's
|
|
99
104
|
* answer is `http.too-large`. The tool ran; its answer was not kept.
|
|
100
105
|
*/
|
|
@@ -110,9 +115,8 @@ export interface McpCallFailure {
|
|
|
110
115
|
/** The request reached the server, so it may have acted on it. */
|
|
111
116
|
readonly sent: boolean;
|
|
112
117
|
readonly retryable: boolean;
|
|
113
|
-
/** With `credential_rejected`: the server
|
|
118
|
+
/** With `credential_rejected`: the server. */
|
|
114
119
|
readonly server?: string;
|
|
115
|
-
readonly vault?: "installation" | "user";
|
|
116
120
|
/** How many credential values the message had in it, replaced (R2b C8). */
|
|
117
121
|
readonly redacted?: number;
|
|
118
122
|
}
|
|
@@ -134,16 +138,16 @@ export declare class McpCallFailed extends Error {
|
|
|
134
138
|
*/
|
|
135
139
|
export declare class CredentialRejected extends McpCallFailed {
|
|
136
140
|
readonly server: string;
|
|
137
|
-
/**
|
|
138
|
-
readonly
|
|
141
|
+
/** Whether a credential of the session's Environment was sent. */
|
|
142
|
+
readonly sent: boolean;
|
|
139
143
|
/**
|
|
140
144
|
* The answer's `WWW-Authenticate`, which may name the server's protected-resource metadata
|
|
141
145
|
* (RFC 9728 `resource_metadata`): a tool preview reads it (R2b C12). Never shown to the model.
|
|
142
146
|
*/
|
|
143
147
|
readonly challenge?: string | undefined;
|
|
144
148
|
constructor(server: string,
|
|
145
|
-
/**
|
|
146
|
-
|
|
149
|
+
/** Whether a credential of the session's Environment was sent. */
|
|
150
|
+
sent: boolean,
|
|
147
151
|
/**
|
|
148
152
|
* The answer's `WWW-Authenticate`, which may name the server's protected-resource metadata
|
|
149
153
|
* (RFC 9728 `resource_metadata`): a tool preview reads it (R2b C12). Never shown to the model.
|
|
@@ -162,9 +166,16 @@ export declare class McpConnectionStale extends McpCallFailed {
|
|
|
162
166
|
}
|
|
163
167
|
/** A tool call that never reached its server: its connection could not be opened. */
|
|
164
168
|
export declare function unreachable(server: string, error: unknown): McpCallFailed;
|
|
169
|
+
/**
|
|
170
|
+
* Where a `tools/call` carries its key (R5.5): MCP has no standard key field, and `_meta` is the
|
|
171
|
+
* spec's place for extra request data. The key is the call's effect id, the same on every send,
|
|
172
|
+
* so a server that reads it can run the call once.
|
|
173
|
+
*/
|
|
174
|
+
export declare const IDEMPOTENCY_META_KEY = "nylorun/idempotency-key";
|
|
165
175
|
/**
|
|
166
176
|
* An SDK `Client` as an `McpClient`. Its answers and failures carry no credential value sent
|
|
167
|
-
* with them (R2b C8), and a failed call is an `McpCallFailed` (C7).
|
|
177
|
+
* with them (R2b C8), and a failed call is an `McpCallFailed` (C7). A keyed call sends its key in
|
|
178
|
+
* `_meta` (R5.5).
|
|
168
179
|
*/
|
|
169
180
|
export declare function sdkClient(client: Client, serverName: string): McpClient;
|
|
170
181
|
export declare function openMcpServer(input: {
|
|
@@ -191,7 +202,6 @@ export declare function serverInstructions(text: unknown, secrets: readonly stri
|
|
|
191
202
|
* collides gets the hash suffix first.
|
|
192
203
|
*/
|
|
193
204
|
export declare function listMcpTools(client: McpClient, input: {
|
|
194
|
-
capabilityId: string;
|
|
195
205
|
serverName: string;
|
|
196
206
|
taken: Set<string>;
|
|
197
207
|
signal?: AbortSignal;
|
|
@@ -212,11 +222,5 @@ export declare function listMcpTools(client: McpClient, input: {
|
|
|
212
222
|
* be opened again (the caller retries once), and with any error that leaves the call's fate
|
|
213
223
|
* unknown.
|
|
214
224
|
*/
|
|
215
|
-
export declare function callMcpTool(client: McpClient, serverToolName: string, args: unknown, options?: McpCallOptions
|
|
216
|
-
|
|
217
|
-
* The tool is read-only or idempotent (`McpToolRecord.annotations`): a call whose answer was
|
|
218
|
-
* lost after it was sent is `mcp.lost` to the model. Otherwise it is left uncertain (Q15).
|
|
219
|
-
*/
|
|
220
|
-
readonly retrySafe?: boolean;
|
|
221
|
-
}): Promise<McpToolOutcome>;
|
|
222
|
-
export declare function diagnosticFromError(capabilityId: string, serverName: string, error: unknown): McpDiagnostic;
|
|
225
|
+
export declare function callMcpTool(client: McpClient, serverToolName: string, args: unknown, options?: McpCallOptions): Promise<McpToolOutcome>;
|
|
226
|
+
export declare function diagnosticFromError(serverName: string, error: unknown): McpDiagnostic;
|
package/dist/mcp/connect.js
CHANGED
|
@@ -29,7 +29,7 @@ export class McpCallFailed extends Error {
|
|
|
29
29
|
/** The failure the gate answered, as the class the loop's own call would have thrown. */
|
|
30
30
|
static from(failure) {
|
|
31
31
|
return failure.code === "credential_rejected" && failure.server !== undefined
|
|
32
|
-
? new CredentialRejected(failure.server, failure.
|
|
32
|
+
? new CredentialRejected(failure.server, !failure.message.includes("holds none"))
|
|
33
33
|
: new McpCallFailed(failure);
|
|
34
34
|
}
|
|
35
35
|
/** The failed tool outcome the model sees. */
|
|
@@ -41,7 +41,6 @@ export class McpCallFailed extends Error {
|
|
|
41
41
|
message: failure.message,
|
|
42
42
|
retryable: failure.retryable,
|
|
43
43
|
...(failure.server === undefined ? {} : { server: failure.server }),
|
|
44
|
-
...(failure.vault === undefined ? {} : { vault: failure.vault }),
|
|
45
44
|
...(failure.redacted ? { redacted: failure.redacted } : {}),
|
|
46
45
|
};
|
|
47
46
|
}
|
|
@@ -54,11 +53,11 @@ export class McpCallFailed extends Error {
|
|
|
54
53
|
*/
|
|
55
54
|
export class CredentialRejected extends McpCallFailed {
|
|
56
55
|
server;
|
|
57
|
-
|
|
56
|
+
sent;
|
|
58
57
|
challenge;
|
|
59
58
|
constructor(server,
|
|
60
|
-
/**
|
|
61
|
-
|
|
59
|
+
/** Whether a credential of the session's Environment was sent. */
|
|
60
|
+
sent,
|
|
62
61
|
/**
|
|
63
62
|
* The answer's `WWW-Authenticate`, which may name the server's protected-resource metadata
|
|
64
63
|
* (RFC 9728 `resource_metadata`): a tool preview reads it (R2b C12). Never shown to the model.
|
|
@@ -66,16 +65,15 @@ export class CredentialRejected extends McpCallFailed {
|
|
|
66
65
|
challenge) {
|
|
67
66
|
super({
|
|
68
67
|
code: "credential_rejected",
|
|
69
|
-
message:
|
|
70
|
-
? `The MCP server '${server}' answered HTTP 401: it
|
|
71
|
-
: `The MCP server '${server}' answered HTTP 401: it
|
|
68
|
+
message: sent
|
|
69
|
+
? `The MCP server '${server}' answered HTTP 401: it rejected the credential from the session's Environment`
|
|
70
|
+
: `The MCP server '${server}' answered HTTP 401: it needs a credential, and the session's Environment holds none for it`,
|
|
72
71
|
sent: true,
|
|
73
72
|
retryable: false,
|
|
74
73
|
server,
|
|
75
|
-
...(vault === undefined ? {} : { vault }),
|
|
76
74
|
});
|
|
77
75
|
this.server = server;
|
|
78
|
-
this.
|
|
76
|
+
this.sent = sent;
|
|
79
77
|
this.challenge = challenge;
|
|
80
78
|
this.name = "CredentialRejected";
|
|
81
79
|
}
|
|
@@ -106,18 +104,26 @@ export function unreachable(server, error) {
|
|
|
106
104
|
}
|
|
107
105
|
/** The operation a request of the transport's fetch belongs to. */
|
|
108
106
|
const tracking = new AsyncLocalStorage();
|
|
109
|
-
function tracked(call, run) {
|
|
107
|
+
function tracked(call, run, traceparent) {
|
|
110
108
|
const tracker = {
|
|
111
109
|
secrets: new Set(),
|
|
112
110
|
sent: false,
|
|
113
111
|
settled: false,
|
|
114
112
|
...(call ? { calling: new AbortController() } : {}),
|
|
113
|
+
...(traceparent ? { traceparent } : {}),
|
|
115
114
|
};
|
|
116
115
|
return tracking.run(tracker, () => run(tracker));
|
|
117
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* Where a `tools/call` carries its key (R5.5): MCP has no standard key field, and `_meta` is the
|
|
119
|
+
* spec's place for extra request data. The key is the call's effect id, the same on every send,
|
|
120
|
+
* so a server that reads it can run the call once.
|
|
121
|
+
*/
|
|
122
|
+
export const IDEMPOTENCY_META_KEY = "nylorun/idempotency-key";
|
|
118
123
|
/**
|
|
119
124
|
* An SDK `Client` as an `McpClient`. Its answers and failures carry no credential value sent
|
|
120
|
-
* with them (R2b C8), and a failed call is an `McpCallFailed` (C7).
|
|
125
|
+
* with them (R2b C8), and a failed call is an `McpCallFailed` (C7). A keyed call sends its key in
|
|
126
|
+
* `_meta` (R5.5).
|
|
121
127
|
*/
|
|
122
128
|
export function sdkClient(client, serverName) {
|
|
123
129
|
return {
|
|
@@ -133,7 +139,10 @@ export function sdkClient(client, serverName) {
|
|
|
133
139
|
callTool: (params, options) => tracked(true, async (tracker) => {
|
|
134
140
|
const lost = tracker.calling.signal;
|
|
135
141
|
try {
|
|
136
|
-
const
|
|
142
|
+
const keyed = options?.key === undefined
|
|
143
|
+
? params
|
|
144
|
+
: { ...params, _meta: { [IDEMPOTENCY_META_KEY]: options.key } };
|
|
145
|
+
const result = (await client.callTool(keyed, undefined, {
|
|
137
146
|
signal: options?.signal ? AbortSignal.any([options.signal, lost]) : lost,
|
|
138
147
|
}));
|
|
139
148
|
// An answer the stream did not count (an `sse` server answers on its event stream).
|
|
@@ -151,7 +160,7 @@ export function sdkClient(client, serverName) {
|
|
|
151
160
|
finally {
|
|
152
161
|
tracker.settled = true;
|
|
153
162
|
}
|
|
154
|
-
}),
|
|
163
|
+
}, options?.traceparent),
|
|
155
164
|
};
|
|
156
165
|
}
|
|
157
166
|
export async function openMcpServer(input) {
|
|
@@ -275,9 +284,7 @@ export async function listMcpTools(client, input) {
|
|
|
275
284
|
continue;
|
|
276
285
|
const { tool, inputSchema } = candidate;
|
|
277
286
|
const outputSchema = tool.outputSchema ? usableSchema(tool.outputSchema) : undefined;
|
|
278
|
-
const annotations = retryHints(tool.annotations);
|
|
279
287
|
tools.push({
|
|
280
|
-
capabilityId: input.capabilityId,
|
|
281
288
|
serverName: input.serverName,
|
|
282
289
|
serverToolName: tool.name,
|
|
283
290
|
name,
|
|
@@ -286,7 +293,6 @@ export async function listMcpTools(client, input) {
|
|
|
286
293
|
: {}),
|
|
287
294
|
inputSchema,
|
|
288
295
|
...(outputSchema === undefined ? {} : { outputSchema }),
|
|
289
|
-
...(annotations === undefined ? {} : { annotations }),
|
|
290
296
|
});
|
|
291
297
|
}
|
|
292
298
|
return { tools, omitted, renamed, disabled, unknownTools };
|
|
@@ -307,6 +313,7 @@ export async function callMcpTool(client, serverToolName, args, options = {}) {
|
|
|
307
313
|
}, {
|
|
308
314
|
...(options.key === undefined ? {} : { key: options.key }),
|
|
309
315
|
...(options.signal ? { signal: options.signal } : {}),
|
|
316
|
+
...(options.traceparent === undefined ? {} : { traceparent: options.traceparent }),
|
|
310
317
|
});
|
|
311
318
|
}
|
|
312
319
|
catch (error) {
|
|
@@ -315,11 +322,7 @@ export async function callMcpTool(client, serverToolName, args, options = {}) {
|
|
|
315
322
|
// The connection's to handle: its holder opens it again and sends the call once more.
|
|
316
323
|
if (error instanceof McpConnectionStale)
|
|
317
324
|
throw error;
|
|
318
|
-
//
|
|
319
|
-
// tool the server says is safe to repeat; any other waits for an operator.
|
|
320
|
-
if (error.code === "mcp.lost" && !options.retrySafe)
|
|
321
|
-
throw new Error(error.message);
|
|
322
|
-
// Otherwise the outcome is known: the model sees it.
|
|
325
|
+
// The model sees it, `mcp.lost` included: sent, then lost, so the tool may have run (R5).
|
|
323
326
|
return error.outcome();
|
|
324
327
|
}
|
|
325
328
|
const { result } = answer;
|
|
@@ -349,14 +352,13 @@ function onlyText(content) {
|
|
|
349
352
|
return (Array.isArray(content) &&
|
|
350
353
|
content.every((part) => !!part && typeof part === "object" && part.type === "text"));
|
|
351
354
|
}
|
|
352
|
-
export function diagnosticFromError(
|
|
355
|
+
export function diagnosticFromError(serverName, error) {
|
|
353
356
|
const credentialIds = error && typeof error === "object" && "credentialIds" in error
|
|
354
357
|
? error.credentialIds
|
|
355
358
|
: undefined;
|
|
356
359
|
const refused = error instanceof Error && error.message.startsWith("MCP authorization refused");
|
|
357
360
|
if (refused) {
|
|
358
361
|
return {
|
|
359
|
-
capabilityId,
|
|
360
362
|
serverName,
|
|
361
363
|
outcome: "refused",
|
|
362
364
|
message: error.message,
|
|
@@ -370,7 +372,6 @@ export function diagnosticFromError(capabilityId, serverName, error) {
|
|
|
370
372
|
code === 401 ||
|
|
371
373
|
(error instanceof Error && /\b401\b/.test(error.message));
|
|
372
374
|
return {
|
|
373
|
-
capabilityId,
|
|
374
375
|
serverName,
|
|
375
376
|
outcome: "failed",
|
|
376
377
|
message: unauthorized
|
|
@@ -418,6 +419,9 @@ function authorizedFetch(server, endpoint, authorize, policy) {
|
|
|
418
419
|
for (const [key, value] of Object.entries(result.headers))
|
|
419
420
|
headers.set(key, value);
|
|
420
421
|
const tracker = tracking.getStore();
|
|
422
|
+
// The tool call's span (R7.4): per request, so a pooled connection carries each call's own.
|
|
423
|
+
if (tracker?.traceparent)
|
|
424
|
+
headers.set("traceparent", tracker.traceparent);
|
|
421
425
|
if (tracker && result.status === "authorized")
|
|
422
426
|
for (const secret of credentialSecrets(result.headers, result.identity))
|
|
423
427
|
tracker.secrets.add(secret);
|
|
@@ -436,7 +440,7 @@ function authorizedFetch(server, endpoint, authorize, policy) {
|
|
|
436
440
|
tracker.sent = true;
|
|
437
441
|
if (response.status === 401) {
|
|
438
442
|
await response.body?.cancel().catch(() => undefined);
|
|
439
|
-
throw new CredentialRejected(server.name, result.status === "authorized"
|
|
443
|
+
throw new CredentialRejected(server.name, result.status === "authorized", response.headers.get("www-authenticate") ?? undefined);
|
|
440
444
|
}
|
|
441
445
|
// The server ended the connection's session: the tool never saw the request.
|
|
442
446
|
if (post && response.status === 404 && headers.has("mcp-session-id")) {
|
|
@@ -622,14 +626,6 @@ function rpcErrorOf(body) {
|
|
|
622
626
|
}
|
|
623
627
|
return undefined;
|
|
624
628
|
}
|
|
625
|
-
/** The hints that calling a tool again is safe, when the server gives any. */
|
|
626
|
-
function retryHints(annotations) {
|
|
627
|
-
const readOnly = annotations?.readOnlyHint === true;
|
|
628
|
-
const idempotent = annotations?.idempotentHint === true;
|
|
629
|
-
if (!readOnly && !idempotent)
|
|
630
|
-
return undefined;
|
|
631
|
-
return { ...(readOnly ? { readOnlyHint: true } : {}), ...(idempotent ? { idempotentHint: true } : {}) };
|
|
632
|
-
}
|
|
633
629
|
function sameOrigin(requestUrl, serverUrl) {
|
|
634
630
|
try {
|
|
635
631
|
return new URL(requestUrl).origin === new URL(serverUrl).origin;
|
package/dist/mcp/pool.d.ts
CHANGED
|
@@ -51,15 +51,14 @@ export declare class McpPool {
|
|
|
51
51
|
call(input: {
|
|
52
52
|
sessionId: string;
|
|
53
53
|
agentId?: string;
|
|
54
|
-
capabilityId: string;
|
|
55
54
|
serverName: string;
|
|
56
55
|
serverToolName: string;
|
|
57
56
|
args: unknown;
|
|
58
57
|
manifest: AgentManifest;
|
|
59
58
|
/** The effect id: a gate runs the call once under it (F4.1). */
|
|
60
59
|
effectId?: string;
|
|
61
|
-
/** The
|
|
62
|
-
|
|
60
|
+
/** The call's span context (R7.4), sent as `traceparent` on a connection this process holds. */
|
|
61
|
+
traceparent?: string;
|
|
63
62
|
signal?: AbortSignal;
|
|
64
63
|
}): Promise<McpToolOutcome>;
|
|
65
64
|
/** The session's connection to the server, opened when there is none, or why it cannot be. */
|
|
@@ -85,7 +84,6 @@ export interface McpServerRef {
|
|
|
85
84
|
readonly sessionId: string;
|
|
86
85
|
/** The agent used as a tool that declares the server; absent for the session's root agent. */
|
|
87
86
|
readonly agentId?: string;
|
|
88
|
-
readonly capabilityId: string;
|
|
89
87
|
readonly serverName: string;
|
|
90
88
|
}
|
|
91
89
|
export interface DeclaredServer {
|
|
@@ -93,9 +91,8 @@ export interface DeclaredServer {
|
|
|
93
91
|
readonly agentId?: string;
|
|
94
92
|
/** The manifest of the agent that declares the server. */
|
|
95
93
|
readonly manifest: AgentManifest;
|
|
96
|
-
readonly capabilityId: string;
|
|
97
94
|
readonly server: McpServerManifest;
|
|
98
95
|
}
|
|
99
96
|
/** Servers of the root agent, then of each agent it uses as a tool. */
|
|
100
97
|
export declare function serversOf(manifest: AgentManifest): DeclaredServer[];
|
|
101
|
-
export declare function findServer(manifest: AgentManifest, agentId: string | undefined,
|
|
98
|
+
export declare function findServer(manifest: AgentManifest, agentId: string | undefined, serverName: string): DeclaredServer | undefined;
|
package/dist/mcp/pool.js
CHANGED
|
@@ -34,7 +34,6 @@ export class McpPool {
|
|
|
34
34
|
if (!taken.has(declared.agentId))
|
|
35
35
|
taken.set(declared.agentId, declaredToolNames(declared.manifest));
|
|
36
36
|
const listed = await listMcpTools(opened.connection.client, {
|
|
37
|
-
capabilityId: declared.capabilityId,
|
|
38
37
|
serverName: declared.server.name,
|
|
39
38
|
taken: taken.get(declared.agentId),
|
|
40
39
|
server: declared.server,
|
|
@@ -43,13 +42,11 @@ export class McpPool {
|
|
|
43
42
|
tools.push(...listed.tools.map((tool) => owned(declared, tool)));
|
|
44
43
|
if (opened.connection.instructions)
|
|
45
44
|
notes.push(owned(declared, {
|
|
46
|
-
capabilityId: declared.capabilityId,
|
|
47
45
|
serverName: declared.server.name,
|
|
48
46
|
instructions: opened.connection.instructions,
|
|
49
47
|
}));
|
|
50
48
|
diagnostics.push({
|
|
51
49
|
...ownerOf(declared),
|
|
52
|
-
capabilityId: declared.capabilityId,
|
|
53
50
|
serverName: declared.server.name,
|
|
54
51
|
outcome: "connected",
|
|
55
52
|
message: [
|
|
@@ -67,7 +64,7 @@ export class McpPool {
|
|
|
67
64
|
}
|
|
68
65
|
catch (error) {
|
|
69
66
|
await opened.connection.close().catch(() => { });
|
|
70
|
-
diagnostics.push(owned(declared, diagnosticFromError(declared.
|
|
67
|
+
diagnostics.push(owned(declared, diagnosticFromError(declared.server.name, error)));
|
|
71
68
|
}
|
|
72
69
|
}
|
|
73
70
|
return {
|
|
@@ -84,7 +81,7 @@ export class McpPool {
|
|
|
84
81
|
const diagnostics = [];
|
|
85
82
|
const seen = new Set();
|
|
86
83
|
for (const tool of input.tools) {
|
|
87
|
-
const key = this.liveKey(input.sessionId, tool.agentId, tool.
|
|
84
|
+
const key = this.liveKey(input.sessionId, tool.agentId, tool.serverName);
|
|
88
85
|
if (seen.has(key))
|
|
89
86
|
continue;
|
|
90
87
|
seen.add(key);
|
|
@@ -95,11 +92,10 @@ export class McpPool {
|
|
|
95
92
|
continue;
|
|
96
93
|
}
|
|
97
94
|
input.signal?.throwIfAborted();
|
|
98
|
-
const declared = findServer(input.manifest, tool.agentId, tool.
|
|
95
|
+
const declared = findServer(input.manifest, tool.agentId, tool.serverName);
|
|
99
96
|
if (!declared) {
|
|
100
97
|
diagnostics.push({
|
|
101
98
|
...(tool.agentId === undefined ? {} : { agentId: tool.agentId }),
|
|
102
|
-
capabilityId: tool.capabilityId,
|
|
103
99
|
serverName: tool.serverName,
|
|
104
100
|
outcome: "failed",
|
|
105
101
|
message: "Declared MCP server is missing from the pinned manifest",
|
|
@@ -116,7 +112,7 @@ export class McpPool {
|
|
|
116
112
|
return diagnostics;
|
|
117
113
|
}
|
|
118
114
|
async call(input) {
|
|
119
|
-
const key = this.liveKey(input.sessionId, input.agentId, input.
|
|
115
|
+
const key = this.liveKey(input.sessionId, input.agentId, input.serverName);
|
|
120
116
|
for (let attempt = 0;; attempt += 1) {
|
|
121
117
|
const live = await this.liveFor(input, key);
|
|
122
118
|
// No connection, so no call was sent: the model sees why, a 401 included (R2b C1, C7).
|
|
@@ -126,8 +122,8 @@ export class McpPool {
|
|
|
126
122
|
try {
|
|
127
123
|
return await callMcpTool(live.connection.client, input.serverToolName, input.args, {
|
|
128
124
|
...(input.effectId === undefined ? {} : { key: input.effectId }),
|
|
129
|
-
...(input.retrySafe ? { retrySafe: true } : {}),
|
|
130
125
|
...(input.signal ? { signal: input.signal } : {}),
|
|
126
|
+
...(input.traceparent === undefined ? {} : { traceparent: input.traceparent }),
|
|
131
127
|
});
|
|
132
128
|
}
|
|
133
129
|
catch (error) {
|
|
@@ -152,7 +148,7 @@ export class McpPool {
|
|
|
152
148
|
const live = this.live.get(key);
|
|
153
149
|
if (live)
|
|
154
150
|
return live;
|
|
155
|
-
const declared = findServer(input.manifest, input.agentId, input.
|
|
151
|
+
const declared = findServer(input.manifest, input.agentId, input.serverName);
|
|
156
152
|
if (!declared)
|
|
157
153
|
throw new Error(`MCP server '${input.serverName}' is not declared`);
|
|
158
154
|
let opening = this.opening.get(key);
|
|
@@ -196,7 +192,7 @@ export class McpPool {
|
|
|
196
192
|
}
|
|
197
193
|
/** Keeps the connection, or closes it when the server is already connected for the session. */
|
|
198
194
|
remember(sessionId, declared, connection) {
|
|
199
|
-
const key = this.liveKey(sessionId, declared.agentId, declared.
|
|
195
|
+
const key = this.liveKey(sessionId, declared.agentId, declared.server.name);
|
|
200
196
|
const existing = this.live.get(key);
|
|
201
197
|
if (existing) {
|
|
202
198
|
if (existing.connection !== connection)
|
|
@@ -204,7 +200,6 @@ export class McpPool {
|
|
|
204
200
|
return existing;
|
|
205
201
|
}
|
|
206
202
|
const live = {
|
|
207
|
-
capabilityId: declared.capabilityId,
|
|
208
203
|
serverName: declared.server.name,
|
|
209
204
|
connection,
|
|
210
205
|
lastUsedAt: this.now(),
|
|
@@ -213,8 +208,8 @@ export class McpPool {
|
|
|
213
208
|
this.live.set(key, live);
|
|
214
209
|
return live;
|
|
215
210
|
}
|
|
216
|
-
liveKey(sessionId, agentId,
|
|
217
|
-
return JSON.stringify([sessionId, agentId ?? null,
|
|
211
|
+
liveKey(sessionId, agentId, serverName) {
|
|
212
|
+
return JSON.stringify([sessionId, agentId ?? null, serverName]);
|
|
218
213
|
}
|
|
219
214
|
async connectDeclared(input, declared) {
|
|
220
215
|
try {
|
|
@@ -222,7 +217,6 @@ export class McpPool {
|
|
|
222
217
|
const connection = await this.options.openRemote({
|
|
223
218
|
sessionId: input.sessionId,
|
|
224
219
|
...ownerOf(declared),
|
|
225
|
-
capabilityId: declared.capabilityId,
|
|
226
220
|
serverName: declared.server.name,
|
|
227
221
|
});
|
|
228
222
|
return { ok: true, connection };
|
|
@@ -242,7 +236,7 @@ export class McpPool {
|
|
|
242
236
|
return {
|
|
243
237
|
ok: false,
|
|
244
238
|
error,
|
|
245
|
-
diagnostic: owned(declared, diagnosticFromError(declared.
|
|
239
|
+
diagnostic: owned(declared, diagnosticFromError(declared.server.name, error)),
|
|
246
240
|
};
|
|
247
241
|
}
|
|
248
242
|
}
|
|
@@ -251,24 +245,16 @@ export class McpPool {
|
|
|
251
245
|
export function serversOf(manifest) {
|
|
252
246
|
const servers = [];
|
|
253
247
|
const add = (agent, agentId) => {
|
|
254
|
-
for (const
|
|
255
|
-
|
|
256
|
-
servers.push({
|
|
257
|
-
...(agentId === undefined ? {} : { agentId }),
|
|
258
|
-
manifest: agent,
|
|
259
|
-
capabilityId: capability.id,
|
|
260
|
-
server,
|
|
261
|
-
});
|
|
248
|
+
for (const server of Object.values(agent.mcpServers ?? {}))
|
|
249
|
+
servers.push({ ...(agentId === undefined ? {} : { agentId }), manifest: agent, server });
|
|
262
250
|
};
|
|
263
251
|
add(manifest);
|
|
264
252
|
for (const child of delegatesOf(manifest))
|
|
265
253
|
add(child.manifest, child.manifest.id);
|
|
266
254
|
return servers;
|
|
267
255
|
}
|
|
268
|
-
export function findServer(manifest, agentId,
|
|
269
|
-
return serversOf(manifest).find((item) => item.agentId === agentId &&
|
|
270
|
-
item.capabilityId === capabilityId &&
|
|
271
|
-
item.server.name === serverName);
|
|
256
|
+
export function findServer(manifest, agentId, serverName) {
|
|
257
|
+
return serversOf(manifest).find((item) => item.agentId === agentId && item.server.name === serverName);
|
|
272
258
|
}
|
|
273
259
|
function ownerOf(declared) {
|
|
274
260
|
return declared.agentId === undefined ? {} : { agentId: declared.agentId };
|
package/dist/mcp/preview.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* A remote MCP server's tools before an agent names it (R2b C12, `POST /v1/tenant/mcp/preview`,
|
|
3
|
-
* `nylorun mcp inspect`,
|
|
3
|
+
* `nylorun mcp inspect`, an Environment's Secrets tab in Studio). It runs where plaintext credentials live:
|
|
4
4
|
* the keys service (`keys/keys.ts`, `previewMcp`), in the gateway or in process.
|
|
5
5
|
*
|
|
6
|
-
* One connection: `initialize` and `tools/list` with the
|
|
6
|
+
* One connection: `initialize` and `tools/list` with the Environment's credential for the
|
|
7
7
|
* URL (its headers, and its `via`), under the Host's address policy, within 15 s; then it closes.
|
|
8
8
|
* It never calls a tool, and sends no identity header: no person asked. A server that answers
|
|
9
9
|
* `401` is `authRequired`, with its RFC 9728 protected-resource metadata when it publishes any,
|
|
@@ -16,7 +16,7 @@ import { openMcpServer } from "./connect.js";
|
|
|
16
16
|
/** How long a preview may take, `initialize` and every `tools/list` page together. */
|
|
17
17
|
export declare const MCP_PREVIEW_TIMEOUT_MS = 15000;
|
|
18
18
|
export interface McpPreviewOptions {
|
|
19
|
-
readonly vault: Pick<VaultService, "authorize" | "
|
|
19
|
+
readonly vault: Pick<VaultService, "authorize" | "listEnvironmentSecrets">;
|
|
20
20
|
/** The Host's address policy for developer URLs (`TenantConfig.delivery`). */
|
|
21
21
|
readonly policy: OutboundPolicy;
|
|
22
22
|
/** Default `MCP_PREVIEW_TIMEOUT_MS`. */
|
package/dist/mcp/preview.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* A remote MCP server's tools before an agent names it (R2b C12, `POST /v1/tenant/mcp/preview`,
|
|
3
|
-
* `nylorun mcp inspect`,
|
|
3
|
+
* `nylorun mcp inspect`, an Environment's Secrets tab in Studio). It runs where plaintext credentials live:
|
|
4
4
|
* the keys service (`keys/keys.ts`, `previewMcp`), in the gateway or in process.
|
|
5
5
|
*
|
|
6
|
-
* One connection: `initialize` and `tools/list` with the
|
|
6
|
+
* One connection: `initialize` and `tools/list` with the Environment's credential for the
|
|
7
7
|
* URL (its headers, and its `via`), under the Host's address policy, within 15 s; then it closes.
|
|
8
8
|
* It never calls a tool, and sends no identity header: no person asked. A server that answers
|
|
9
9
|
* `401` is `authRequired`, with its RFC 9728 protected-resource metadata when it publishes any,
|
|
10
10
|
* read under the same policy. No credential value sent is in the answer (`scrubValues`, C8).
|
|
11
11
|
*/
|
|
12
|
-
import {
|
|
12
|
+
import { McpPreviewRequestSchema, } from "@nylorun/core/contracts";
|
|
13
13
|
import { scrubValues } from "../redact.js";
|
|
14
14
|
import { HttpError } from "../tenant/http.js";
|
|
15
15
|
import { guardedFetch, OutboundRefused } from "../tenant/outbound.js";
|
|
@@ -72,7 +72,6 @@ export async function previewMcpServer(body, options) {
|
|
|
72
72
|
callTool: () => Promise.reject(new Error("A preview never calls a tool")),
|
|
73
73
|
};
|
|
74
74
|
const listed = await listMcpTools(listing, {
|
|
75
|
-
capabilityId: "preview",
|
|
76
75
|
serverName: name,
|
|
77
76
|
taken: new Set(),
|
|
78
77
|
signal,
|
|
@@ -119,28 +118,18 @@ export async function previewMcpServer(body, options) {
|
|
|
119
118
|
}
|
|
120
119
|
}
|
|
121
120
|
/**
|
|
122
|
-
* The
|
|
123
|
-
*
|
|
124
|
-
* them.
|
|
121
|
+
* The credential of Environment `environmentId`'s vault for the URL; none without an
|
|
122
|
+
* Environment (a preview without a credential). An Environment that does not exist is a 404.
|
|
125
123
|
*/
|
|
126
124
|
async function credentialFor(vault, request) {
|
|
127
|
-
|
|
128
|
-
if (request.vaultId !== undefined) {
|
|
129
|
-
const found = await vault.getVault(request.vaultId);
|
|
130
|
-
if (found.ownerUserId !== INSTALLATION_OWNER)
|
|
131
|
-
throw new VaultError(400, "A preview uses an installation vault's credential: no person asked for it");
|
|
132
|
-
vaultIds = [found.id];
|
|
133
|
-
}
|
|
134
|
-
else
|
|
135
|
-
vaultIds = (await vault.listVaults(undefined, { installation: true })).map((found) => found.id);
|
|
136
|
-
if (vaultIds.length === 0)
|
|
125
|
+
if (request.environmentId === undefined)
|
|
137
126
|
return { status: "unauthenticated", url: request.url, headers: {} };
|
|
138
|
-
|
|
127
|
+
// The 404 of a missing Environment, before any request leaves.
|
|
128
|
+
await vault.listEnvironmentSecrets(request.environmentId);
|
|
129
|
+
const result = await vault.authorize({ environmentId: request.environmentId, url: request.url });
|
|
139
130
|
if (result.status !== "refused")
|
|
140
131
|
return result;
|
|
141
|
-
throw new HttpError(409, result.reason
|
|
142
|
-
? "Several installation vaults hold a credential for this URL: name one with vaultId"
|
|
143
|
-
: `The installation vault's credential for this URL cannot be used (${result.reason})`, { code: "request_rejected", details: { reason: result.reason, credentialIds: result.credentialIds } });
|
|
132
|
+
throw new HttpError(409, `Environment ${request.environmentId}'s credential for this URL cannot be used (${result.reason})`, { code: "request_rejected", details: { reason: result.reason, credentialIds: result.credentialIds } });
|
|
144
133
|
}
|
|
145
134
|
/** A preview that listed nothing: `mcp_preview_failed`, with the code a tool call would have had. */
|
|
146
135
|
function failed(server, error, signal, secrets, timeoutMs) {
|