@plurnk/plurnk-mcp 1.24.0 → 1.25.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/.env.defaults +16 -36
- package/README.md +115 -131
- package/SPEC.md +189 -166
- package/dist/McpExecutor.d.ts +3 -3
- package/dist/McpExecutor.d.ts.map +1 -1
- package/dist/McpExecutor.js +18 -26
- package/dist/McpExecutor.js.map +1 -1
- package/dist/Module.d.ts +14 -13
- package/dist/Module.d.ts.map +1 -1
- package/dist/Module.js +191 -188
- package/dist/Module.js.map +1 -1
- package/dist/PluginConfiguration.d.ts +23 -0
- package/dist/PluginConfiguration.d.ts.map +1 -0
- package/dist/PluginConfiguration.js +34 -0
- package/dist/PluginConfiguration.js.map +1 -0
- package/dist/ToolPresentation.d.ts +1 -1
- package/dist/ToolPresentation.d.ts.map +1 -1
- package/dist/ToolPresentation.js +3 -7
- package/dist/ToolPresentation.js.map +1 -1
- package/dist/client.d.ts +8 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +96 -87
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +25 -11
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +151 -310
- package/dist/config.js.map +1 -1
- package/dist/definition.d.ts +3 -0
- package/dist/definition.d.ts.map +1 -0
- package/dist/definition.js +16 -0
- package/dist/definition.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/registry.d.ts +60 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +150 -0
- package/dist/registry.js.map +1 -0
- package/docs/mcp.md +53 -34
- package/package.json +6 -4
package/SPEC.md
CHANGED
|
@@ -172,11 +172,21 @@ conformance stays a separate named gate, never folded into a matrix row.
|
|
|
172
172
|
| stdio | Spawn one exact executable with an explicit argument array and no shell; newline-delimited JSON-RPC is the only stdout/stdin traffic; stderr is diagnostic; shutdown closes stdin, waits, then terminates if necessary |
|
|
173
173
|
| Streamable HTTP | Send one POST per request or notification; accept JSON or SSE responses; close the response stream to cancel; modern connections never open the removed general GET stream |
|
|
174
174
|
|
|
175
|
+
§mcp-endpoint-security **Configured MCP and registry endpoints accept HTTP or HTTPS,
|
|
176
|
+
including private-network hosts.** Transport admission does not relax OAuth:
|
|
177
|
+
the SDK retains token-endpoint TLS enforcement (with its loopback exception),
|
|
178
|
+
issuer/resource binding, PKCE and redirect validation. No transport-policy bypass is supplied.
|
|
179
|
+
|
|
175
180
|
§mcp-stdio-process-ownership A stdio connection owns the complete process group
|
|
176
181
|
created for its server. Ordinary closure forwards stdin EOF and permits a
|
|
177
182
|
bounded graceful exit; an expired shutdown bound or disappearance of the host
|
|
178
183
|
process forcibly terminates the group, including descendants.
|
|
179
184
|
|
|
185
|
+
§mcp-redirect-refused **An HTTP endpoint is never redirected.** Every Streamable HTTP request,
|
|
186
|
+
authorization discovery included, sets `redirect: "manual"`; a 301, 302, 303, 307, or 308 fails the
|
|
187
|
+
connection naming the `Location`. Configured headers therefore never reach another origin
|
|
188
|
+
and the endpoint URL is corrected where it is configured.
|
|
189
|
+
|
|
180
190
|
At the pinned revision, HTTP requests carry matching `MCP-Protocol-Version` and `Mcp-Method`
|
|
181
191
|
headers. Named requests also carry `Mcp-Name`; declared primitive tool
|
|
182
192
|
parameters carry validated `Mcp-Param-*` headers. Header names compare
|
|
@@ -204,169 +214,183 @@ originating distinction in its canonical Problem/result path.
|
|
|
204
214
|
|
|
205
215
|
## §mcp-configuration Configuration
|
|
206
216
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
service definition's enabledness or own an added definition and its enabledness.
|
|
212
|
-
Disabled definitions remain client-visible but contribute no connection,
|
|
213
|
-
Registry, documentation, or resource authority.
|
|
217
|
+
MCP servers are complete connection definitions ({§mcp-server-definition}). Standalone files, plugin components, and environment declarations
|
|
218
|
+
supply the service baseline; live additions belong to the workspace. All use the common resolution
|
|
219
|
+
and lifecycle ({§configuration-definition-resolution}, {§functionality-coordinator}). Disabled definitions remain
|
|
220
|
+
client-visible but contribute no connection, Registry, documentation, or resource authority.
|
|
214
221
|
|
|
215
|
-
§mcp-
|
|
216
|
-
|
|
222
|
+
§mcp-file-configuration **Standalone `mcp.json` files are read-only configuration inputs, not plugins.**
|
|
223
|
+
The module reads `mcp.json` in the directories supplied by {§agent-roots}. Definitions resolve
|
|
224
|
+
by alias, highest precedence first:
|
|
225
|
+
|
|
226
|
+
| Source | Location |
|
|
227
|
+
|---|---|
|
|
228
|
+
| Workspace overlay | Ordinary `mcp (add)` state |
|
|
229
|
+
| Environment | `PLURNK_MCP_<alias>` |
|
|
230
|
+
| Project | `<project>/.agents/mcp.json`, then selected project plugins |
|
|
231
|
+
| Plurnk-only | `$XDG_CONFIG_HOME/plurnk/mcp.json`, then selected Plurnk plugins |
|
|
232
|
+
| Shared global | `~/.agents/mcp.json`, then selected global plugins |
|
|
233
|
+
| Installed npm plugins | Selected plugin bundles in the installed graph |
|
|
234
|
+
|
|
235
|
+
- The document is an object with required `mcpServers`, an object keyed by server alias,
|
|
236
|
+
and an optional string `$schema` editor hint. No schema is fetched. No other top-level fields.
|
|
237
|
+
- Each entry is the owning connection definition without `name`; its map key supplies the name.
|
|
238
|
+
An omitted `type` is inferred from `command` (stdio) or `url` (Streamable HTTP).
|
|
239
|
+
Ambiguous, unsupported, or incomplete entries fail the same definition validator as environment/live inputs.
|
|
240
|
+
- Select the whole winning entry before validating it; never merge fields or fall back from an invalid winner.
|
|
241
|
+
Environment enabledness and tool controls apply independently to file-backed aliases.
|
|
242
|
+
- Missing files contribute nothing. Invalid/unreadable files or selected entries produce a named
|
|
243
|
+
configuration diagnostic under {§configuration-repair-path}, not a daemon exit or an empty catalog.
|
|
244
|
+
- Inspection reports `kind: file`, the absolute file `source`, and the entry's JSON Pointer `reference`.
|
|
245
|
+
Normal pre-turn refresh observes file edits and deletion; `list` never starts a server or writes a file.
|
|
246
|
+
Workspace removal restores the current inherited definition and controls.
|
|
247
|
+
- File entries retain {§mcp-launch-directory} and {§mcp-launch-environment}; a file's location is
|
|
248
|
+
not a subprocess working directory. Plugin-root configuration and packaging are separate.
|
|
249
|
+
|
|
250
|
+
`mcpServers` follows the common MCP catalog shape, also used by
|
|
251
|
+
[MCP Inspector](https://github.com/modelcontextprotocol/inspector/blob/main/docs/mcp-server-configuration.md).
|
|
252
|
+
The discovery locations are Plurnk's supported cross-client convention, not an MCP wire requirement.
|
|
253
|
+
|
|
254
|
+
§mcp-plugin-configuration **Plugin MCP components use their format's interpretation, not the native file dialect.**
|
|
255
|
+
The validated components supplied by {§agent-plugins-hosting} become ordinary configured MCP
|
|
256
|
+
definitions. The adapter carries the canonical plugin root and persistent data directory as
|
|
257
|
+
interpretation context under {§functionality-adapter}; inspection preserves symbolic definitions.
|
|
258
|
+
|
|
259
|
+
| Field or boundary | Agent Plugins v1 behavior |
|
|
260
|
+
|---|---|
|
|
261
|
+
| `command` | Bare executable or plugin-root-relative `./` path; no expansion |
|
|
262
|
+
| `args`, `env`, `cwd` | One nonrecursive expansion of `${PLUGIN_ROOT}` and `${PLUGIN_DATA}` only; all other placeholder-like text stays literal |
|
|
263
|
+
| Omitted `cwd` | Plugin root; explicit plugin/data cwd is contained within its corresponding resolved root |
|
|
264
|
+
| Subprocess environment | Ordinary admitted operator/workspace environment, then configured values, then authoritative `PLUGIN_ROOT` and `PLUGIN_DATA` |
|
|
265
|
+
| Data | Created before launch; preserved across disable, removal of an override, and in-place plugin updates |
|
|
266
|
+
| Filesystem containment | Rechecked before each subprocess launch, including existing parents of missing paths |
|
|
267
|
+
| HTTP URL and headers | Literal; client-owned protocol headers win; existing no-redirect rule applies |
|
|
268
|
+
| Unsupported transport or name | Skip the entry with a configuration notice; never rename, reinterpret, or disable independent entries |
|
|
269
|
+
|
|
270
|
+
Supported transports are stdio and Streamable HTTP, not legacy SSE. Server names must meet
|
|
271
|
+
Plurnk's runtime alias grammar `[a-z][a-z0-9-]*`; other standard map keys are explicitly unsupported.
|
|
272
|
+
The standard loader owns component validation and narrow failure boundaries
|
|
273
|
+
({§agent-plugins-mcp-entries}, {§agent-plugins-components}). Connection failures remain ordinary
|
|
274
|
+
unavailable outcomes. Current adapter notices join client/model configuration diagnostics;
|
|
275
|
+
source repair replaces them, rather than retaining a historical warning.
|
|
276
|
+
|
|
277
|
+
§mcp-activation-isolation **Cold endpoint failure is capability-local.** An enabled server
|
|
217
278
|
that cannot connect or complete discovery during workspace activation remains
|
|
218
279
|
enabled and client-visible as `unavailable`. It publishes no runtime, tools,
|
|
219
280
|
resources, or documentation and cannot prevent other capabilities or the daemon
|
|
220
281
|
from starting or serving dormant workers. Enabling that already-enabled alias is an explicit reconnect
|
|
221
282
|
attempt; failure preserves the unavailable snapshot, while success atomically
|
|
222
|
-
replaces it.
|
|
223
|
-
|
|
283
|
+
replaces it. An interactive enable continues to reject an unavailable candidate
|
|
284
|
+
without changing durable state.
|
|
224
285
|
|
|
225
286
|
| Variable | Contract |
|
|
226
287
|
|---|---|
|
|
227
|
-
| `PLURNK_MCP_<
|
|
228
|
-
| `PLURNK_MCP_<
|
|
229
|
-
| `PLURNK_MCP_<
|
|
230
|
-
| `
|
|
231
|
-
| `
|
|
232
|
-
| `
|
|
233
|
-
| `
|
|
234
|
-
| `
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
288
|
+
| `PLURNK_MCP_<alias>` | Complete `McpServerDefinition` JSON; transport and authentication replace together |
|
|
289
|
+
| `PLURNK_MCP_ENABLED`, `PLURNK_MCP_<alias>_ENABLED` | Shared default and per-alias flags ({§resource-environment}); declared servers default enabled |
|
|
290
|
+
| `PLURNK_MCP_<alias>_TOOLS` | Optional JSON array of exact enabled tool names; absent or empty enables every listed tool, and `[]` enables none |
|
|
291
|
+
| `PLURNK_MCP_EXPANDED` | JSON array of aliases whose tool invocations are surveyed at turn 0 ({§tools-resource-materialization}); absent or `[]` expands none |
|
|
292
|
+
| `PLURNK_MCP_CONNECT_TIMEOUT` | Positive integer milliseconds for setup and each complete catalog walk |
|
|
293
|
+
| `PLURNK_MCP_REQUEST_TIMEOUT` | Positive integer milliseconds for the whole operation |
|
|
294
|
+
| `PLURNK_MCP_REGISTRY_URL` | HTTP(S) registry discovery endpoint; empty disables registry search |
|
|
295
|
+
| `PLURNK_MCP_REGISTRY_LIMIT` | Positive integer result bound |
|
|
296
|
+
|
|
297
|
+
Aliases and controls follow {§resource-environment}. Malformed definitions or controls fail
|
|
298
|
+
configuration by variable name, even when disabled or not yet associated with a resource.
|
|
299
|
+
Authentication belongs in the definition, never in separate bearer/OAuth environment companions.
|
|
300
|
+
The package's offline validator composes these same readers
|
|
301
|
+
({§operator-config-offline-validation}); it performs no connection or secret resolution.
|
|
302
|
+
Runtime configuration errors leave the manager inspectable without publishing MCP
|
|
303
|
+
capabilities or preventing unrelated model work ({§configuration-repair-path}).
|
|
304
|
+
|
|
305
|
+
§mcp-definitions **A server is not a plugin installation.** `add` persists a workspace
|
|
306
|
+
connection definition; `remove` removes that definition through the common coordinator.
|
|
307
|
+
Neither writes nor deletes project, user or global configuration files. MCP management
|
|
308
|
+
and preparation do not enumerate or install Agent Plugins. Plugin integration is a separate
|
|
309
|
+
configuration-source concern, not an MCP definition or lifecycle.
|
|
310
|
+
|
|
311
|
+
| Transport | Launch or connection |
|
|
312
|
+
|---|---|
|
|
313
|
+
| `stdio` | Spawn `command` with `args`, without a shell. Bare executable names use PATH; relative executable paths use the working directory. `args`, `env` and explicit `cwd` expand `${NAME}` references once against the workspace-composed operator environment. |
|
|
314
|
+
| `streamable-http` | Connect to `url` with `headers` and `authorization` from that same definition. Header references expand at connection time. Protocol-generated headers are transport-owned; application authentication headers are retained. Declaring both structured authorization and an Authorization header is invalid. No redirect is followed ({§mcp-redirect-refused}). |
|
|
315
|
+
|
|
316
|
+
§mcp-launch-directory **An implicit working directory is workspace-owned state, not the project.**
|
|
317
|
+
Core's {§module-workspace-directory} supplies a stable directory isolated by workspace and MCP alias.
|
|
318
|
+
An explicit `cwd` must resolve to an absolute path and overrides that directory; it is not confined
|
|
319
|
+
to an installation root. The caller owns provisioning an explicit directory. Runtime paths and
|
|
320
|
+
resolved environment values are never copied back into a stored definition. Disabling or removing
|
|
321
|
+
a server closes its connection, not its retained state directory or saved results.
|
|
322
|
+
|
|
323
|
+
A tool whose `annotations.readOnlyHint` is true takes the `read` effect; every other tool keeps the
|
|
324
|
+
conservative `host` effect ({§mcp-model-projection}).
|
|
325
|
+
|
|
326
|
+
§mcp-summary-derivation **Orientation prefers the server's own purpose over display
|
|
251
327
|
labels; no capabilities are inferred.** Blank values fall through:
|
|
252
328
|
|
|
253
329
|
| Summary | Precedence, highest first |
|
|
254
330
|
|---|---|
|
|
255
|
-
| Server | `
|
|
256
|
-
| Tool | `
|
|
331
|
+
| Server | `serverInfo.description` → `instructions` → `serverInfo.title` → effective admitted tool-name list → server alias |
|
|
332
|
+
| Tool | `description` → `title` → `annotations.title` → tool name |
|
|
257
333
|
|
|
258
334
|
Derived prose is whitespace-normalized, limited to its first sentence, and
|
|
259
|
-
clipped within 80 characters plus an ellipsis, preferring a word boundary.
|
|
260
|
-
one-line overrides remain intact. Full server instructions remain authored
|
|
335
|
+
clipped within 80 characters plus an ellipsis, preferring a word boundary. Full server instructions remain authored
|
|
261
336
|
Markdown in the family document's runtime `details`, available on demand;
|
|
262
337
|
turn0 surveys only the compact summary/invocations. Full tool descriptions
|
|
263
338
|
remain in the linked input-contract documents. With tools, the runtime declares
|
|
264
339
|
`{ from: "tools", description }`: purpose annotates rather than replaces the
|
|
265
340
|
complete effective menu in the family Summary and survey row
|
|
266
|
-
({§scheme-catalog-aside}). Without
|
|
341
|
+
({§scheme-catalog-aside}). Without a stated purpose the menu stands alone. The tool
|
|
267
342
|
doc's Summary section IS the invocation form
|
|
268
343
|
```` ```server (tool) <!-- one-liner --> ````, so the discovery row teaches the
|
|
269
|
-
call ({§tools-resource-materialization}).
|
|
270
|
-
references like every other companion.
|
|
344
|
+
call ({§tools-resource-materialization}).
|
|
271
345
|
|
|
272
|
-
§mcp-
|
|
273
|
-
the
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
| `http` + bearer | above plus `authorization: { type: "bearer", token: "${NAME}" }` | — |
|
|
280
|
-
| `http` + interactive OAuth / CIMD preferred | above plus `authorization: { type: "oauth", redirectUrl, clientMetadataUrl }` | `scope`; DCR remains the server-advertised fallback when CIMD is unavailable |
|
|
281
|
-
| `http` + interactive OAuth / pre-registered | above plus `authorization: { type: "oauth", redirectUrl, clientId, clientSecret: "${NAME}" }` | `scope` |
|
|
282
|
-
| `http` + interactive OAuth / DCR fallback only | above plus `authorization: { type: "oauth", redirectUrl }` | `scope` |
|
|
283
|
-
| `http` + client credentials | above plus `authorization: { type: "client-credentials", clientId, clientSecret: "${NAME}" }` | `scope`; `issuer` binds the credential to its authorization server ({§oauth-client-credentials}) |
|
|
284
|
-
|
|
285
|
-
`tools` absent enables the complete listed set; `[]` enables none. `read` is an
|
|
286
|
-
exact subset of the enabled set. A credential field is one complete symbolic
|
|
287
|
-
environment reference, not a copied token. Other string-valued `headers`,
|
|
288
|
-
`env`, `cwd`, and argument values may contain symbolic references and are
|
|
289
|
-
expanded only while preparing a connection. The unexpanded definition is the
|
|
290
|
-
only durable form. Interactive OAuth tokens, PKCE verifier, issuer-bound
|
|
291
|
-
discovery state, and authorization callback state remain process-memory
|
|
292
|
-
credentials; a restart reconstructs the attachment as authorization-required
|
|
293
|
-
instead of writing secrets into SQLite.
|
|
294
|
-
|
|
295
|
-
The contracts-owned `McpServerDefinition` is the exact definition one `add`
|
|
296
|
-
accepts and the coordinator persists; a client composes it from its target
|
|
297
|
-
(an absolute HTTP(S) URL selects Streamable HTTP, anything else is one exact
|
|
298
|
-
stdio executable) and its options (`args`, `cwd`, `env`, `headers`,
|
|
299
|
-
`authorization`, `tools`, `read`). The normalized definition rejects
|
|
300
|
-
transport-inapplicable options before any connection work.
|
|
301
|
-
|
|
302
|
-
§mcp-configuration-cascade MCP server configuration has one field-wise
|
|
303
|
-
precedence order: service environment, then the workspace's durable definition.
|
|
304
|
-
Arrays and maps replace their lower value instead of appending or merging.
|
|
305
|
-
An explicitly empty environment target omits that service definition, its
|
|
306
|
-
companions (including summaries), and its inherited `ENABLED`/`EXPANDED`
|
|
307
|
-
selections. Companion values are neither parsed nor expanded. It does not
|
|
308
|
-
remove a workspace-owned definition or prohibit adding one. Genuinely undeclared
|
|
309
|
-
aliases and case-fold collisions still fail validation.
|
|
310
|
-
Client configuration is not a live layer: the contracts-owned
|
|
311
|
-
`{§mcp-configuration-overlay}` enters only as the `configuration` of a
|
|
312
|
-
`discover` query, is parsed by the same owner and path as service environment
|
|
313
|
-
declarations, and yields inert candidates with client-configuration provenance
|
|
314
|
-
({§mcp-discovery}); adding one persists a complete, normalized, unexpanded
|
|
315
|
-
workspace definition. Thus later enablement needs neither the originating client
|
|
316
|
-
nor its configuration file, and symbolic credentials remain resolvable only by
|
|
317
|
-
the service at connection preparation.
|
|
346
|
+
§mcp-server-settings **Independent behavior settings do not change connection definitions.**
|
|
347
|
+
`<alias>_TOOLS` narrows the effective tool catalog. A valid control may precede its definition,
|
|
348
|
+
without creating a server; all control values are validated immediately ({§resource-environment}).
|
|
349
|
+
Bearer and OAuth settings belong to the whole HTTP definition. Secrets in structured authorization
|
|
350
|
+
are `${NAME}` references expanded only while preparing the connection. Interactive tokens,
|
|
351
|
+
PKCE verifiers and callback state remain in memory; restart reconstructs an authorization-required
|
|
352
|
+
attachment, never a stored credential.
|
|
318
353
|
|
|
319
354
|
### §mcp-module The MCP family beneath the coordinator
|
|
320
355
|
|
|
321
|
-
§mcp-launch-environment
|
|
322
|
-
environment
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
copied into
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
family with the common semantics, durable state, and publication; this module
|
|
360
|
-
registers the family adapter and owns protocol truth beneath it. `available`
|
|
361
|
-
is the service environment with its `PLURNK_MCP_ENABLED` defaults; `admit`
|
|
362
|
-
validates one exact definition and binds the alias to its `name`; `prepare`
|
|
363
|
-
connects the enabled set, reusing unchanged live attachments, and returns one
|
|
364
|
-
executor family and resource facet per connected server, one outcome per alias
|
|
365
|
-
(`active` with catalog detail — negotiated protocol version, server identity,
|
|
366
|
-
capabilities, tool names, resource and prompt counts — `unavailable` with its
|
|
367
|
-
exact Problem, or `authorization-required` with its URL), and a two-phase
|
|
368
|
-
snapshot: `commit` closes connections the new set no longer uses and records
|
|
369
|
-
pending authorizations; `abort` closes only what the attempt opened.
|
|
356
|
+
§mcp-launch-environment **A server inherits the operator's environment.** A stdio server
|
|
357
|
+
starts with the operator's environment without plurnk's own secrets ({§exec-env-scoped}), as every MCP
|
|
358
|
+
client launches one: stdio servers read their credentials from the environment. The model's command
|
|
359
|
+
ceiling is not its base. The workspace layer ({§workspace-env}) applies on top, its values and
|
|
360
|
+
withholdings included, never the invoking worker's overrides; the definition's `env` follows. Definition references resolve against the operator environment with the same workspace
|
|
361
|
+
entries and masks applied, and resolved ambient values are never copied into definitions. A running server keeps its launch environment:
|
|
362
|
+
`disable` and `enable` restart it after an environment change, and there is no automatic restart or
|
|
363
|
+
stale-configuration state. HTTP servers have no local process environment.
|
|
364
|
+
|
|
365
|
+
§mcp-management-actions MCP is one family of workspace Functionality ({§functionality-coordinator}). The
|
|
366
|
+
coordinator publishes `workspace.mcp.list | discover | add | enable | disable | remove` and the model's
|
|
367
|
+
`mcp` executable fence family with the common semantics, durable state, and publication; this module
|
|
368
|
+
registers the family adapter and owns protocol truth beneath it. `available` is the configured service baseline; `add` and `remove` change workspace state ({§mcp-definitions}), and
|
|
369
|
+
`discover` searches the MCP Registry ({§mcp-registry-discovery}). `prepare` connects the enabled set, reusing
|
|
370
|
+
unchanged live attachments, and returns one executor family and resource facet per connected server,
|
|
371
|
+
one outcome per alias (`active` with catalog detail — negotiated protocol version, server identity,
|
|
372
|
+
capabilities, tool names, resource and prompt counts — `unavailable` with its exact Problem, or
|
|
373
|
+
`authorization-required` with its URL), and a two-phase snapshot: `commit` closes connections the new
|
|
374
|
+
set no longer uses and records pending authorizations; `abort` closes only what the attempt opened.
|
|
375
|
+
|
|
376
|
+
§mcp-registry-discovery **`discover` searches the MCP Registry.** `{ query }` asks
|
|
377
|
+
`PLURNK_MCP_REGISTRY_URL` (API v0.1) for one page of at most `PLURNK_MCP_REGISTRY_LIMIT` servers, each at
|
|
378
|
+
its latest version, whose names match. Each npm, PyPI, NuGet or OCI package with a stdio transport
|
|
379
|
+
becomes a stdio entry run as the registry's own examples run it (`npx -y`, `uvx`, `dnx`, or
|
|
380
|
+
`docker run -i --rm` passing each declared variable through), and each Streamable HTTP remote becomes a
|
|
381
|
+
URL entry with its non-secret literal headers. An entry that needs a person's input first, such as a
|
|
382
|
+
template variable or a required argument with no value, has none. A candidate is a complete definition;
|
|
383
|
+
its summary names the environment variables and headers the
|
|
384
|
+
server needs, and its provenance names the registry and the server's `name@version`. `source` and
|
|
385
|
+
`configuration` are not registry queries and are refused.
|
|
386
|
+
|
|
387
|
+
| Discovery, admission, or installation condition | Problem | Status |
|
|
388
|
+
|---|---|---|
|
|
389
|
+
| No registry is configured | `registry-not-configured` | 501 |
|
|
390
|
+
| The registry is unreachable, fails, or answers malformed | `discover-failed`, retryable | 502 |
|
|
391
|
+
| `discover` names a `source` or client `configuration` | `source-unsupported`, `configuration-unsupported` | 400 |
|
|
392
|
+
| The complete connection definition is invalid | `definition-invalid` | 400 |
|
|
393
|
+
| The alias differs from the definition's name | `alias-mismatch` | 400 |
|
|
370
394
|
|
|
371
395
|
Two protocol continuations remain MCP-registered workspace actions beneath the
|
|
372
396
|
common grammar:
|
|
@@ -376,15 +400,6 @@ common grammar:
|
|
|
376
400
|
| `workspace.mcp.oauth.complete` | `alias`, `callbackUrl` | State- and issuer-validates one pending interactive callback through the SDK, completes connection preparation, and re-enables the alias through the coordinator ({§oauth-continuation}); the result is the common mutation result. |
|
|
377
401
|
| `workspace.mcp.complete` | `server`, `ref`, `argument`; optional `context` | Requests negotiated prompt/resource-template argument completion for a client-owned interaction. |
|
|
378
402
|
|
|
379
|
-
§mcp-discovery Discovery is inert. `configuration` (a client's own
|
|
380
|
-
`PLURNK_MCP_*` overlay) becomes candidates without connecting; `source` — an
|
|
381
|
-
absolute HTTP(S) URL or one whitespace-separated command line — is probed at the
|
|
382
|
-
negotiated revision for its catalog and released, yielding one candidate with
|
|
383
|
-
direct-target provenance (an authorization challenge yields the candidate with
|
|
384
|
-
that fact in its summary); a bare `query` requires a configured downstream
|
|
385
|
-
registry and is `501 registry-not-configured` until one exists. No candidate
|
|
386
|
-
is installed, persisted, enabled, or executed by discovery.
|
|
387
|
-
|
|
388
403
|
Expected preparation failures cross the boundary as MCP-management Problems
|
|
389
404
|
rather than generic failures; an explicit client action rejects them and a
|
|
390
405
|
Worker's own accepted mutation publishes them as unavailable
|
|
@@ -392,7 +407,9 @@ Worker's own accepted mutation publishes them as unavailable
|
|
|
392
407
|
|
|
393
408
|
| Endpoint condition | Problem |
|
|
394
409
|
|---|---|
|
|
395
|
-
| Cannot connect or complete discovery/catalog preparation at the negotiated revision | `502 server-unavailable`, retryable; names the server and
|
|
410
|
+
| Cannot connect or complete discovery/catalog preparation at the negotiated revision | `502 server-unavailable`, retryable; names the server and its `type` without exposing credentials |
|
|
411
|
+
| The endpoint answers with a redirect | `502 server-redirected`, non-retryable ({§mcp-redirect-refused}) |
|
|
412
|
+
| An operator setting of the alias is invalid | `422 server-settings-invalid`, non-retryable ({§mcp-server-settings}) |
|
|
396
413
|
| Client-credentials grant rejected by the authorization server | `502 oauth-client-credentials-failed`, non-retryable; names the server and client id, never the secret ({§oauth-client-credentials}) |
|
|
397
414
|
|
|
398
415
|
Resource and prompt failures retain one caught remote diagnostic only through
|
|
@@ -429,32 +446,32 @@ as an accidental failure.
|
|
|
429
446
|
|
|
430
447
|
| Journey point | Behaviour |
|
|
431
448
|
|---|---|
|
|
432
|
-
| Pending authorization | One pending candidate per `(workspace, alias)`; a new
|
|
449
|
+
| Pending authorization | One pending candidate per `(workspace, alias)`; a new challenge or customized enable cancels and replaces it. A callback from a superseded attempt fails state validation instead of cross-completing. |
|
|
433
450
|
| Client disconnect | Does not touch the pending candidate; it can still be completed, or replaced by a fresh request. |
|
|
434
451
|
| Daemon restart during pending | The candidate is lost: nothing was durable, no attachment publishes, and `oauth.complete` answers `404 oauth-not-pending`. Start authorization again. |
|
|
435
452
|
| Daemon restart after authorization | The durable definition rehydrates but tokens are gone; the attachment publishes `authorization-required` and enable returns status `202` with `definition.authorization.url` in the common mutation result. The operator reauthorizes. |
|
|
436
453
|
| Token expiry | An expired access token surfaces as one unauthorized response; the SDK re-acquires via `refresh_token` when one was issued, otherwise re-enters interactive authorization. |
|
|
437
454
|
| Refresh | Happens only against the issuer bound during the original authorization; the refreshed token replaces the in-memory token. |
|
|
438
455
|
| Workspace disable/remove | Closes the attachment and clears its pending candidate; no durable secret deletion is needed because nothing secret is durable. |
|
|
439
|
-
| Server replacement |
|
|
456
|
+
| Server replacement | Publication discards any superseded pending connection and releases its residency, including replacement by a definition without OAuth. Completion of an unpublished configuration drift fails `409 oauth-target-conflict` instead of replaying a stale snapshot. |
|
|
440
457
|
| Cross-authorization protection | Candidates are keyed by `(workspace, alias)`; callback state, PKCE, and issuer are validated by the SDK against the attempt that created them, so no other workspace, alias, or attempt can complete this authorization. |
|
|
441
458
|
|
|
442
459
|
## §oauth-client-credentials Client-credentials grant adoption
|
|
443
460
|
|
|
444
461
|
The `client-credentials` arm of `McpServerDefinition.authorization` adopts the
|
|
445
462
|
official `io.modelcontextprotocol/oauth-client-credentials` extension's
|
|
446
|
-
client-secret form faithfully:
|
|
447
|
-
client-credentials grant advertises the extension capability in
|
|
463
|
+
client-secret form faithfully: a connection whose definition holds a
|
|
464
|
+
client-credentials grant ({§mcp-server-settings}) advertises the extension capability in
|
|
448
465
|
`clientCapabilities.extensions`; connections without one never claim it. The
|
|
449
466
|
grant uses `client_secret_basic` authentication with `grant_type
|
|
450
|
-
client_credentials`.
|
|
467
|
+
client_credentials`. The setting's optional `scope` is passed to
|
|
451
468
|
the token request. The credential itself is one complete symbolic environment
|
|
452
469
|
reference (`clientSecret: "${NAME}"`), expanded only while preparing the
|
|
453
470
|
connection; it is never stored in SQLite, logged, or echoed in Problems.
|
|
454
471
|
|
|
455
472
|
| Aspect | Behaviour |
|
|
456
473
|
|---|---|
|
|
457
|
-
| Issuer binding | The
|
|
474
|
+
| Issuer binding | The setting's optional `issuer` is passed as the SDK provider's `expectedIssuer`, stamping the credential with its authorization server so SEP-2352 issuer checks refuse to send it elsewhere. Absent, the SDK's legacy no-binding behaviour applies. |
|
|
458
475
|
| Token lifetime | Token refresh is 401-triggered by the SDK client: an expired access token surfaces as one unauthorized response, the provider re-fetches with the stored credential, and the request is retried. Proactive expiry scheduling is a client-internal optimization, not a wire requirement; Plurnk does not wrap the SDK with its own scheduler. |
|
|
459
476
|
| Rotation | `clientSecret` resolution happens per connection preparation, so rotating the operator environment value takes effect on the next preparation of the server. |
|
|
460
477
|
| Errors | A rejected grant crosses the action boundary as `502 oauth-client-credentials-failed`, non-retryable, naming the server and client id only; SDK OAuth error text is never echoed. Other connection failures keep the generic `server-unavailable` allocation. |
|
|
@@ -513,6 +530,9 @@ boundaries; neither closes or replaces the committed connection.
|
|
|
513
530
|
| Disable/remove, workspace cooling, or shutdown | Retires obsolete refresh timers and invalidations. |
|
|
514
531
|
|
|
515
532
|
MCP participates in core Functionality residency ({§module-workspace-residency}).
|
|
533
|
+
Preparation reports the current server alias through the coordinator's activity
|
|
534
|
+
contract ({§functionality-preparation-visibility}); inspection never starts a
|
|
535
|
+
connection ({§functionality-inspection}).
|
|
516
536
|
Every tool call and Task retains the workspace from executor entry through its
|
|
517
537
|
terminal result; an interactive OAuth candidate retains it until completion,
|
|
518
538
|
replacement, cancellation, or module shutdown. Catalog refresh timers are
|
|
@@ -533,8 +553,11 @@ commit leaves the durable definition, connection, Registry, docs, and resource
|
|
|
533
553
|
authority unchanged. Materialization and registration inspect the complete
|
|
534
554
|
owning operation result; a non-success preserves its original Problem.
|
|
535
555
|
|
|
536
|
-
§mcp-connection-shutdown
|
|
537
|
-
active requests, including client-input waits.
|
|
556
|
+
§mcp-connection-shutdown Module `stop()` prevents new work and aborts each connection's
|
|
557
|
+
active requests, including client-input waits. Discovery and preparation recheck
|
|
558
|
+
admission after asynchronous environment resolution, before owning a new connection;
|
|
559
|
+
connection startup rechecks after asynchronous directory resolution before opening
|
|
560
|
+
its transport. Their protocol cleanup settles
|
|
538
561
|
before the extension channel or connected transport closes, so a created Task
|
|
539
562
|
can receive `tasks/cancel`. Concurrent closers await the same settlement.
|
|
540
563
|
Candidates still negotiating and standalone OAuth transports close immediately.
|
|
@@ -586,6 +609,11 @@ does not weaken this uncertain-outcome boundary.
|
|
|
586
609
|
remote diagnostic as structured extensions. Their prose states only the failed
|
|
587
610
|
boundary fact; it neither repeats those fields nor infers whether the remote
|
|
588
611
|
effect occurred.
|
|
612
|
+
For `isError: true`, nonblank text content blocks, in order and joined by
|
|
613
|
+
newlines, supply `diagnostic` under the existing executor error-detail bound.
|
|
614
|
+
Nontext parts and structured data are not interpreted as explanations. With no
|
|
615
|
+
text explanation the extension is absent; successful results do not acquire one.
|
|
616
|
+
The complete result and passive output remain unchanged ({§mcp-result-content}).
|
|
589
617
|
|
|
590
618
|
§mcp-trailing-aside A tool call's body is one JSON object. HTML comments after
|
|
591
619
|
that object are the writer's aside, not arguments: when the body does not parse
|
|
@@ -696,7 +724,7 @@ shallow required-field previews and alias-scoped schema links. Each linked child
|
|
|
696
724
|
preserves the complete remote description and raw input schema, without
|
|
697
725
|
reconstructing property tables or expanding nested constraints into the preview.
|
|
698
726
|
Output schemas do not enter model teaching; the returned value remains ordinary evidence. Disabled names
|
|
699
|
-
appear in
|
|
727
|
+
appear in no model teaching, and there is no MCP-specific FIND,
|
|
700
728
|
READ, authority-root, or other model discovery mechanism for tools.
|
|
701
729
|
|
|
702
730
|
Core validates the exact target and the selected tool's invocation before
|
|
@@ -707,10 +735,9 @@ contain resources and resource templates, never tools. Tool results become
|
|
|
707
735
|
ordinary Plurnk entries and channels, so slicing, tags, curation, notices, and
|
|
708
736
|
Problems need no MCP-specific parallel mechanism.
|
|
709
737
|
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
ordinary proposal policy. Effect classification receiving an unregistered
|
|
738
|
+
A configured server's `annotations.readOnlyHint` supplies its tool's declared effect:
|
|
739
|
+
a tool marked read-only takes the executor `read` effect; every other enabled
|
|
740
|
+
tool remains `host` and therefore uses the ordinary proposal policy. Effect classification receiving an unregistered
|
|
714
741
|
target is an internal contract violation rather than a conservative guess.
|
|
715
742
|
|
|
716
743
|
## §mcp-conformance Conformance authority
|
|
@@ -736,12 +763,7 @@ stdio/Streamable HTTP servers are composition evidence only.
|
|
|
736
763
|
| `invalid-tool-arguments` | 400 | The tool arguments are not one JSON object. Recovery: One JSON object per MCP tool call; a second call is a second fence. |
|
|
737
764
|
| `oauth-client-credentials-failed` | 502 | MCP server '*name*' rejected the client-credentials grant; check the configured client credentials and issuer. |
|
|
738
765
|
| `parameters-invalid` | 400 | Unsupported parameter(s): *names*. |
|
|
739
|
-
| `
|
|
740
|
-
| `discover-failed` | 502 | MCP target '*source*' could not be inspected. |
|
|
741
|
-
| `registry-not-configured` | 501 | MCP registry search requires a configured downstream registry; none is configured. Recovery: Discover an explicit source (URL or command) or configure a registry. |
|
|
742
|
-
| `env-transport` | 400 | An HTTP MCP server has no local process environment. |
|
|
743
|
-
| `definition-invalid` | 400 | The MCP server definition is invalid. |
|
|
744
|
-
| `alias-mismatch` | 400 | Alias '*alias*' must equal the definition's name '*name*'. |
|
|
766
|
+
| `server-settings-invalid` | 422 | MCP server '*name*' has invalid operator settings: *cause*. |
|
|
745
767
|
| `server-busy` | 409 | MCP server '*name*' has *n* active request(s). |
|
|
746
768
|
| `obsolete-connection-close-failed` | 500 | The MCP capability change committed, but an obsolete connection did not close cleanly. |
|
|
747
769
|
| `oauth-not-pending` | 404 | MCP server '*alias*' has no pending OAuth authorization. |
|
|
@@ -750,3 +772,4 @@ stdio/Streamable HTTP servers are composition evidence only.
|
|
|
750
772
|
| `server-not-connected` | 409 | MCP server '*name*' is not connected for this workspace. |
|
|
751
773
|
| `completion-parameters-invalid` | 400 | MCP completion requires 'ref' and 'argument' objects. |
|
|
752
774
|
| `server-unavailable` | 502 | Configured MCP server '*name*' is unavailable. |
|
|
775
|
+
| `server-redirected` | 502 | MCP endpoint *url* redirected to *location*; plurnk follows no redirect, so the server's url must be the endpoint itself ({§mcp-redirect-refused}). |
|
package/dist/McpExecutor.d.ts
CHANGED
|
@@ -3,8 +3,8 @@ import type { ChannelDecl, Effect, ExecArgs, ExecResult, RuntimeAvailability, Ru
|
|
|
3
3
|
import ServerConnection, { type ServerCatalog } from "./client.ts";
|
|
4
4
|
import type { ContentBlock } from "@modelcontextprotocol/client";
|
|
5
5
|
import type { ToolPolicy } from "./config.ts";
|
|
6
|
-
export declare const serverSummary: (name: string, catalog: ServerCatalog | undefined
|
|
7
|
-
export declare const runtimeServerSummary: (name: string, catalog: ServerCatalog | undefined
|
|
6
|
+
export declare const serverSummary: (name: string, catalog: ServerCatalog | undefined) => string;
|
|
7
|
+
export declare const runtimeServerSummary: (name: string, catalog: ServerCatalog | undefined) => RuntimeSummaryDecl;
|
|
8
8
|
export type ToolResultShape = {
|
|
9
9
|
readonly content?: readonly ContentBlock[];
|
|
10
10
|
readonly structuredContent?: unknown;
|
|
@@ -20,7 +20,7 @@ export default class McpExecutor extends BaseExecutor {
|
|
|
20
20
|
constructor(metadata: {
|
|
21
21
|
runtime: string;
|
|
22
22
|
glyph: string;
|
|
23
|
-
}, connection: ServerConnection, retainWorkspace: () => () => void, policy?: Partial<ToolPolicy
|
|
23
|
+
}, connection: ServerConnection, retainWorkspace: () => () => void, policy?: Partial<ToolPolicy>);
|
|
24
24
|
get channels(): Readonly<Record<string, ChannelDecl>>;
|
|
25
25
|
effect(target: string | null): Effect;
|
|
26
26
|
get publishedChannel(): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"McpExecutor.d.ts","sourceRoot":"","sources":["../src/McpExecutor.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,YAAY,EAIf,MAAM,sBAAsB,CAAC;AAE9B,OAAO,KAAK,EACR,WAAW,EACX,MAAM,EACN,QAAQ,EACR,UAAU,EAEV,mBAAmB,EACnB,WAAW,EACX,kBAAkB,EAClB,mBAAmB,EACtB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,gBAAgB,EAAE,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAkB,MAAM,8BAA8B,CAAC;AAGjF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"McpExecutor.d.ts","sourceRoot":"","sources":["../src/McpExecutor.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,YAAY,EAIf,MAAM,sBAAsB,CAAC;AAE9B,OAAO,KAAK,EACR,WAAW,EACX,MAAM,EACN,QAAQ,EACR,UAAU,EAEV,mBAAmB,EACnB,WAAW,EACX,kBAAkB,EAClB,mBAAmB,EACtB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,gBAAgB,EAAE,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAkB,MAAM,8BAA8B,CAAC;AAGjF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAY9C,eAAO,MAAM,aAAa,SAChB,MAAM,WACH,aAAa,GAAG,SAAS,KACnC,MAOF,CAAC;AAGF,eAAO,MAAM,oBAAoB,SACvB,MAAM,WACH,aAAa,GAAG,SAAS,KACnC,kBAKF,CAAC;AAMF,MAAM,MAAM,eAAe,GAAG;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,YAAY,EAAE,CAAC;IAC3C,QAAQ,CAAC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IACrC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC9B,CAAC;AAEF,eAAO,MAAM,cAAc,WAAkB,eAAe,WAAW,MAAM,UAAU,QAAQ,CAAC,OAAO,CAAC,KAAG,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAqBvJ,CAAC;AAwBF,eAAO,MAAM,WAAW,SAAU,MAAM,WAAW,kBAAkB,eAAe,OAAO,iBAAiB,MAAM,KAAG,WAenH,CAAC;AAuBH,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,YAAY;;IAoBjD,YACI,QAAQ,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAC5C,UAAU,EAAE,gBAAgB,EAC5B,eAAe,EAAE,MAAM,MAAM,IAAI,EACjC,MAAM,GAAE,OAAO,CAAC,UAAU,CAAM,EAMnC;IAED,IAAI,QAAQ,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAOpD;IAEQ,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAK7C;IAED,IAAa,gBAAgB,IAAI,MAAM,CAEtC;IAyBD,YAAY,IAAI,mBAAmB,CAKlC;IAED,IAAI,OAAO,IAAI,aAAa,CAK3B;IAEc,KAAK,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,mBAAmB,CAAC,CASvE;IAEK,gBAAgB,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAsBzE;IAEK,GAAG,CAAC,EACN,OAAO,EACP,IAAI,EACJ,MAAM,EACN,MAAM,EACN,KAAK,EACL,QAAQ,EACR,IAAI,EACJ,QAAQ,EACR,KAAK,GACR,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC,CA0HhC;CACJ"}
|