Skip to content

Commit 37df49e

Browse files
committed
update
1 parent 381e007 commit 37df49e

208 files changed

Lines changed: 1614 additions & 19365 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Cargo.lock

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/sms/mcp.d/fs.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ args = ["-y", "@modelcontextprotocol/server-filesystem", "./"]
1010
cwd = ""
1111

1212
[budgets]
13-
tool_timeout_ms = 8000
13+
tool_timeout_ms = 30000

docs/INDEX.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
| 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 约定 |
1212
| SMS Terminology | [sms-terminology-en.md](./sms-terminology-en.md) | [sms-terminology-zh.md](./sms-terminology-zh.md) | SMS术语和架构说明 |
1313
| ObjectRef API Removal | [objectref-api-removal-en.md](./objectref-api-removal-en.md) | [objectref-api-removal-zh.md](./objectref-api-removal-zh.md) | ObjectRef API移除文档 |
14+
| MCP Integration Architecture | [mcp-integration-architecture-en.md](./mcp-integration-architecture-en.md) | [mcp-integration-architecture-zh.md](./mcp-integration-architecture-zh.md) | MCP 注册中心、注入与执行链路 |
15+
| Task-level MCP Subset Design | [mcp-task-subset-design-en.md](./mcp-task-subset-design-en.md) | [mcp-task-subset-design-zh.md](./mcp-task-subset-design-zh.md) | Task 级 MCP 子集选择与治理 |
1416

1517
### 💾 Storage Layer / 存储层
1618

@@ -85,6 +87,7 @@
8587
| 文档 / Document | 英文版 / English | 中文版 / Chinese | 描述 / Description |
8688
|---|---|---|---|
8789
| gRPC Transport Error | [grpc-transport-error-troubleshooting-en.md](./grpc-transport-error-troubleshooting-en.md) | [grpc-transport-error-troubleshooting-zh.md](./grpc-transport-error-troubleshooting-zh.md) | gRPC传输错误故障排除指南 |
90+
| MCP Troubleshooting | [mcp-troubleshooting-en.md](./mcp-troubleshooting-en.md) | [mcp-troubleshooting-zh.md](./mcp-troubleshooting-zh.md) | MCP 工具注入与执行排障 |
8891
| Ollama Model Discovery | [ollama-discovery-en.md](./ollama-discovery-en.md) | [ollama-discovery-zh.md](./ollama-discovery-zh.md) | Ollama 模型导入与排障 |
8992
| API Usage Guide | [api-usage-guide-en.md](./api-usage-guide-en.md) | [api-usage-guide-zh.md](./api-usage-guide-zh.md) | RESTful API使用指南 |
9093
| WASM Runtime Usage | [wasm-runtime-usage-en.md](./wasm-runtime-usage-en.md) | [wasm-runtime-usage-zh.md](./wasm-runtime-usage-zh.md) | WASM运行时使用与错误行为说明 |

docs/mcp-integration-architecture-en.md

Lines changed: 61 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,29 @@
1-
# MCP Integration Architecture (Registry + Tool Injection + Hostcalls)
1+
# MCP Integration Architecture (Current Implementation)
22

33
## Overview
44

5-
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:
66

77
- Spear stores a registry of MCP servers that are allowed to connect.
88
- 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.
1010

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.
1212

1313
## Goals
1414

1515
- Centralize external MCP server configuration, policy, and credentials in Spear.
1616
- 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.
1817
- 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.
2020

2121
## Non-goals
2222

2323
- 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.
2627

2728
## Current Spear foundations to reuse
2829

@@ -39,39 +40,32 @@ References:
3940
- **MCP Server**: an external process/service exposing `tools/list` and `tools/call`.
4041
- **Spear MCP Registry**: Spear-managed list of allowed MCP servers and policies.
4142
- **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>`
4346

4447
## High-level architecture
4548

4649
### Components
4750

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.
5255

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.
5858

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`).
6362

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.
6665

67-
### Two usage modes
66+
### Usage mode (current)
6867

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.
7569

7670
## MCP Server Registry
7771

@@ -118,8 +112,8 @@ Data-plane access patterns:
118112

119113
Best practice is to avoid flat namespaces. Spear should expose MCP tools to the model using a deterministic namespace.
120114

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.
123117

