You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/INDEX.md
+3Lines changed: 3 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -11,6 +11,8 @@
11
11
| Task Execution Model |[task-execution-model-en.md](./task-execution-model-en.md)|[task-execution-model-zh.md](./task-execution-model-zh.md)| Task 执行模型与方案 A 约定 |
This document proposes how to integrate external MCP (Model Context Protocol) servers into Spear, aligned with common industry practices:
5
+
This document describes the current MCP (Model Context Protocol) integration in Spear, based on the code in this repository:
6
6
7
7
- Spear stores a registry of MCP servers that are allowed to connect.
8
8
- At the agent layer, MCP tools are exposed to Chat Completions as standard `tools` so the agent can be unaware of MCP.
9
-
-Additionally, a dedicated set of MCP hostcalls is provided so WASM workloads can call MCP tools programmatically.
9
+
-MCP tools are injected into Chat Completions as standard `tools` entries and executed by the host auto tool-call loop.
10
10
11
-
The design is intended to reuse Spear’s existing fd-based hostcall model and its existing “auto tool call loop” for Chat Completion.
11
+
Note: there is no separate `mcp_*` hostcall surface in the current implementation; MCP access is currently integrated via `cchat` only.
12
12
13
13
## Goals
14
14
15
15
- Centralize external MCP server configuration, policy, and credentials in Spear.
16
16
- Make MCP tools available to Chat Completion tool-calling without requiring agent-side MCP awareness.
17
-
- Provide an fd-based MCP hostcall API for explicit tool calls from WASM.
18
17
- Ensure safety by default (deny-by-default, allowlists, namespacing, budgets, auditability).
19
-
- Support multiple transports (stdio for local subprocess; Streamable HTTP for remote).
18
+
- Support stdio-based MCP servers (local subprocess) end-to-end.
19
+
- The registry schema also allows `streamable_http`, but Spearlet-side execution currently supports stdio only.
20
20
21
21
## Non-goals
22
22
23
23
- Implementing a full MCP gateway for third-party clients outside Spear.
24
-
- Implementing every MCP capability category on day one (resources/prompts can be phased in).
25
-
- Allowing arbitrary, user-provided subprocess spawning without policy controls.
24
+
- Implementing a dedicated `mcp_open/list_tools/call_tool/close` hostcall API (future work).
25
+
- Executing Streamable HTTP MCP servers in Spearlet (future work).
26
+
- Allowing arbitrary, user-provided subprocess spawning without registry policy controls.
26
27
27
28
## Current Spear foundations to reuse
28
29
@@ -39,39 +40,32 @@ References:
39
40
-**MCP Server**: an external process/service exposing `tools/list` and `tools/call`.
40
41
-**Spear MCP Registry**: Spear-managed list of allowed MCP servers and policies.
41
42
-**Tool injection**: converting MCP tools into OpenAI-compatible `tools` entries used by Chat Completions.
42
-
-**Namespaced tool name**: a stable name that avoids collisions, e.g. `mcp.<server_id>.<tool_name>`.
43
+
-**Namespaced tool name**: a stable tool name exposed to the model.
44
+
- Injected form (current): `mcp__<base64(server_id)>__<base64(tool_name)>`
45
+
- Accepted for routing (compat): `mcp.<server_id>.<tool_name>`
43
46
44
47
## High-level architecture
45
48
46
49
### Components
47
50
48
-
1.**MCP Registry Service (control plane)**
49
-
-Implemented in SMS as the single source of truth.
50
-
-Stores MCP server registrations, policy, and credential references.
51
-
-Spearlets fetch and cache registry data from SMS (revision-based).
51
+
1.**MCP Registry (SMS, control plane)**
52
+
- SMS keeps an in-memory registry of MCP server records.
53
+
-SMS can bootstrap records from a directory of `*.toml`/`*.json` config files.
54
+
-Spearlet fetches registry records from SMS via gRPC and keeps a local snapshot.
52
55
53
-
2.**MCP Client Pool (data plane, per Spearlet)**
54
-
- Maintains connections to MCP servers.
55
-
- Provides:
56
-
- tools discovery (with caching)
57
-
- tool execution with timeouts, concurrency limits, output caps
56
+
2.**Registry sync (Spearlet, data plane)**
57
+
- Spearlet runs a watch+poll sync loop to keep the registry snapshot fresh.
58
58
59
-
3.**Chat Completion Tool Bridge (data plane, per chat session)**
60
-
- Determines which MCP servers are enabled for the current chat session.
61
-
- Injects MCP tools into the upstream chat request.
62
-
- Routes tool calls returned by the model to either WASM tools or MCP tools.
59
+
3.**Stdio MCP client (Spearlet, data plane)**
60
+
- For each `tools/list` and `tools/call`, Spearlet spawns the configured MCP server subprocess via stdio and speaks MCP using `rmcp`.
61
+
- Current limitation: stdio only (even though the registry schema includes `streamable_http`).
63
62
64
-
4.**MCP Hostcall Surface (data plane, for WASM workloads)**
65
-
-Exposes an fd-based API to connect/list/call MCP tools explicitly.
63
+
4.**Chat Completion injection + execution (Spearlet, per chat session)**
64
+
-The `cchat` host API injects MCP tools (via `tools/list`) and executes MCP tool calls (via `tools/call`) inside the existing auto tool-call loop.
66
65
67
-
### Two usage modes
66
+
### Usage mode (current)
68
67
69
-
-**Agent-unaware mode (recommended default)**
70
-
- The agent only uses Chat Completions tool calling.
71
-
- Spear injects MCP tools and executes them automatically.
72
-
73
-
-**Programmable hostcall mode**
74
-
- WASM code calls MCP tools directly via `mcp_*` hostcalls.
68
+
- MCP tools are injected and executed only through the `cchat` auto tool-call loop.
75
69
76
70
## MCP Server Registry
77
71
@@ -118,8 +112,8 @@ Data-plane access patterns:
118
112
119
113
Best practice is to avoid flat namespaces. Spear should expose MCP tools to the model using a deterministic namespace.
120
114
121
-
- External tool name presented to the model: `mcp.<server_id>.<tool_name>`
122
-
- Internal routing: parse prefix, map to `(server_id, tool_name)`
115
+
- External tool name presented to the model (injected): `mcp__<base64(server_id)>__<base64(tool_name)>`
116
+
- Internal routing: decode to `(server_id, tool_name)`; routing also accepts `mcp.<server_id>.<tool_name>` for compatibility.
123
117
124
118
This makes audit logs and policy enforcement straightforward and avoids tool name conflicts across servers.
125
119
@@ -161,7 +155,7 @@ Recommended tool calling policy (passed to the upstream model as request params)
161
155
162
156
-`tool_choice = "none"`: user explicitly disables tool calling for this request.
163
157
-`tool_choice = "auto"`: default; model may choose among the already-filtered tools.
164
-
-`tool_choice = {"type":"function","function":{"name":"mcp.<server_id>.<tool_name>"}}`: user selected a specific tool; force the model to use it.
158
+
-`tool_choice = {"type":"function","function":{"name":"mcp__...__..."}}`: user selected a specific tool; force the model to use it (the exact name must match an injected tool).
165
159
166
160
Product UX guideline:
167
161
@@ -179,6 +173,10 @@ When building the Chat Completions request, Spear constructs:
179
173
- per-session allowlist overrides
180
174
- global governance policies
181
175
176
+
Notes (current code):
177
+
178
+
- MCP session params (`mcp.*`) are internal host-side controls. They are materialized into the session param map, but are not forwarded to the upstream model request body.
179
+
182
180
### Executing tool calls
183
181
184
182
Spear reuses the existing auto tool-call loop:
@@ -187,7 +185,7 @@ Spear reuses the existing auto tool-call loop:
187
185
2. If the model returns `tool_calls`:
188
186
- For each call:
189
187
- If name matches a WASM tool, invoke WASM function by `fn_offset`.
190
-
- If name matches `mcp.<server_id>.<tool_name>`, call MCP `tools/call`.
188
+
- If name matches `mcp__...__...` (or the compat `mcp.<server_id>.<tool_name>`), call MCP `tools/call`.
191
189
- Append each tool result as `role=tool` with the correct `tool_call_id`.
192
190
3. Repeat until no more tool calls or budgets are exceeded.
193
191
@@ -200,51 +198,7 @@ Budgets and safety limits should be enforced exactly the same way for both WASM
200
198
201
199
## MCP hostcalls (programmable API)
202
200
203
-
### Design principles
204
-
205
-
- fd-based, syscall-like API consistent with existing `cchat_*`.
206
-
- Avoid exposing raw spawning capabilities without registry policy.
207
-
- Prefer calling registered servers by `server_id`.
208
-
209
-
### Proposed hostcall set
210
-
211
-
#### 1) `mcp_open(server_id) -> mcp_fd`
212
-
213
-
- Opens a session/connection handle to a registered MCP server.
214
-
-`server_id` is resolved via the registry.
215
-
- The host establishes (or reuses) a connection from the client pool.
Registry mutation (register/update/delete) is recommended to remain in control-plane APIs, not hostcalls.
201
+
This section describes potential future work. The current implementation does not expose `mcp_*` hostcalls; MCP access is integrated via `cchat` only.
248
202
249
203
## Security and governance
250
204
@@ -293,30 +247,18 @@ This section is implementation-oriented. It proposes concrete module boundaries,
293
247
294
248
### Code layout (recommended)
295
249
296
-
Split the MCP integration into three independent parts: registry (in SMS, control plane), client (in Spearlet, data plane), and bridge/hostcalls (in Spearlet, integration plane).
297
-
298
-
-`src/sms/mcp/registry/`
299
-
-`types.rs`: registry record, policy, budgets
300
-
-`store.rs`: persistent store + revision
301
-
-`service.rs`: registry business logic (CRUD + validation)
302
-
-`http.rs`: public API (`/api/v1/mcp/*`) and admin API (`/admin/api/mcp/*`)
303
-
-`src/spearlet/mcp/registry_client/`
304
-
-`client.rs`: fetch registry from SMS (revision-aware)
305
-
-`cache.rs`: in-memory cache (TTL + revision)
306
-
-`src/spearlet/mcp/client/`
307
-
-`transport/mod.rs`: `McpTransport` trait
308
-
-`transport/stdio.rs`: stdio subprocess transport
309
-
-`transport/http_streamable.rs`: Streamable HTTP transport
310
-
-`jsonrpc.rs`: JSON-RPC 2.0 encode/decode
311
-
-`types.rs`: MCP Tool/CallResult structs
312
-
-`pool.rs`: connection pool, concurrency, reconnect, health
- MCP fd API (`mcp_open/list_tools/call_tool/close`) using the shared pool
250
+
Current code map:
251
+
252
+
- SMS
253
+
- Bootstrap MCP configs from a directory: [`src/apps/sms/main.rs`](../src/apps/sms/main.rs) and [`src/sms/service.rs`](../src/sms/service.rs) (`bootstrap_mcp_from_dir`)
Extend the existing auto tool-call loop with a unified dispatcher:
673
611
674
612
- if tool name matches a WASM tool: call by `fn_offset`
675
-
- if tool name matches `mcp.<server_id>.<tool_name>`:
613
+
- if tool name matches `mcp__...__...` (or compat `mcp.<server_id>.<tool_name>`):
676
614
- parse `server_id/tool_name`
677
615
- parse `arguments` into a JSON object (return structured error on failure)
678
616
- call MCP `tools/call`
679
617
680
618
Return tool output as a JSON string (success or error) and append as `role=tool`.
681
619
682
-
### MCP hostcalls: fd model and ABI
620
+
### MCP hostcalls: fd model and ABI (future work)
683
621
684
622
#### fd kind
685
623
686
-
Introduce`FdKind::McpSession` (or a tagged generic) with internal state:
624
+
Not implemented in the current codebase. If added later, a possible direction is to introduce`FdKind::McpSession` (or a tagged generic) with internal state:
687
625
688
626
-`server_id`
689
627
- optional: pool handle reference
@@ -744,12 +682,12 @@ Audit logs should include `request_id/session_id/server_id/tool_name/status` wit
744
682
### Minimal test plan
745
683
746
684
- Unit tests
747
-
- route parsing for `mcp.<server_id>.<tool_name>`
685
+
- route parsing for `mcp__...__...` (and compat `mcp.<server_id>.<tool_name>`)
748
686
- allow/deny pattern matching
749
687
- injection filtering
750
688
- Integration tests (tokio)
751
689
- stdio: spawn a fake MCP server subprocess for `tools/list` and `tools/call`
752
-
- HTTP: start a local axum mock for Streamable HTTP
690
+
-Streamable HTTP: add a local mock when/if the transport is implemented
753
691
- cchat auto tool-call loop: inject MCP tools and verify `role=tool` append behavior
0 commit comments