@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 CHANGED
@@ -1,7 +1,12 @@
1
- # @plurnk/plurnk-mcp — workspace MCP servers.
2
- # Alias blocks make servers available; ENABLED selects initial attachments.
3
- # An empty alias target masks its definition, companions and ENABLED/EXPANDED selections.
4
- # Workers manage the shared servers through the mcp Functionality controls.
1
+ # @plurnk/plurnk-mcp — complete server definitions and independent behavior controls.
2
+ # Lowercase aliases; underscores represent hyphens. Declared servers default enabled.
3
+ # Files: project .agents/mcp.json > XDG plurnk/mcp.json > ~/.agents/mcp.json.
4
+ # Environment definitions override files whole; these controls apply to every source.
5
+ PLURNK_MCP_ENABLED=1
6
+ # PLURNK_MCP_code_search={"name":"code-search","type":"stdio","command":"npx","args":["-y","example-mcp-server@1.0.0"]}
7
+ # PLURNK_MCP_code_search_ENABLED=0
8
+ # HTTP authentication belongs to the whole definition; secrets remain environment references.
9
+ # PLURNK_MCP_forge={"name":"forge","type":"streamable-http","url":"https://forge.example/mcp","authorization":{"type":"bearer","token":"${FORGE_TOKEN}"}}
5
10
 
6
11
  # Connection deadline and complete catalog/list-walk deadline, positive ms each.
7
12
  PLURNK_MCP_CONNECT_TIMEOUT=30000
@@ -11,38 +16,13 @@ PLURNK_MCP_REQUEST_TIMEOUT=86400000
11
16
  PLURNK_MCP_RETRY_FLOOR_MS=250
12
17
  # Ceiling of that doubling delay, positive ms, at least the floor.
13
18
  PLURNK_MCP_RETRY_CEILING_MS=5000
14
- # Initially enabled aliases, JSON array; [] = none.
15
- PLURNK_MCP_ENABLED=[]
16
- # Expand these servers' tool lists in turn 0; otherwise show one row per server.
19
+ # The HTTP(S) MCP Registry `mcp (discover)` searches; empty turns registry search off.
20
+ PLURNK_MCP_REGISTRY_URL=https://registry.modelcontextprotocol.io
21
+ # Most servers one registry search returns, positive.
22
+ PLURNK_MCP_REGISTRY_LIMIT=20
23
+ # Expand these servers' tool lists in turn 0, JSON array of aliases; otherwise one row per server.
17
24
  # PLURNK_MCP_EXPANDED=[]
18
25
 
19
- # --- HTTP target ---
20
- # URL, optional bearer reference, optional headers (string-valued JSON object).
21
- # PLURNK_MCP_forge=https://example.test/mcp
22
- # PLURNK_MCP_forge_BEARER=${FORGE_TOKEN}
23
- # PLURNK_MCP_forge_HEADERS={"X-Tenant":"${FORGE_TENANT}"}
24
-
25
- # --- stdio target ---
26
- # One exact executable (not a shell command); ARGS is a literal JSON array.
27
- # CWD defaults to per-workspace server state under XDG_STATE_HOME (normally ~/.local/state).
28
- # Explicit CWD is honored; ENV overlays the subprocess environment, resolving ${NAME} references.
29
- # PLURNK_MCP_browser=node
30
- # PLURNK_MCP_browser_ARGS=["/absolute/path/to/browser-server.mjs"]
31
- # PLURNK_MCP_browser_CWD=/absolute/working/directory
32
- # PLURNK_MCP_browser_ENV={"TOKEN":"${BROWSER_TOKEN}"}
33
-
34
- # --- Tool admission and orientation ---
35
- # Optional tool allowlist; omitted = all server tools. READ marks tools as read-only for admission.
26
+ # --- Per-server operator settings ---
27
+ # Tool allowlist, JSON array; absent enables every tool the server lists, [] none.
36
28
  # PLURNK_MCP_forge_TOOLS=["issue_read","issue_search"]
37
- # PLURNK_MCP_forge_READ=["issue_read","issue_search"]
38
- # Summary fallbacks when server metadata is inadequate; per-server or per-tool.
39
- # PLURNK_MCP_forge_SUMMARY=```forge (issue_read) <!-- Read forge issues -->```
40
- # PLURNK_MCP_forge_issue_read_SUMMARY=Read one issue.
41
-
42
- # --- Optional Brave Search ---
43
- # PLURNK_MCP_brave=npx
44
- # PLURNK_MCP_brave_ARGS=["-y","@brave/brave-search-mcp-server@2.1.0"]
45
- # PLURNK_MCP_brave_ENV={"BRAVE_API_KEY":"${BRAVE_API_KEY}"}
46
- # PLURNK_MCP_brave_TOOLS=["brave_web_search","brave_news_search"]
47
- # PLURNK_MCP_brave_READ=["brave_web_search","brave_news_search"]
48
- # PLURNK_MCP_ENABLED=["brave"]
package/README.md CHANGED
@@ -1,9 +1,8 @@
1
1
  # @plurnk/plurnk-mcp