124118
This makes audit logs and policy enforcement straightforward and avoids tool name conflicts across servers.
125119

@@ -161,7 +155,7 @@ Recommended tool calling policy (passed to the upstream model as request params)
161155

162156
- `tool_choice = "none"`: user explicitly disables tool calling for this request.
163157
- `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).
165159

166160
Product UX guideline:
167161

@@ -179,6 +173,10 @@ When building the Chat Completions request, Spear constructs:
179173
- per-session allowlist overrides
180174
- global governance policies
181175

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+
182180
### Executing tool calls
183181

184182
Spear reuses the existing auto tool-call loop:
@@ -187,7 +185,7 @@ Spear reuses the existing auto tool-call loop:
187185
2. If the model returns `tool_calls`:
188186
- For each call:
189187
- 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`.
191189
- Append each tool result as `role=tool` with the correct `tool_call_id`.
192190
3. Repeat until no more tool calls or budgets are exceeded.
193191

@@ -200,51 +198,7 @@ Budgets and safety limits should be enforced exactly the same way for both WASM
200198

201199
## MCP hostcalls (programmable API)
202200

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.
216-
217-
#### 2) `mcp_list_tools(mcp_fd, out_buf, out_len_ptr) -> rc`
218-
219-
- Returns JSON with a stable schema:
220-
221-
```json
222-
{
223-
"server_id": "fs",
224-
"tools": [
225-
{"name": "read_file", "description": "...", "inputSchema": {"type":"object", "properties":{}}}
226-
]
227-
}
228-
```
229-
230-
#### 3) `mcp_call_tool(mcp_fd, tool_name, args_json, out_buf, out_len_ptr) -> rc`
231-
232-
- `tool_name` is the MCP-native name (without the `mcp.<server_id>.` prefix).
233-
- `args_json` is a UTF-8 JSON string.
234-
- Returns JSON string output (success or error) in `out_buf`.
235-
236-
#### 4) `mcp_close(mcp_fd) -> rc`
237-
238-
- Releases the handle; the host may keep pooled connections alive.
239-
240-
### Optional hostcalls
241-
242-
If WASM needs discovery of registered servers:
243-
244-
- `mcp_registry_list(out_buf, out_len_ptr) -> rc`
245-
- `mcp_registry_get(server_id, out_buf, out_len_ptr) -> rc`
246-
247-
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.
248202

249203
## Security and governance
250204

@@ -293,30 +247,18 @@ This section is implementation-oriented. It proposes concrete module boundaries,
293247

294248
### Code layout (recommended)
295249

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
313-
- `cache.rs`: tools/list cache (TTL + revision)
314-
- `src/spearlet/mcp/bridge/`
315-
- `tool_injection.rs`: MCP tools -> OpenAI tools mapping + filtering
316-
- `router.rs`: parse and route `mcp.<server_id>.<tool_name>`
317-
- `policy.rs`: session allow/deny + approval policy enforcement
318-
- `src/spearlet/execution/host_api/mcp.rs`
319-
- 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`)
254+
- MCP registry gRPC service: [`src/sms/service.rs`](../src/sms/service.rs) (`McpRegistryService`)
255+
- Web Admin MCP endpoints: [`src/sms/web_admin.rs`](../src/sms/web_admin.rs)
256+
- Spearlet
257+
- Registry sync (watch + periodic refresh): [`src/spearlet/mcp/registry_sync.rs`](../src/spearlet/mcp/registry_sync.rs)
258+
- Stdio MCP client wrapper (rmcp): [`src/spearlet/mcp/client.rs`](../src/spearlet/mcp/client.rs)
259+
- Tool naming + allow/deny policy + routing helpers: [`src/spearlet/mcp/policy.rs`](../src/spearlet/mcp/policy.rs)
260+
- Task-level subset policy parsing: [`src/spearlet/mcp/task_subset.rs`](../src/spearlet/mcp/task_subset.rs)
261+
- Tool injection + execution in cchat: [`src/spearlet/execution/host_api/cchat.rs`](../src/spearlet/execution/host_api/cchat.rs)
320262

321263
### Configuration and registry persistence
322264

@@ -340,9 +282,9 @@ Suggested behavior:
340282
Suggested config naming (examples):
341283

342284
- SMS:
343-
- CLI: `--mcp-registry-dir <DIR>`
344-
- ENV: `SMS_MCP_REGISTRY_DIR=<DIR>`
345-
- Config: `mcp.registry_dir = "..."`
285+
- CLI: `--mcp-dir <DIR>`
286+
- ENV: `SMS_MCP_DIR=<DIR>`
287+
- Config file: `[mcp]\ndir = "..."` (in `sms` config)
346288

347289
Directory scan rules (recommended):
348290

@@ -356,19 +298,18 @@ File schema: prefer “one file per server record”.
356298
Example (TOML, single server per file):
357299

358300
```toml
359-
version = 1
360301
server_id = "fs"
361302
display_name = "Filesystem"
362303
transport = "stdio"
363-
tool_namespace = "mcp.fs"
304+
tool_namespace = ""
364305
allowed_tools = ["read_*", "search_*"]
365306

