11# Native MCP migration — development
22
33MCP remains an open [ native release gate] ( NATIVE_MIGRATION.md ) . The Rust stdio
4- client foundation is implemented and tested. Application registration,
5- workspace/content activation, agent approvals, Settings/CLI integration,
6- Streamable HTTP, built-in SQLite compatibility, and the ShadowCode MCP server
7- still need their native implementation and verification. The stdio module alone
8- does not enable MCP tools in the application.
4+ client now connects to native Settings, CLI registration, project activation,
5+ and individually approved agent tool calls. Streamable HTTP, built-in SQLite
6+ compatibility, the ShadowCode MCP server, and broader interoperability/real-model
7+ verification are still being migrated. Existing URL definitions remain visible
8+ and inactive; this development build does not silently launch the Python client.
9+
10+ ## Enable external tools
11+
12+ In ** Settings → MCP** , register a server with its name and a JSON array containing
13+ the executable and literal arguments, then review and enable it for the current
14+ trusted project. Registration and catalog refresh do not execute the command.
15+ Four servers may be enabled per project. A grant records the project's canonical
16+ path, the definition ID, and its SHA-256 content hash.
17+
18+ Project definitions are direct ` .yaml ` , ` .yml ` , or ` .json ` files in
19+ ` .shadowcode/mcp/ ` or ` .shadow/mcp/ ` . For example:
20+
21+ ``` json
22+ {
23+ "name" : " my-tools" ,
24+ "command" : [" /absolute/path/to/mcp-server" , " --stdio" ],
25+ "description" : " Tools for this project" ,
26+ "timeout_sec" : 30 ,
27+ "env_refs" : { "API_TOKEN" : " MY_TOOLS_API_KEY" }
28+ }
29+ ```
30+
31+ ` env_refs ` maps child environment variables to names in the protected application
32+ secret store or the application's environment. Referenced values resolve only
33+ when a connection starts. Legacy literal ` env ` entries remain supported, but
34+ catalogs show only their variable names. Configured environment values are
35+ redacted from returned tool metadata and results; stderr is not put in task
36+ history. Avoid placing credentials in executable arguments, URLs, descriptions,
37+ or task prompts. External servers run with the user's OS privileges; activation
38+ is not an OS sandbox or a guarantee of network confinement.
39+
40+ The same controls are available without a display:
41+
42+ ``` sh
43+ shadowcode mcp add /path/to/definition.json
44+ shadowcode --json mcp
45+ shadowcode mcp enable config:my-tools --hash HASH_FROM_CATALOG
46+ shadowcode mcp disable config:my-tools
47+ shadowcode mcp remove config:my-tools --hash HASH_FROM_CATALOG
48+ ```
49+
50+ Project IDs use ` project:.shadowcode/mcp/my-tools.yaml ` . Global IDs use
51+ ` config:my-tools ` . Replacing a global definition with ` mcp add ` requires its
52+ current ` --hash ` . Removing it also removes its project grants, so re-registering
53+ identical content does not reactivate it. Project overlays cannot grant access.
54+ Changed or missing project definitions must be reviewed or disabled before a
55+ new Build task uses them.
56+
57+ Build tasks with enabled servers receive two compact tools: ` mcp_tools ` discovers
58+ servers, lists tool names in pages of 20, or reads one exact tool schema;
59+ ` mcp_call ` requests a call to a server/tool with an argument object. Initialization
60+ is lazy and uses the explicit launch grant. Every ` mcp_call ` requires a separate
61+ session-scoped approval, even if the peer marks its tool read-only. The server
62+ ID, tool name, and complete arguments appear in that approval. Plan, Review,
63+ and untrusted projects cannot start MCP processes.
64+
65+ Running and queued tasks retain their configuration snapshot. Disabling a grant
66+ or replacing a global registration applies to new tasks; cancel existing tasks
67+ to stop their current connections. Project files are re-read and their hash
68+ checked before each external action, including after an approval wait.
69+ Definitions cannot replace their command while an earlier approval is pending.
70+ MCP actions invalidate earlier file observations and disable parallel native
71+ reads for that task. Changes made by an external server are not checkpointed by
72+ ShadowCode's file tools.
973
1074## Stdio client foundation
1175
@@ -19,7 +83,7 @@ The omitted crate license is retained from that same commit in
1983The low-level ` Client::connect ` API starts only the literal command supplied by
2084its caller, in a canonical project directory. It does not discover or activate
2185servers. Callers must obtain server activation and exact tool approval before
22- using it; those application controls are the next integration step . A server's
86+ using it; the application runner supplies those controls . A server's
2387tool annotations, descriptions or instructions cannot supply approval.
2488
2589The child receives basic path/home/locale/XDG environment variables and explicitly
@@ -51,6 +115,8 @@ Unsolicited sampling fails and elicitation is declined. Interactive or backgroun
51115task continuations returned from a tool are rejected rather than automatically
52116supplying inputs or starting an unattended polling loop. A failed call does not
53117prove that the remote tool made no changes; the client never retries it silently.
118+ A closed connection cannot return its old catalog as if it were still live;
119+ start a new task to reconnect after a protocol failure.
54120
55121## Ownership and cancellation
56122
@@ -63,13 +129,17 @@ Processes that deliberately escape their group are outside this user-level
63129cleanup boundary.
64130
65131` Client::close ` waits for cleanup. Dropping a client or abandoning its connection
66- future triggers cleanup asynchronously; application task integration must await
67- close before reporting task completion or releasing its workspace reservation.
132+ future immediately signals its group to stop; the cleanup worker reaps it.
133+ The application task loop awaits all connections before reporting completion or
134+ releasing the workspace, including provider errors, step limits, cancellation,
135+ and shutdown. Connections are scoped to one task and reused within that task.
136+ Durable ` mcp.connected ` and ` mcp.closed ` events retain the definition ID/hash
137+ and cleanup outcome without environment values.
68138
69139## Verification
70140
71141``` sh
72- cargo test -p shadowcode-core --test mcp_stdio --locked
142+ cargo test -p shadowcode-core --test mcp_stdio --test mcp_application -- locked
73143```
74144
75145Ten regressions exercise real subprocess pipes, current and legacy protocol
@@ -80,6 +150,17 @@ stubborn descendant cleanup, leader reaping, stderr floods, protocol floods, and
80150refusal of unsolicited sampling/credential requests. The small Node peer is a
81151test fixture; no Node or Python runtime is added to the application.
82152
83- These checks validate the client foundation. They do not stand in for the
84- remaining application permission, UI, HTTP, server, interoperability and real
85- local-model checks required for the full MCP migration.
153+ Eight application regressions cover inert/symlink-confined discovery, malformed
154+ definitions, hidden credentials, project trust and permissions, definition
155+ hashes, removal/re-registration, exact argument approval and denial, connection
156+ reuse, error-result preservation, bounded single-pass credential redaction,
157+ closed-catalog rejection, stale file observations, read-only tasks, and
158+ process cleanup before actual engine completion on every exit path. The native
159+ CLI and real-window probes also exercise their respective controls; the window
160+ probe drives a scripted model through discovery, approval, a real subprocess
161+ tool result, and cleanup.
162+
163+ These checks do not stand in for the remaining HTTP, server, interoperability,
164+ and real local-model checks required for the full MCP migration. The standalone
165+ application does not bundle runtimes for third-party servers: install whatever
166+ an explicitly selected external command requires separately.
0 commit comments