@plurnk/plurnk-mcp 1.6.0 → 1.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/.env.defaults +6 -4
- package/README.md +110 -45
- package/SPEC.md +274 -30
- package/dist/McpExecutor.d.ts +25 -4
- package/dist/McpExecutor.d.ts.map +1 -1
- package/dist/McpExecutor.js +105 -31
- package/dist/McpExecutor.js.map +1 -1
- package/dist/McpResources.d.ts +2 -2
- package/dist/McpResources.d.ts.map +1 -1
- package/dist/McpResources.js +116 -26
- package/dist/McpResources.js.map +1 -1
- package/dist/Module.d.ts +30 -5
- package/dist/Module.d.ts.map +1 -1
- package/dist/Module.js +649 -35
- package/dist/Module.js.map +1 -1
- package/dist/ToolPresentation.d.ts +5 -0
- package/dist/ToolPresentation.d.ts.map +1 -0
- package/dist/ToolPresentation.js +134 -0
- package/dist/ToolPresentation.js.map +1 -0
- package/dist/client.d.ts +25 -7
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +478 -85
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +7 -13
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +114 -45
- package/dist/config.js.map +1 -1
- package/dist/extensionChannel.d.ts +25 -0
- package/dist/extensionChannel.d.ts.map +1 -0
- package/dist/extensionChannel.js +195 -0
- package/dist/extensionChannel.js.map +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/inputRequired.d.ts +36 -0
- package/dist/inputRequired.d.ts.map +1 -0
- package/dist/inputRequired.js +171 -0
- package/dist/inputRequired.js.map +1 -0
- package/dist/oauth.d.ts +28 -0
- package/dist/oauth.d.ts.map +1 -0
- package/dist/oauth.js +149 -0
- package/dist/oauth.js.map +1 -0
- package/dist/protocol.d.ts +7 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +7 -0
- package/dist/protocol.js.map +1 -0
- package/dist/protocolHeaders.d.ts +4 -0
- package/dist/protocolHeaders.d.ts.map +1 -0
- package/dist/protocolHeaders.js +87 -0
- package/dist/protocolHeaders.js.map +1 -0
- package/dist/subscriptions.d.ts +15 -0
- package/dist/subscriptions.d.ts.map +1 -0
- package/dist/subscriptions.js +188 -0
- package/dist/subscriptions.js.map +1 -0
- package/dist/tasks.d.ts +19 -0
- package/dist/tasks.d.ts.map +1 -0
- package/dist/tasks.js +334 -0
- package/dist/tasks.js.map +1 -0
- package/package.json +10 -6
package/.env.defaults
CHANGED
|
@@ -5,14 +5,16 @@
|
|
|
5
5
|
PLURNK_MCP_CONNECT_TIMEOUT=30000
|
|
6
6
|
PLURNK_MCP_REQUEST_TIMEOUT=86400000
|
|
7
7
|
|
|
8
|
-
# Each configured server
|
|
8
|
+
# Each configured server contributes its enabled `[server] (tool)` rows and server:// resources.
|
|
9
9
|
#
|
|
10
10
|
# Streamable HTTP:
|
|
11
11
|
# PLURNK_MCP_github=https://example.test/mcp
|
|
12
|
-
#
|
|
12
|
+
# PLURNK_MCP_github_BEARER=${GITHUB_TOKEN}
|
|
13
|
+
# PLURNK_MCP_github_TOOLS=["issue_read","issue_search"]
|
|
14
|
+
# PLURNK_MCP_github_READ=["issue_read","issue_search"]
|
|
13
15
|
#
|
|
14
|
-
# stdio: the target is one executable
|
|
15
|
-
# does not invent or interpret a shell
|
|
16
|
+
# stdio: the target is one exact executable path/name, including whitespace.
|
|
17
|
+
# Arguments are a JSON array so Plurnk does not invent or interpret a shell.
|
|
16
18
|
# PLURNK_MCP_atlas=node
|
|
17
19
|
# PLURNK_MCP_atlas_ARGS=["/absolute/path/to/atlas-server.mjs"]
|
|
18
20
|
# PLURNK_MCP_atlas_CWD=/absolute/working/directory
|
package/README.md
CHANGED
|
@@ -1,35 +1,74 @@
|
|
|
1
1
|
# @plurnk/plurnk-mcp
|
|
2
2
|
|
|
3
3
|
The current [Model Context Protocol](https://modelcontextprotocol.io/) host
|
|
4
|
-
module for [Plurnk](https://github.com/plurnk/plurnk-service).
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
module for [Plurnk](https://github.com/plurnk/plurnk-service). It projects
|
|
5
|
+
trusted MCP servers through Plurnk's existing executor, resource, proposal,
|
|
6
|
+
entry, Problem, lifecycle, and AG-UI contracts.
|
|
7
|
+
|
|
8
|
+
The module accepts only protocol revision `2026-07-28`. A legacy endpoint is
|
|
9
|
+
rejected with a `protocol-revision-unsupported` Problem that names the required
|
|
10
|
+
revision and `server/discover`; Plurnk does not negotiate a downgrade.
|
|
11
|
+
|
|
12
|
+
## Attach a server
|
|
13
|
+
|
|
14
|
+
Service environment variables provide optional defaults for every workspace.
|
|
15
|
+
Users can also attach, replace, reconnect, or detach a server in one workspace
|
|
16
|
+
without restarting the daemon. An existing AG-UI connection sends the ordinary
|
|
17
|
+
management-action form under `forwardedProps.plurnk.action`:
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"forwardedProps": {
|
|
22
|
+
"plurnk": {
|
|
23
|
+
"workspace": "example",
|
|
24
|
+
"action": {
|
|
25
|
+
"kind": "workspace.mcp.attach",
|
|
26
|
+
"server": {
|
|
27
|
+
"name": "project",
|
|
28
|
+
"transport": "stdio",
|
|
29
|
+
"command": "/opt/mcp/current-server",
|
|
30
|
+
"args": ["--stdio"],
|
|
31
|
+
"env": { "PROJECT_TOKEN": "${PROJECT_TOKEN}" },
|
|
32
|
+
"tools": ["issue_read", "issue_write"],
|
|
33
|
+
"read": ["issue_read"]
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
```
|
|
8
40
|
|
|
9
|
-
|
|
10
|
-
|
|
41
|
+
The standard `plurnk.action.result` event reports success or exact RFC 9457
|
|
42
|
+
Problem Details. The definition is durable and workspace-shared; symbolic
|
|
43
|
+
environment references remain unexpanded at rest.
|
|
11
44
|
|
|
12
|
-
|
|
13
|
-
{"title":"Bug"}
|
|
45
|
+
Available workspace actions are:
|
|
14
46
|
|
|
15
|
-
|
|
47
|
+
| Action | Parameters |
|
|
48
|
+
|---|---|
|
|
49
|
+
| `workspace.mcp.list` | none |
|
|
50
|
+
| `workspace.mcp.attach` | `server` definition |
|
|
51
|
+
| `workspace.mcp.replace` | `server` definition with the existing name |
|
|
52
|
+
| `workspace.mcp.detach` | `name` |
|
|
53
|
+
| `workspace.mcp.reconnect` | `name` |
|
|
54
|
+
| `workspace.mcp.oauth.complete` | `name`, complete `callbackUrl` |
|
|
55
|
+
| `workspace.mcp.complete` | `server`, completion `ref` and `argument`; optional `context` |
|
|
16
56
|
|
|
17
|
-
|
|
18
|
-
|
|
57
|
+
The owning [specification](./SPEC.md) defines the complete action and server
|
|
58
|
+
definition contracts.
|
|
19
59
|
|
|
20
|
-
|
|
21
|
-
fall back to a legacy revision.
|
|
60
|
+
## Service defaults
|
|
22
61
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
Configuration is daemon-owned. One `PLURNK_MCP_<server>` variable declares
|
|
26
|
-
each server; the suffix case-folds to the executor and URI authority name.
|
|
62
|
+
One `PLURNK_MCP_<server>` variable declares each default server. Its suffix
|
|
63
|
+
case-folds to an `[a-z][a-z0-9-]*` executor and URI-authority name.
|
|
27
64
|
|
|
28
65
|
Streamable HTTP:
|
|
29
66
|
|
|
30
67
|
```text
|
|
31
68
|
PLURNK_MCP_github=https://example.test/mcp
|
|
32
|
-
|
|
69
|
+
PLURNK_MCP_github_BEARER=${GITHUB_TOKEN}
|
|
70
|
+
PLURNK_MCP_github_TOOLS=["issue_read","issue_search"]
|
|
71
|
+
PLURNK_MCP_github_READ=["issue_read","issue_search"]
|
|
33
72
|
```
|
|
34
73
|
|
|
35
74
|
Stdio:
|
|
@@ -41,33 +80,59 @@ PLURNK_MCP_local_CWD=/absolute/working/directory
|
|
|
41
80
|
PLURNK_MCP_local_ENV={"TOKEN":"${LOCAL_TOKEN}"}
|
|
42
81
|
```
|
|
43
82
|
|
|
44
|
-
The stdio target is
|
|
45
|
-
module never parses or invokes a
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
[
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
83
|
+
The stdio target is one exact executable path or name, including literal
|
|
84
|
+
whitespace. Arguments are a JSON array; the module never parses or invokes a
|
|
85
|
+
shell command. `${NAME}` references resolve from the daemon's inherited
|
|
86
|
+
environment only while preparing a connection.
|
|
87
|
+
|
|
88
|
+
`PLURNK_MCP_<server>_TOOLS` is an optional JSON array of exact names. Absence
|
|
89
|
+
enables every listed server tool; an array enables exactly those names; `[]`
|
|
90
|
+
enables none. `PLURNK_MCP_<server>_READ` is an exact enabled-tool subset whose
|
|
91
|
+
calls use Plurnk's `read` effect. Every other enabled tool conservatively uses
|
|
92
|
+
the proposal-gated `host` effect. Remote annotations never grant effect
|
|
93
|
+
authority.
|
|
94
|
+
|
|
95
|
+
Portable timeouts and complete examples live in [`.env.defaults`](./.env.defaults).
|
|
96
|
+
|
|
97
|
+
## Plurnk projection
|
|
98
|
+
|
|
99
|
+
| MCP surface | Plurnk surface |
|
|
100
|
+
|---|---|
|
|
101
|
+
| Enabled tool | Exact Registered Tools row and `## EXEC0 [server] (tool)` |
|
|
102
|
+
| Tool schemas | `worker://plurnk/docs/<server>.md` |
|
|
103
|
+
| Resource catalog | `server:///` or `server:///resources` |
|
|
104
|
+
| Resource | `server:///resources/<encoded-uri>` through ordinary `FIND` and `READ` |
|
|
105
|
+
| Prompt catalog | `server:///prompts` |
|
|
106
|
+
| Prompt retrieval | `server:///prompts/<encoded-name>?argument=value` through ordinary `READ` |
|
|
107
|
+
| Completion | Client-owned `workspace.mcp.complete` action |
|
|
108
|
+
|
|
109
|
+
Tool results, resource bodies, prompt messages, and failures become ordinary
|
|
110
|
+
Plurnk entries and channels. Disabled tools appear in neither teaching nor
|
|
111
|
+
admission. There is no MCP-specific model discovery grammar.
|
|
112
|
+
|
|
113
|
+
Current pagination, cache hints, unified subscriptions, progress,
|
|
114
|
+
cancellation, multi-round-trip input, elicitation, and negotiated Tasks remain
|
|
115
|
+
inside the owning operation. Client input uses the standard AG-UI interrupt and
|
|
116
|
+
resume lifecycle; protocol continuation state is never exposed to the model or
|
|
117
|
+
client.
|
|
118
|
+
|
|
119
|
+
## Authorization
|
|
120
|
+
|
|
121
|
+
HTTP definitions support bearer references, client credentials, and
|
|
122
|
+
interactive OAuth. Stdio never receives OAuth. Interactive attachment returns
|
|
123
|
+
`{ "status": 202, "authorization": { "url": "..." } }` without publishing a
|
|
124
|
+
partial server. After the user completes that URL, the client submits its
|
|
125
|
+
complete callback URL through `workspace.mcp.oauth.complete`. PKCE, issuer and
|
|
126
|
+
resource validation, refresh, scope escalation, and credentials remain inside
|
|
127
|
+
the host connection.
|
|
68
128
|
|
|
69
129
|
## Verification
|
|
70
130
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
131
|
+
```sh
|
|
132
|
+
npm test -w @plurnk/plurnk-mcp
|
|
133
|
+
npm run test:mcp:dogfood -w @plurnk/plurnk-service
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
The package gate runs the exact current SDK and official conformance
|
|
137
|
+
requirements. The opt-in dogfood gate composes representative current stdio
|
|
138
|
+
and Streamable HTTP servers through the assembled daemon and AG-UI product.
|
package/SPEC.md
CHANGED
|
@@ -1,18 +1,118 @@
|
|
|
1
1
|
# Plurnk MCP host specification
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## §mcp-role Host boundary
|
|
4
4
|
|
|
5
|
-
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
Shell command parsing is not part of the contract.
|
|
10
|
-
- HTTP uses Streamable HTTP. There is no legacy SSE fallback.
|
|
11
|
-
- A connection or discovery failure leaves no registered runtime.
|
|
12
|
-
- Shutdown waits for every connection attempt to settle, then closes every
|
|
13
|
-
acquired connection and reports all close failures.
|
|
5
|
+
`@plurnk/plurnk-mcp` is an MCP **host/client** that projects trusted remote
|
|
6
|
+
servers into Plurnk. It does not implement an MCP server or authorization
|
|
7
|
+
server. Protocol mechanics remain inside this package; core sees ordinary
|
|
8
|
+
executor, resource, proposal, entry, Problem, and lifecycle contracts.
|
|
14
9
|
|
|
15
|
-
##
|
|
10
|
+
## §mcp-authority Protocol authority
|
|
11
|
+
|
|
12
|
+
The only accepted revision is `2026-07-28`, specification commit
|
|
13
|
+
`5f5440bb26a62e2cf3440b92da5a667efa03b267`. The implementation exact-pins
|
|
14
|
+
`@modelcontextprotocol/client@2.0.0`. SDK exports are not protocol authority:
|
|
15
|
+
that package deliberately retains legacy and deprecated API shapes. It owns
|
|
16
|
+
core negotiation and transport; this package owns only exact-pinned extension
|
|
17
|
+
wire that the SDK does not yet implement.
|
|
18
|
+
|
|
19
|
+
The optional Tasks authority is the official `experimental-ext-tasks` contract
|
|
20
|
+
at commit `2c1425d9a288b9b1f489430fe1e00bb392b47e48`. Its absence from the current
|
|
21
|
+
SDK runtime does not revive that SDK's retained 2025 Tasks vocabulary.
|
|
22
|
+
|
|
23
|
+
Every request carries the modern `_meta` envelope. Connection setup verifies
|
|
24
|
+
`server/discover` at the pinned revision before publishing any runtime or
|
|
25
|
+
scheme. There is no protocol downgrade or legacy fallback.
|
|
26
|
+
|
|
27
|
+
## §mcp-core-matrix Core capability matrix
|
|
28
|
+
|
|
29
|
+
| Surface | Upstream contract | Plurnk host disposition |
|
|
30
|
+
|---|---|---|
|
|
31
|
+
| Base | JSON-RPC 2.0; per-request protocol, identity, and capability metadata; every result has `resultType` | Require the modern envelope and preserve protocol results and errors without reconstructing them |
|
|
32
|
+
| Discovery | Servers implement `server/discover` | Probe before registration; retain identity, instructions, capabilities, versions, and cache hints |
|
|
33
|
+
| Tools | Negotiated server capability: `tools/list`, `tools/call` | Build one operator-filtered exact Registry snapshot at setup; route only its enabled names without renaming them |
|
|
34
|
+
| Resources | Negotiated server capability: `resources/list`, `resources/templates/list`, `resources/read` | Publish catalogs, templates, and materialized contents through the server's resource authority |
|
|
35
|
+
| Prompts | Negotiated server capability: `prompts/list`, `prompts/get` | Publish prompt definitions and retrieve prompt messages through the same server authority |
|
|
36
|
+
| Completion | Negotiated server capability: `completion/complete` | Make prompt and resource-template completion available to the host interaction that owns the argument |
|
|
37
|
+
| Pagination | Opaque cursors on list methods | Drain every page with a finite non-convergence guard; never publish a partial catalog as complete |
|
|
38
|
+
| Caching | `server/discover`, list methods, and `resources/read` carry `ttlMs` and `cacheScope` | Honor freshness and notification invalidation; partition private entries by authorization context |
|
|
39
|
+
| Subscriptions | `subscriptions/listen` plus acknowledged filters and correlated notifications | Keep one current filter for list changes, resource URIs read into cache, and active Task IDs; overlap filter replacement, re-listen after loss, and never use the removed resource subscription methods |
|
|
40
|
+
| Progress | Request-scoped `notifications/progress` | Project progress onto the owning Plurnk operation without creating an independent protocol lifecycle |
|
|
41
|
+
| Cancellation | Per-request stream closure on HTTP; `notifications/cancelled` on stdio | Drive cancellation from the owning Plurnk abort signal and settle the same operation |
|
|
42
|
+
| MRTR | `input_required` on `tools/call`, `resources/read`, or `prompts/get` | Fulfill supported input requests, echo opaque `requestState` byte-for-byte, and retry only the originating request with a fresh JSON-RPC ID |
|
|
43
|
+
| Elicitation | Active client capability carried through MRTR | Advertise supported form/URL modes and route the request through Plurnk's client-owned interaction lifecycle |
|
|
44
|
+
| Authorization | OAuth profile for HTTP transports | Require validated protected-resource and authorization-server metadata; never infer endpoints; use PKCE, issuer validation, resource indicators, refresh, and bounded scope escalation; never apply OAuth to stdio |
|
|
45
|
+
|
|
46
|
+
## §mcp-tasks Tasks extension
|
|
47
|
+
|
|
48
|
+
Tasks is the optional `io.modelcontextprotocol/tasks` extension, never core
|
|
49
|
+
conformance. Plurnk advertises it only when its complete lifecycle is active.
|
|
50
|
+
The server may return an unsolicited `resultType: "task"` handle from
|
|
51
|
+
`tools/call`; the host then uses `tasks/get`, `tasks/update`, and
|
|
52
|
+
`tasks/cancel`. `tasks/get` carries status, outstanding input, and the terminal
|
|
53
|
+
result or protocol error. Task notifications, when selected, use the unified
|
|
54
|
+
subscription stream. `tasks/list`, `tasks/result`, and per-call task opt-in do
|
|
55
|
+
not exist in this revision.
|
|
56
|
+
|
|
57
|
+
Polling honors each current `pollIntervalMs` under the one owning operation
|
|
58
|
+
deadline. Task input keys are fulfilled at most once, in one atomic client
|
|
59
|
+
interaction per observed input set. A completed Task is validated as the
|
|
60
|
+
originating tool result; a failed Task preserves its JSON-RPC error.
|
|
61
|
+
|
|
62
|
+
## §mcp-exclusions Removed, deprecated, and excluded surfaces
|
|
63
|
+
|
|
64
|
+
| Classification | Surfaces | Disposition |
|
|
65
|
+
|---|---|---|
|
|
66
|
+
| Deprecated | Roots, Sampling, Logging | Do not advertise or implement; use explicit resources/tool arguments, Plurnk's provider layer, and stderr/OpenTelemetry respectively |
|
|
67
|
+
| Deprecated | HTTP+SSE transport; Sampling `includeContext` values | Do not adopt; use Streamable HTTP and no Sampling |
|
|
68
|
+
| Deprecated fallback | OAuth Dynamic Client Registration | Prefer pre-registration, then CIMD when advertised; use DCR only when authorization-server metadata advertises `registration_endpoint`; otherwise fail without probing an inferred endpoint |
|
|
69
|
+
| Removed | `initialize`, `notifications/initialized`, `Mcp-Session-Id`, HTTP GET event stream | Reject the legacy lifecycle; every request is stateless and self-contained |
|
|
70
|
+
| Removed | `ping`, `logging/setLevel`, `notifications/roots/list_changed` | Do not send, handle, or teach |
|
|
71
|
+
| Removed | `resources/subscribe`, `resources/unsubscribe`, SSE resumption and `Last-Event-ID` | Use `subscriptions/listen`; reissue a lost request with a new ID |
|
|
72
|
+
| Removed | Legacy Tasks `tasks/list`, `tasks/result`, and task-augmentation request fields | Use only the negotiated final Tasks extension |
|
|
73
|
+
| Excluded | Other official, experimental, or private extensions | Require a separately owned contract before negotiation |
|
|
74
|
+
| Excluded | Legacy revisions and dual-era operation | Pin `2026-07-28`; never probe-and-fallback |
|
|
75
|
+
| Excluded | MCP server and authorization-server roles | This package is the host/client only |
|
|
76
|
+
|
|
77
|
+
## §mcp-transports Transport bindings
|
|
78
|
+
|
|
79
|
+
| Binding | Contract |
|
|
80
|
+
|---|---|
|
|
81
|
+
| 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 |
|
|
82
|
+
| Streamable HTTP | Send one POST per request or notification; accept JSON or SSE responses; close the response stream to cancel; never open the removed general GET stream |
|
|
83
|
+
|
|
84
|
+
Every HTTP request carries matching `MCP-Protocol-Version` and `Mcp-Method`
|
|
85
|
+
headers. Named requests also carry `Mcp-Name`; declared primitive tool
|
|
86
|
+
parameters carry validated `Mcp-Param-*` headers. Header names compare
|
|
87
|
+
case-insensitively, and body/header disagreement fails instead of guessing.
|
|
88
|
+
For `tasks/get`, `tasks/update`, and `tasks/cancel`, `Mcp-Name` is the encoded
|
|
89
|
+
`taskId` required by the Tasks extension.
|
|
90
|
+
|
|
91
|
+
## §mcp-errors Error allocation
|
|
92
|
+
|
|
93
|
+
| Condition | Code and boundary |
|
|
94
|
+
|---|---|
|
|
95
|
+
| Standard JSON-RPC parse/request/method/params/internal failures | `-32700`, `-32600`, `-32601`, `-32602`, `-32603` |
|
|
96
|
+
| Missing resource or task handle | `-32602` |
|
|
97
|
+
| Tasks extension capability absent | `-32003` |
|
|
98
|
+
| Header/body mismatch | `-32020` `HeaderMismatch`; HTTP 400 |
|
|
99
|
+
| Required client capability absent | `-32021` `MissingRequiredClientCapability`; HTTP 400 where applicable |
|
|
100
|
+
| Protocol revision unsupported | `-32022` `UnsupportedProtocolVersion`; HTTP 400 |
|
|
101
|
+
| Server-private errors | `-32000` through `-32019` only |
|
|
102
|
+
| Future MCP-reserved errors | `-32020` through `-32099` only as assigned by the protocol |
|
|
103
|
+
|
|
104
|
+
A tool-level `isError: true` result is a completed tool result, not a JSON-RPC
|
|
105
|
+
failure. A failed Task carries its originating JSON-RPC error; a Task wrapping
|
|
106
|
+
a tool-level error completes with that tool result. Plurnk preserves the
|
|
107
|
+
originating distinction in its canonical Problem/result path.
|
|
108
|
+
|
|
109
|
+
## §mcp-configuration Configuration
|
|
110
|
+
|
|
111
|
+
Two inputs produce one effective definition per workspace. Service environment
|
|
112
|
+
variables are convenience defaults instantiated independently for every
|
|
113
|
+
workspace. The workspace's durable attachment map may add a server, replace a
|
|
114
|
+
default, or retain a tombstone that suppresses a default after detach. Neither
|
|
115
|
+
source expands the model-facing namespace with a second discovery surface.
|
|
16
116
|
|
|
17
117
|
| Variable | Contract |
|
|
18
118
|
|---|---|
|
|
@@ -20,29 +120,173 @@
|
|
|
20
120
|
| `PLURNK_MCP_<server>_ARGS` | JSON string array for stdio |
|
|
21
121
|
| `PLURNK_MCP_<server>_CWD` | Working directory for stdio |
|
|
22
122
|
| `PLURNK_MCP_<server>_ENV` | JSON string map for stdio |
|
|
23
|
-
| `PLURNK_MCP_<server>
|
|
123
|
+
| `PLURNK_MCP_<server>_BEARER` | HTTP bearer credential; use `${TOKEN}` expansion to retain the authoritative environment value |
|
|
124
|
+
| `PLURNK_MCP_<server>_HEADERS` | JSON string map for supplementary HTTP headers |
|
|
125
|
+
| `PLURNK_MCP_<server>_TOOLS` | Optional JSON array of exact enabled tool names; absent enables all listed server tools, while `[]` enables none |
|
|
126
|
+
| `PLURNK_MCP_<server>_READ` | JSON string array forming an exact subset of enabled tools that the operator classifies as read-only; every other enabled tool retains the conservative `host` effect |
|
|
24
127
|
| `PLURNK_MCP_CONNECT_TIMEOUT` | Positive integer milliseconds |
|
|
25
128
|
| `PLURNK_MCP_REQUEST_TIMEOUT` | Positive integer milliseconds |
|
|
26
129
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
130
|
+
Configured server names match `[a-z][a-z0-9-]*` after case-folding and share
|
|
131
|
+
the executor and URI-authority namespace. Duplicate names, reserved-name
|
|
132
|
+
collisions, orphan companions, wrong-transport companions, missing environment
|
|
133
|
+
references, and invalid JSON fail startup. A stdio target is one exact
|
|
134
|
+
executable string even when its path contains whitespace; arguments never hide
|
|
135
|
+
inside it. Bearer authentication and a case-insensitive `Authorization` entry
|
|
136
|
+
in `_HEADERS` are mutually exclusive.
|
|
137
|
+
|
|
138
|
+
§mcp-definition-wire The contracts-owned `McpServerDefinition` JSON Schema is
|
|
139
|
+
the one workspace action and durable-state shape. It is a closed discriminated
|
|
140
|
+
union:
|
|
141
|
+
|
|
142
|
+
| Transport / authorization | Required definition | Optional definition |
|
|
143
|
+
|---|---|---|
|
|
144
|
+
| `stdio` | `name`, `transport`, `command` | `args`, `cwd`, `env`, `tools`, `read` |
|
|
145
|
+
| `http` + none | `name`, `transport`, `url` | `headers`, `tools`, `read` |
|
|
146
|
+
| `http` + bearer | above plus `authorization: { type: "bearer", token: "${NAME}" }` | — |
|
|
147
|
+
| `http` + interactive OAuth / CIMD preferred | above plus `authorization: { type: "oauth", redirectUrl, clientMetadataUrl }` | `scope`; DCR remains the server-advertised fallback when CIMD is unavailable |
|
|
148
|
+
| `http` + interactive OAuth / pre-registered | above plus `authorization: { type: "oauth", redirectUrl, clientId, clientSecret: "${NAME}" }` | `scope` |
|
|
149
|
+
| `http` + interactive OAuth / DCR fallback only | above plus `authorization: { type: "oauth", redirectUrl }` | `scope` |
|
|
150
|
+
| `http` + client credentials | above plus `authorization: { type: "client-credentials", clientId, clientSecret: "${NAME}" }` | `scope` |
|
|
30
151
|
|
|
31
|
-
|
|
152
|
+
`tools` absent enables the complete listed set; `[]` enables none. `read` is an
|
|
153
|
+
exact subset of the enabled set. A credential field is one complete symbolic
|
|
154
|
+
environment reference, not a copied token. Other string-valued `headers`,
|
|
155
|
+
`env`, `cwd`, and argument values may contain symbolic references and are
|
|
156
|
+
expanded only while preparing a connection. The unexpanded definition is the
|
|
157
|
+
only durable form. Interactive OAuth tokens, PKCE verifier, issuer-bound
|
|
158
|
+
discovery state, and authorization callback state remain process-memory
|
|
159
|
+
credentials; a restart reconstructs the attachment as authorization-required
|
|
160
|
+
instead of writing secrets into SQLite.
|
|
161
|
+
|
|
162
|
+
### §mcp-management-actions Workspace management
|
|
163
|
+
|
|
164
|
+
Every action below declares `scope: "workspace"` under
|
|
165
|
+
{§module-action-registration}. AG-UI binds its workspace; none accepts a
|
|
166
|
+
workspace identifier in params.
|
|
167
|
+
|
|
168
|
+
| Action | Parameters | Result / effect |
|
|
169
|
+
|---|---|---|
|
|
170
|
+
| `workspace.mcp.list` | none | Sorted effective server summaries: name, source, transport, connection/authorization state, negotiated identity/capabilities, enabled tools, and read subset. No credential values. |
|
|
171
|
+
| `workspace.mcp.attach` | `server: McpServerDefinition` | Adds a name absent from the effective workspace. Preparation completes before publication. |
|
|
172
|
+
| `workspace.mcp.replace` | `server: McpServerDefinition` | Replaces one effective definition under the same name; absence is 404. |
|
|
173
|
+
| `workspace.mcp.detach` | `name` | Removes a workspace attachment or writes a tombstone for a service default, then removes its exact Registry, docs, and resource authority. |
|
|
174
|
+
| `workspace.mcp.reconnect` | `name` | Builds a fresh connection from the existing unexpanded definition and atomically replaces the old connection after successful preparation. |
|
|
175
|
+
| `workspace.mcp.oauth.complete` | `name`, `callbackUrl` | State- and issuer-validates one pending interactive callback through the SDK, completes connection preparation, then performs the originally requested attach, replace, or reconnect. |
|
|
176
|
+
| `workspace.mcp.complete` | `server`, `ref`, `argument`; optional `context` | Requests negotiated prompt/resource-template argument completion for a client-owned interaction. |
|
|
177
|
+
|
|
178
|
+
Expected preparation failures cross the action boundary as MCP-management
|
|
179
|
+
Problems rather than generic AG-UI failures:
|
|
180
|
+
|
|
181
|
+
| Endpoint condition | Problem |
|
|
182
|
+
|---|---|
|
|
183
|
+
| Definitively does not offer pinned `2026-07-28` through `server/discover` | `502 protocol-revision-unsupported`, non-retryable; names the server, required revision and method, and directs the operator to upgrade or replace the legacy endpoint |
|
|
184
|
+
| Cannot connect or complete current discovery/catalog preparation | `502 server-unavailable`, retryable; names the server and transport without exposing credentials |
|
|
185
|
+
|
|
186
|
+
Interactive preparation returns a successful pending result shaped as
|
|
187
|
+
`{ status: 202, authorization: { url } }`; it publishes no candidate runtime.
|
|
188
|
+
The action owner retains one pending candidate per `(workspace, name)` and a
|
|
189
|
+
new request cancels and replaces it. Unrelated workspace changes remain
|
|
190
|
+
authoritative while authorization is pending; drift of the same server fails
|
|
191
|
+
completion with a conflict instead of replaying a stale workspace snapshot.
|
|
192
|
+
`oauth.complete` accepts the complete
|
|
193
|
+
callback URL so state, `code`, and `iss` remain one parsing unit. A missing,
|
|
194
|
+
expired, mismatched, or replayed callback fails without exposing attacker-owned
|
|
195
|
+
OAuth error text. It completes either pending hydration or the originally
|
|
196
|
+
requested attach, replace, or reconnect. There is no callback HTTP endpoint,
|
|
197
|
+
authority-root resource, or MCP-specific AG-UI route.
|
|
198
|
+
|
|
199
|
+
## §mcp-setup Atomic lifecycle
|
|
200
|
+
|
|
201
|
+
For each workspace, hydration resolves service defaults against the durable
|
|
202
|
+
attachment/tombstone map, opens and discovers every effective connection,
|
|
203
|
+
lists the negotiated catalogs, applies enabled/effect policy, builds each exact
|
|
204
|
+
tool Registry and resource facet, and submits one complete owner snapshot to
|
|
205
|
+
{§module-workspace-capabilities}. A configured tool absent from the server, a
|
|
206
|
+
duplicate remote name, an enabled name not representable as a Plurnk target,
|
|
207
|
+
or a `read` name outside the enabled set fails that workspace hydration. No
|
|
208
|
+
partial namespace is published and every acquired candidate closes.
|
|
209
|
+
|
|
210
|
+
Attach, replace, and reconnect prepare the candidate while the old snapshot
|
|
211
|
+
remains authoritative, then commit only at {§module-workspace-quiescence}. The
|
|
212
|
+
old connection rejects replacement while it owns an active protocol request,
|
|
213
|
+
MRTR exchange, or Task. Cache/list-change watches are infrastructure and close
|
|
214
|
+
with the old connection after the new snapshot commits. A failed candidate or
|
|
215
|
+
commit leaves the durable definition, connection, Registry, docs, and resource
|
|
216
|
+
authority unchanged. Materialization and registration inspect the complete
|
|
217
|
+
owning operation result; a non-success preserves its original Problem.
|
|
218
|
+
|
|
219
|
+
Shutdown first prevents new work, cancels pending OAuth candidates and
|
|
220
|
+
infrastructure watches, settles every active request and Task, closes every
|
|
221
|
+
acquired connection, then reports all close failures. Whole-connection
|
|
222
|
+
shutdown retires subscription work before closing its transport; it does not
|
|
223
|
+
first issue a redundant per-listen cancellation.
|
|
224
|
+
|
|
225
|
+
## §mcp-host-composition Protocol-to-Plurnk composition
|
|
226
|
+
|
|
227
|
+
One `ServerConnection` owns negotiation, SDK caches, authorization partition,
|
|
228
|
+
subscriptions, active request controllers, MRTR rounds, and Tasks for one
|
|
229
|
+
workspace attachment. The host does not reproduce SDK protocol machinery.
|
|
230
|
+
|
|
231
|
+
| Protocol event | Plurnk composition |
|
|
232
|
+
|---|---|
|
|
233
|
+
| `tools/call` progress | Writes ordinary transient progress on the owning EXEC stream; it creates no log sibling or polling vocabulary. |
|
|
234
|
+
| Operation cancellation | The owning EXEC abort signal closes the HTTP request stream or sends the stdio cancellation notification. |
|
|
235
|
+
| `input_required` | Batches all embedded requests from one result into one atomic client interaction. Opaque `requestState` remains private to the connection and only the originating request is reissued after a complete response. |
|
|
236
|
+
| Elicitation form / URL | Validates the response against the requested form or URL action contract. Client cancellation becomes the standard `cancel` action; unsupported families or modes fail before any interaction or retry. |
|
|
237
|
+
| Task handle | Keeps the original EXEC stream active, follows `tasks/get` and selected Task notifications, and settles that same stream with the terminal result or error. |
|
|
238
|
+
| Task input | Routes through the operation's client interaction, then sends `tasks/update`; it never asks the model to manufacture protocol state. |
|
|
239
|
+
| Task cancellation | The owning EXEC cancellation invokes `tasks/cancel` before settling the ordinary stream cancellation. |
|
|
240
|
+
| List/resource invalidation | List changes invalidate SDK catalogs and atomically refresh the attachment snapshot. Updates to selected resource URIs invalidate their SDK cache entries; private entries remain authorization-partitioned. |
|
|
241
|
+
| Prompt get / completion | Serves ordinary resource-authority reads and host interactions from negotiated prompt/template definitions; no prompt becomes an executable tool. |
|
|
242
|
+
|
|
243
|
+
The general executor interaction contract, not this package, owns client
|
|
244
|
+
interrupt durability and AG-UI presentation. A disconnect re-surfaces its
|
|
245
|
+
pending client-owned interaction exactly as proposal review does. MRTR round
|
|
246
|
+
limits, request timeout, cancellation, and Task terminal state are one
|
|
247
|
+
operation lifecycle; none becomes a hidden retry loop.
|
|
248
|
+
|
|
249
|
+
## §mcp-model-projection Model-facing projection
|
|
32
250
|
|
|
33
251
|
| MCP surface | Plurnk surface |
|
|
34
252
|
|---|---|
|
|
35
|
-
| Server |
|
|
36
|
-
|
|
|
37
|
-
|
|
|
38
|
-
| Resource catalog |
|
|
39
|
-
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
253
|
+
| Server | One registered executor family and matching resource scheme |
|
|
254
|
+
| Enabled tool | One exact Registered Tools row and `## EXEC0 [<server>] (<tool>)` call |
|
|
255
|
+
| Enabled tool details | `worker://plurnk/docs/<server>.md` through the standard kernel documentation surface |
|
|
256
|
+
| Resource catalog | `<server>:///` and `<server>:///resources` |
|
|
257
|
+
| Resources | `<server>:///resources` and encoded resource-URI descendants |
|
|
258
|
+
| Prompts | `<server>:///prompts` and encoded prompt-name descendants |
|
|
259
|
+
|
|
260
|
+
§mcp-tool-presentation One canonical enabled-tool snapshot owns every
|
|
261
|
+
model-facing and executable consequence. Each enabled remote tool becomes one
|
|
262
|
+
exact target in {§executor-tool-registry}. Its Registered Tools row carries the
|
|
263
|
+
remote description, requiredness derived from the input schema, and a
|
|
264
|
+
deterministic one-line JSON-shaped signature: quoted property names, `?` on
|
|
265
|
+
optional properties, primitive type words, and literal unions—never fabricated
|
|
266
|
+
argument data. The snapshot's standard kernel document contains each enabled
|
|
267
|
+
tool's description and exact input/output JSON Schemas. Disabled names appear
|
|
268
|
+
in neither surface, and there is no MCP-specific FIND, READ, authority-root, or
|
|
269
|
+
other model discovery mechanism for tools.
|
|
270
|
+
|
|
271
|
+
Core validates the exact target and the selected tool's invocation before
|
|
272
|
+
effect admission. `McpExecutor.run()` independently rejects a target outside
|
|
273
|
+
the same snapshot before issuing `tools/call`. The server's empty-authority
|
|
274
|
+
scheme is consequently resource-only: its root and `/resources` catalogs
|
|
275
|
+
contain resources and resource templates, never tools. Tool results become
|
|
276
|
+
ordinary Plurnk entries and channels, so slicing, tags, curation, notices, and
|
|
277
|
+
Problems need no MCP-specific parallel mechanism.
|
|
278
|
+
|
|
279
|
+
MCP tool annotations remain untrusted metadata, not admission authority. The
|
|
280
|
+
operator-owned `_READ` subset classifies enabled observations as the executor
|
|
281
|
+
`read` effect; every other enabled tool remains `host` and therefore uses the
|
|
282
|
+
ordinary proposal policy. Effect classification receiving an unregistered
|
|
283
|
+
target is an internal contract violation rather than a conservative guess.
|
|
284
|
+
|
|
285
|
+
## §mcp-conformance Conformance authority
|
|
286
|
+
|
|
287
|
+
Protocol conformance runs through official
|
|
288
|
+
`@modelcontextprotocol/conformance@0.2.0-alpha.11`, whose immutable
|
|
289
|
+
`2026-07-28` requirement manifest freezes the release-time alpha.10 scenario
|
|
290
|
+
set. The core client leg must pass; supported extension scenarios run and
|
|
291
|
+
report separately because Tasks cannot alter the core pass rate. Atlas and
|
|
292
|
+
third-party stdio/Streamable HTTP servers are composition evidence only.
|
package/dist/McpExecutor.d.ts
CHANGED
|
@@ -1,16 +1,37 @@
|
|
|
1
1
|
import { BaseExecutor } from "@plurnk/plurnk-execs";
|
|
2
|
-
import type { ChannelDecl, Effect, ExecArgs, ExecResult, RuntimeAvailability, RuntimeDecl } from "@plurnk/plurnk-execs";
|
|
3
|
-
import ServerConnection from "./client.ts";
|
|
2
|
+
import type { ChannelDecl, Effect, ExecArgs, ExecResult, RuntimeAvailability, RuntimeDecl, RuntimeToolRegistry } from "@plurnk/plurnk-execs";
|
|
3
|
+
import ServerConnection, { type ServerCatalog } from "./client.ts";
|
|
4
|
+
import type { ToolPolicy } from "./config.ts";
|
|
4
5
|
export declare const runtimeDecl: (name: string) => RuntimeDecl;
|
|
5
6
|
export default class McpExecutor extends BaseExecutor {
|
|
6
7
|
#private;
|
|
7
8
|
constructor(metadata: {
|
|
8
9
|
runtime: string;
|
|
9
10
|
glyph: string;
|
|
10
|
-
}, connection: ServerConnection);
|
|
11
|
+
}, connection: ServerConnection, policy?: Partial<ToolPolicy>);
|
|
12
|
+
get manifest(): {
|
|
13
|
+
name: string;
|
|
14
|
+
channels: Record<string, string>;
|
|
15
|
+
defaultChannel: string;
|
|
16
|
+
category: "data" | "logging" | "control";
|
|
17
|
+
writableBy: ReadonlyArray<import("@plurnk/plurnk-schemes").WriterTier>;
|
|
18
|
+
volatile: boolean;
|
|
19
|
+
modelVisible: boolean;
|
|
20
|
+
folderScopes?: boolean;
|
|
21
|
+
textEditScopes?: boolean;
|
|
22
|
+
foldedByDefault?: boolean;
|
|
23
|
+
flags?: import("@plurnk/plurnk-schemes").SchemeFlagAffinity;
|
|
24
|
+
documentation?: string;
|
|
25
|
+
glyph?: string;
|
|
26
|
+
storedScheme?: string;
|
|
27
|
+
example: string;
|
|
28
|
+
};
|
|
11
29
|
get channels(): Readonly<Record<string, ChannelDecl>>;
|
|
12
30
|
effect(target: string | null): Effect;
|
|
31
|
+
toolRegistry(): RuntimeToolRegistry;
|
|
32
|
+
get catalog(): ServerCatalog;
|
|
13
33
|
probe(signal?: AbortSignal): Promise<RuntimeAvailability>;
|
|
14
|
-
|
|
34
|
+
requireAvailable(signal?: AbortSignal): Promise<RuntimeAvailability>;
|
|
35
|
+
run({ runtime, body, target, signal, write, setState, emit, interact, }: ExecArgs): Promise<ExecResult>;
|
|
15
36
|
}
|
|
16
37
|
//# sourceMappingURL=McpExecutor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"McpExecutor.d.ts","sourceRoot":"","sources":["../src/McpExecutor.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,YAAY,
|
|
1
|
+
{"version":3,"file":"McpExecutor.d.ts","sourceRoot":"","sources":["../src/McpExecutor.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,YAAY,EAMf,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EACR,WAAW,EACX,MAAM,EACN,QAAQ,EACR,UAAU,EAEV,mBAAmB,EACnB,WAAW,EACX,mBAAmB,EACtB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,gBAAgB,EAAE,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAEnE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAK9C,eAAO,MAAM,WAAW,SAAU,MAAM,KAAG,WASzC,CAAC;AAuBH,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,YAAY;;IAOjD,YACI,QAAQ,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAC5C,UAAU,EAAE,gBAAgB,EAC5B,MAAM,GAAE,OAAO,CAAC,UAAU,CAAM,EAMnC;IAED,IAAa,QAAQ;;;;;;;;;;;;;;;;MAKpB;IAED,IAAI,QAAQ,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAMpD;IAEQ,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAK7C;IAgCD,YAAY,IAAI,mBAAmB,CAKlC;IAED,IAAI,OAAO,IAAI,aAAa,CAK3B;IAEc,KAAK,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAgBvE;IAEK,gBAAgB,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAqBzE;IAEK,GAAG,CAAC,EACN,OAAO,EACP,IAAI,EACJ,MAAM,EACN,MAAM,EACN,KAAK,EACL,QAAQ,EACR,IAAI,EACJ,QAAQ,GACX,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC,CAiHhC;CACJ"}
|