366307
[stdio]
367-
command = "uvx"
368-
args = ["xxx@latest"]
308+
command = "npx"
309+
args = ["-y", "@modelcontextprotocol/server-filesystem", "./"]
369310

370311
[budgets]
371-
tool_timeout_ms = 8000
312+
tool_timeout_ms = 30000
372313
max_concurrency = 8
373314
max_tool_output_bytes = 65536
374315
```
@@ -508,15 +449,12 @@ Recommended UI capabilities:
508449
- Tool preview (optional): show the post-filter tool list (including namespaced names)
509450
- Import from file (optional): upload a registry file, validate, then upsert
510451

511-
Recommended backend endpoints under the existing `/admin/api` prefix, reusing current optional auth:
452+
Current backend endpoints under the existing `/admin/api` prefix (implemented in `sms` Web Admin gateway):
512453

513454
- `GET /admin/api/mcp/servers`
514455
- `GET /admin/api/mcp/servers/{server_id}`
515-
- `POST /admin/api/mcp/servers`
516-
- `PUT /admin/api/mcp/servers/{server_id}`
456+
- `POST /admin/api/mcp/servers` (upsert)
517457
- `DELETE /admin/api/mcp/servers/{server_id}`
518-
- `POST /admin/api/mcp/servers/{server_id}/test` (optional)
519-
- `POST /admin/api/mcp/servers/import` (optional)
520458

521459
### Function/method-level details (recommended)
522460

@@ -663,7 +601,7 @@ Input: `session_params + registry_records + cached_tools`.
663601
2. For each server_id:
664602
- load registry record
665603
- list_tools (via cache)
666-
- map tool name to `mcp.<server_id>.<tool_name>`
604+
- map tool name to injected OpenAI tool name `mcp__<base64(server_id)>__<base64(tool_name)>`
667605
- filter by registry allowlist + session allow/deny
668606
3. Append MCP tools to the `tools` array (merge with WASM tools).
669607

@@ -672,18 +610,18 @@ Input: `session_params + registry_records + cached_tools`.
672610
Extend the existing auto tool-call loop with a unified dispatcher:
673611

674612
- 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>`):
676614
- parse `server_id/tool_name`
677615
- parse `arguments` into a JSON object (return structured error on failure)
678616
- call MCP `tools/call`
679617

680618
Return tool output as a JSON string (success or error) and append as `role=tool`.
681619

682-
### MCP hostcalls: fd model and ABI
620+
### MCP hostcalls: fd model and ABI (future work)
683621

684622
#### fd kind
685623

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:
687625

688626
- `server_id`
689627
- optional: pool handle reference
@@ -744,12 +682,12 @@ Audit logs should include `request_id/session_id/server_id/tool_name/status` wit
744682
### Minimal test plan
745683

746684
- Unit tests
747-
- route parsing for `mcp.<server_id>.<tool_name>`
685+
- route parsing for `mcp__...__...` (and compat `mcp.<server_id>.<tool_name>`)
748686
- allow/deny pattern matching
749687
- injection filtering
750688
- Integration tests (tokio)
751689
- 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
753691
- cchat auto tool-call loop: inject MCP tools and verify `role=tool` append behavior
754692
- Regression
755693
- behavior unchanged when MCP is disabled

0 commit comments

Comments
 (0)