2
2
 
3
- The current [Model Context Protocol](https://modelcontextprotocol.io/) host
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.
3
+ The [Model Context Protocol](https://modelcontextprotocol.io/) host module for
4
+ [Plurnk](https://github.com/plurnk/plurnk-service). It projects configured MCP servers
5
+ through Plurnk's executor, resource, proposal, entry, Problem, lifecycle, and AG-UI contracts.
7
6
 
8
7
  The module's own wire authority is protocol revision `2026-07-28`
9
8
  ({§mcp-authority}). Connection setup negotiates-and-degrades: a server that
@@ -13,54 +12,95 @@ serves its standard surface at its own negotiated revision. Plurnk does not
13
12
  downgrade its own extension wire, but it does not reject an older supported
14
13
  revision.
15
14
 
16
- ## Manage workspace servers
15
+ ## Configure servers
17
16
 
18
- Service environment variables provide available servers for every workspace;
19
- `PLURNK_MCP_ENABLED` selects the exact cold-enabled subset. Users can add,
20
- enable, disable, or remove a workspace's servers without restarting the daemon. An
21
- existing AG-UI connection sends the ordinary management-action form under
22
- `forwardedProps.plurnk.action`:
17
+ Put shared servers in `~/.agents/mcp.json` ({§mcp-file-configuration}):
23
18
 
24
19
  ```json
25
20
  {
26
- "forwardedProps": {
27
- "plurnk": {
28
- "workspace": "example",
29
- "action": {
30
- "kind": "workspace.mcp.add",
31
- "alias": "project",
32
- "definition": {
33
- "name": "project",
34
- "transport": "stdio",
35
- "command": "/opt/mcp/current-server",
36
- "args": ["--stdio"],
37
- "env": { "PROJECT_TOKEN": "${PROJECT_TOKEN}" },
38
- "tools": ["issue_read", "issue_write"],
39
- "read": ["issue_read"]
40
- }
41
- }
42
- }
21
+ "mcpServers": {
22
+ "files": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/absolute/project/path"] },
23
+ "forge": { "type": "streamable-http", "url": "https://forge.example/mcp", "headers": { "Authorization": "Bearer ${FORGE_TOKEN}" } }
43
24
  }
44
25
  }
45
26
  ```
46
27
 
47
- The standard `plurnk.action.result` event reports success or exact RFC 9457
48
- Problem Details. The definition is durable and workspace-shared; symbolic
49
- environment references remain unexpanded at rest.
28
+ Project `.agents/mcp.json` overrides `$XDG_CONFIG_HOME/plurnk/mcp.json`, which
29
+ overrides that shared global file, by complete server entry. The existing
30
+ `PLURNK_SERVICE_ROOTS` selection applies. Files stay read-only; no plugin is required.
50
31
 
51
- MCP is one family of workspace Functionality; the common lifecycle actions are
52
- published by the coordinator and the two continuations by this module:
32
+ Installed Agent Plugins also contribute `mcp.json` servers through the same
33
+ workspace management. Standalone definitions precede plugin components within
34
+ each scope; npm bundles come last. Plugin subprocesses use their plugin root
35
+ and persistent data directory, with the standard's literal-string rules
36
+ ({§mcp-plugin-configuration}).
37
+ `list` identifies the winning file and entry; file changes are read before the next turn.
38
+
39
+ Environment-only configuration remains available and overrides file definitions
40
+ ({§mcp-configuration}); live additions persist in the workspace and override both.
41
+
42
+ ```dotenv
43
+ PLURNK_MCP_brave={"name":"brave","type":"stdio","command":"npx","args":["-y","@brave/brave-search-mcp-server@2.1.0"]}
44
+ PLURNK_MCP_forge={"name":"forge","type":"streamable-http","url":"https://forge.example/mcp","authorization":{"type":"bearer","token":"${FORGE_TOKEN}"}}
45
+ ```
46
+
47
+ Aliases match `[a-z][a-z0-9-]*`; environment suffixes use lowercase and
48
+ encode hyphens as underscores. The alias names both the model's executor
49
+ and the server's resource scheme. A higher-precedence definition replaces
50
+ the whole connection, including authentication; it does not patch fields.
51
+ Declaring a server enables it unless an independent control disables it.
52
+ Explicit HTTP(S) endpoints may use private-network hosts; OAuth retains its
53
+ TLS and authorization requirements ({§mcp-endpoint-security}).
54
+
55
+ | Transport | Behaviour |
56
+ |---|---|
57
+ | `stdio` | Spawn `command` and `args` without a shell. Bare commands use PATH; relative commands use the working directory. References in `args`, `env`, and `cwd` expand at launch. |
58
+ | `streamable-http` | Connect to `url`, with optional `headers` and `authorization`. Header references expand at connection time; SDK protocol headers remain transport-owned. Structured authorization and an Authorization header cannot both be declared. Redirects are refused ({§mcp-redirect-refused}). |
59
+
60
+ A local server defaults to a stable, workspace-owned state directory outside
61
+ the project ({§mcp-launch-directory}). An explicit absolute `cwd` overrides
62
+ that location; the caller provisions it. This is file placement, not a sandbox.
63
+
64
+ Stdio servers inherit the operator's environment without Plurnk settings or
65
+ model-provider keys. Workspace `env` entries apply on top, then the definition's
66
+ `env`; worker-local overrides do not ({§mcp-launch-environment}). A running
67
+ server keeps its launch environment; disable then enable it to pick up changes.
68
+
69
+ A tool marked `annotations.readOnlyHint` runs with the `read` effect;
70
+ other tools retain the `host` effect and the loop's proposal policy.
71
+
72
+ ## Manage a workspace's servers
73
+
74
+ MCP is one family of workspace Functionality, managed with the six verbs every family has
75
+ ({§mcp-management-actions}).
53
76
 
54
77
  | Action | Parameters |
55
78
  |---|---|
56
79
  | `workspace.mcp.list` | — |
57
- | `workspace.mcp.discover` | optional `query`, `source` (URL or command line), `configuration` (a client's `PLURNK_MCP_*` overlay) |
58
- | `workspace.mcp.add` | optional `alias` (must equal the definition's `name`), `definition: McpServerDefinition` |
80
+ | `workspace.mcp.discover` | `query`: searches the MCP Registry ({§mcp-registry-discovery}) |
81
+ | `workspace.mcp.add` | optional `alias` (the definition's `name`), `definition`: a complete connection definition |
59
82
  | `workspace.mcp.enable` / `disable` / `remove` | `alias` |
60
83
  | `workspace.mcp.oauth.complete` | `alias`, complete `callbackUrl` |
61
84
  | `workspace.mcp.complete` | `server`, completion `ref` and `argument`; optional `context` |
62
85
 
63
- The model manages the same family through ````` ````mcp (list|discover|add|enable|disable|remove) `````.
86
+ `add` persists a workspace definition; `remove` undoes it, restoring any inherited
87
+ definition and enabled state. Use `disable` to suppress an inherited server.
88
+ MCP management neither installs nor deletes plugins or configuration files.
89
+
90
+ An AG-UI client sends each as the ordinary management action under
91
+ `forwardedProps.plurnk.action`, and the standard `plurnk.action.result` event reports the result or
92
+ exact RFC 9457 Problem Details:
93
+
94
+ ```json
95
+ { "forwardedProps": { "plurnk": { "workspace": "example", "action": {
96
+ "kind": "workspace.mcp.add",
97
+ "definition": { "name": "brave", "type": "stdio", "command": "npx", "args": ["-y", "@brave/brave-search-mcp-server@2.1.0"] }
98
+ } } } }
99
+ ```
100
+
101
+ The model manages the same family through ````` ````mcp (list|discover|add|enable|disable|remove) `````,
102
+ each change a proposal under the loop's policy. Disabling is durable and workspace-shared; enabling an
103
+ unavailable server retries its connection.
64
104
 
65
105
  Tool discovery uses ordinary `FIND (worker:///_plurnk/tools/*.md)` and READ.
66
106
  Each server's document lists enabled tool calls with required-field previews
@@ -69,110 +109,53 @@ The manager uses the same layout under `plurnk/mcp.md` and `plurnk/mcp/`;
69
109
  schema documents preserve descriptions and constraints without adding them to
70
110
  the initial survey ({§tools-resource-discovery}).
71
111
 
72
- The owning [specification](./SPEC.md) defines the complete action and server
73
- definition contracts.
74
-
75
- Client and project configuration can specialize a cold service definition
76
- without copying it or restarting the daemon. For example, a service catalog
77
- can provide the executable while one project's `.env` supplies its identity:
112
+ ## Operator settings
78
113
 
79
- ```text
80
- # $XDG_CONFIG_HOME/plurnk/.env, read by the service
81
- PLURNK_MCP_project=/opt/mcp/current-server
82
- PLURNK_MCP_ENABLED=[]
83
-
84
- # <project>/.env, read by the client
85
- PLURNK_MCP_project_ARGS=["--stdio","--project","example"]
86
- ```
114
+ Independent controls use the same lowercase alias spelling as definitions
115
+ ({§mcp-server-settings}). A control may precede its server; it is validated
116
+ without creating one.
87
117
 
88
- The client carries its raw declarations while listing and enabling. Listing is
89
- inert. `/mcp enable project` (or `plurnk mcp enable project` in a bound conversation)
90
- composes service, durable workspace, client, and optional command-file fields
91
- in that order, prepares the connection, then persists the complete unexpanded
92
- workspace definition. Arrays and maps replace rather than append or merge.
93
- Reapplying an identical definition is idempotent. A different definition for
94
- the same workspace alias requires explicit removal before replacement.
95
-
96
- ## Demo fixtures
97
-
98
- Web discovery is an ordinary MCP attachment ({§web-search-retrieval}); the demo
99
- tier exercises search through a documented fixture rather than an owned
100
- runtime. Two service-owned definitions are permitted to participate in demos
101
- of MCP and model behavior — Gitea and Brave Search:
102
-
103
- ```text
104
- # $XDG_CONFIG_HOME/plurnk/.env, read by the service — demo fixtures; never default-enabled
105
- PLURNK_MCP_BRAVE=npx
106
- PLURNK_MCP_BRAVE_ARGS=["-y","@brave/brave-search-mcp-server@2.1.0"]
107
- PLURNK_MCP_BRAVE_ENV={"BRAVE_API_KEY":"${BRAVE_API_KEY}"}
108
- PLURNK_MCP_BRAVE_TOOLS=["brave_web_search","brave_news_search"]
109
- PLURNK_MCP_BRAVE_READ=["brave_web_search","brave_news_search"]
110
- PLURNK_MCP_ENABLED=[]
118
+ | Variable | Setting |
119
+ |---|---|
120
+ | `PLURNK_MCP_ENABLED` | Family enabledness default |
121
+ | `PLURNK_MCP_<alias>_ENABLED` | Per-server override |
122
+ | `PLURNK_MCP_<alias>_TOOLS` | JSON array of exact tool names; absent or empty enables all, `[]` none |
123
+ | `PLURNK_MCP_EXPANDED` | JSON array of servers whose complete tool menu is surveyed at turn 0 |
124
+ | `PLURNK_MCP_REGISTRY_URL`, `PLURNK_MCP_REGISTRY_LIMIT` | Discovery endpoint (empty disables search) and result bound |
125
+
126
+ ```dotenv
127
+ PLURNK_MCP_brave_TOOLS=["brave_web_search","brave_news_search"]
128
+ PLURNK_MCP_forge_ENABLED=0
111
129
  ```
112
130
 
113
- The credential is one symbolic reference — the authoritative `BRAVE_API_KEY`
114
- environment value is expanded only while preparing the connection, never
115
- copied. The fixture admits exactly the web/news search tools and classifies
116
- them read-only; the rest of the vendor catalog is not admitted.
117
-
118
- **Pinned release and revision.** `@brave/brave-search-mcp-server@2.1.0`
119
- (stdio) pins `@modelcontextprotocol/sdk@1.29.0`, whose latest protocol
120
- revision is `2025-11-25` and which does not implement `server/discover`. The
121
- host negotiates-and-degrades ({§mcp-authority}), so the fixture connects at
122
- `2025-11-25` with the standard tool surface. Covered live by the demo story
123
- `{§web-search-retrieval}`, which researches through the real Brave MCP tool.
124
-
125
- ## Service defaults
131
+ HTTP authorization belongs in the complete definition. Bearer tokens and
132
+ OAuth client secrets are symbolic `${NAME}` environment references, resolved
133
+ only when preparing a connection; interactive grants remain in memory.
134
+ See [`.env.defaults`](./.env.defaults) for all controls and
135
+ [`SPEC.md`](./SPEC.md#mcp-configuration-configuration) for the contract.
126
136
 
127
- One `PLURNK_MCP_<server>` variable declares each available server. Its suffix
128
- case-folds to an `[a-z][a-z0-9-]*` executor and URI-authority name.
137
+ ## Demo fixture
129
138
 
130
- Streamable HTTP:
139
+ Web discovery is an ordinary MCP attachment ({§web-search-retrieval}). The demo
140
+ story that researches through it adds Brave Search to its fixture workspace, and
141
+ runs only when `BRAVE_API_KEY` is in the environment:
131
142
 
132
- ```text
133
- PLURNK_MCP_github=https://example.test/mcp
134
- PLURNK_MCP_github_BEARER=${GITHUB_TOKEN}
135
- PLURNK_MCP_github_TOOLS=["issue_read","issue_search"]
136
- PLURNK_MCP_github_READ=["issue_read","issue_search"]
137
- PLURNK_MCP_ENABLED=["github"]
138
- ```
139
-
140
- Stdio:
141
-
142
- ```text
143
- PLURNK_MCP_local=/absolute/path/to/executable
144
- PLURNK_MCP_local_ARGS=["--stdio"]
145
- PLURNK_MCP_local_CWD=/absolute/working/directory
146
- PLURNK_MCP_local_ENV={"TOKEN":"${LOCAL_TOKEN}"}
143
+ ```json
144
+ { "name": "brave", "type": "stdio", "command": "npx", "args": ["-y", "@brave/brave-search-mcp-server@2.1.0"] }
147
145
  ```
148
146
 
149
- The stdio target is one exact executable path or name, including literal
150
- whitespace. Arguments are a JSON array; the module never parses or invokes a
151
- shell command. `${NAME}` references resolve from the daemon's inherited
152
- environment only while preparing a connection.
153
-
154
- Without `CWD`, a local server uses its own workspace directory under
155
- `$XDG_STATE_HOME/plurnk` (normally `~/.local/state/plurnk`), not the daemon's
156
- launch directory. State survives reconnects and disable/remove; discovery
157
- scratch is removed after its probe closes. Use absolute project paths or an
158
- explicit `CWD` for servers that operate on a project. This default does not
159
- confine arbitrary subprocess writes.
160
-
161
- `PLURNK_MCP_<server>_TOOLS` is an optional JSON array of exact names. Absence
162
- enables every listed server tool; an array enables exactly those names; `[]`
163
- enables none. `PLURNK_MCP_<server>_READ` is an exact enabled-tool subset whose
164
- calls use Plurnk's `read` effect. Every other enabled tool conservatively uses
165
- the proposal-gated `host` effect. Remote annotations never grant effect
166
- authority.
167
-
168
- Portable timeouts and complete examples live in [`.env.defaults`](./.env.defaults).
147
+ `@brave/brave-search-mcp-server@2.1.0` pins `@modelcontextprotocol/sdk@1.29.0`,
148
+ whose latest revision is `2025-11-25` and which does not implement
149
+ `server/discover`, so the host negotiates down to its standard tool surface
150
+ ({§mcp-authority}). Its tools declare `openWorldHint` but not `readOnlyHint`,
151
+ so each search runs under the proposal policy.
169
152
 
170
153
  ## Plurnk projection
171
154
 
172
155
  | MCP surface | Plurnk surface |
173
156
  |---|---|
174
157
  | Server tools | `worker:///_plurnk/tools/<server>.md` family summary |
175
- | Enabled tool | Exact `worker:///_plurnk/tools/<server>/<encoded-tool>.json` document and ````` ````server (tool) ````` |
158
+ | Enabled tool | Exact `worker:///_plurnk/tools/<server>/<encoded-tool>.md` document and ````` ````server (tool) ````` |
176
159
  | Resource catalog | `server:///` or `server:///resources` |
177
160
  | Resource | `server:///resources/<encoded-uri>` through ordinary `FIND` and `READ` |
178
161
  | Prompt catalog | `server:///prompts` |
@@ -197,13 +180,14 @@ client.
197
180
 
198
181
  ## Authorization
199
182
 
200
- HTTP definitions support bearer references, client credentials, and
201
- interactive OAuth. Stdio never receives OAuth. Interactive add or enable returns
202
- `{ "status": 202, "authorization": { "url": "..." } }` without publishing a
203
- partial server. After the user completes that URL, the client submits its
204
- complete callback URL through `workspace.mcp.oauth.complete`. PKCE, issuer and
205
- resource validation, refresh, scope escalation, and credentials remain inside
206
- the host connection.
183
+ HTTP servers support bearer references, client credentials, and interactive
184
+ OAuth; stdio servers read their credentials from the environment. A server that
185
+ needs interactive OAuth comes up `authorization-required`, and enabling it
186
+ returns `{ "status": 202, "authorization": { "url": "..." } }` without
187
+ publishing a partial server. After the user completes that URL, the client
188
+ submits its complete callback URL through `workspace.mcp.oauth.complete`. PKCE,
189
+ issuer and resource validation, refresh, scope escalation, and credentials
190
+ remain inside the host connection.
207
191
 
208
192
  ## Verification
209
193