FN-7033: document MCP operator workflows

Add a canonical MCP guide with operator workflows and contract coverage.

- Add docs/mcp.md covering MCP settings resolution, transports, secret references, validation, dashboard management, CLI commands, import/export, and AI lane forwarding.
- Link the MCP guide from the docs index, CLI reference, settings reference, secrets docs, and dashboard guide.
- Add a documentation contract test that checks required guide sections and source-aligned MCP surfaces.

Files changed:
 docs/README.md                                     |   1 +
 docs/cli-reference.md                              |   2 +-
 docs/dashboard-guide.md                            |   2 +-
 docs/mcp.md                                        | 221 +++++++++++++++++++++
 docs/secrets.md                                    |   2 +-
 docs/settings-reference.md                         |   2 +
 .../src/__tests__/mcp-documentation.test.ts        |  65 ++++++
 7 files changed, 292 insertions(+), 3 deletions(-)

Fusion-Task-Id: FN-7033

Fusion-Task-Lineage: 530abc69-0247-49ee-9487-54d8e2a1b988
This commit is contained in:
gsxdsm
2026-06-26 02:52:51 -07:00
parent 8131d541ca
commit 3bfdb16650
7 changed files with 292 additions and 3 deletions

View File

@@ -46,6 +46,7 @@ For a full walkthrough (installation, onboarding, first task, and daily workflow
| Guide | Description |
|---|---|
| [Settings Reference](./settings-reference.md) | Global/project settings, workflow setting values, model/fallback lane hierarchy, defaults, and API endpoints |
| [MCP](./mcp.md) | Model Context Protocol server configuration, secret references, validation, CLI, dashboard, and import/export workflows |
| [Agents](./agents.md) | Agent management, presets, prompts, heartbeat behavior, spawning, and mailbox workflows |
### Architecture & Development

View File

@@ -988,7 +988,7 @@ fn settings import <file> [--scope global|project|both] [--merge] [--yes]
## `fn mcp`
Manage Fusion MCP server definitions for stdio, SSE, and streamable HTTP transports.
Manage Fusion MCP server definitions for stdio, SSE, and streamable HTTP transports. See [MCP](./mcp.md) for the full configuration and usage guide, including dashboard flows and secret-reference behavior.
```bash
fn mcp list [--project <name>] [--json]

View File

@@ -1512,7 +1512,7 @@ Manage project and global secrets directly inside **Settings → Project → Sec
### MCP server management in Settings
Manage Model Context Protocol servers from the existing Settings modal; no new top-level dashboard view is introduced.
Manage Model Context Protocol servers from the existing Settings modal; no new top-level dashboard view is introduced. See [MCP](./mcp.md) for the full setup, validation, CLI, import, and export guide.
- **Settings → Global → MCP Servers** stores global MCP defaults shared by projects.
- **Settings → Project → MCP Servers** stores project-level MCP settings. Project entries override global servers by matching `name`, and a same-named disabled project entry suppresses the inherited global server. The project list marks inherited, overridden, project-local, and disabled-global states so operators can see which scope owns the effective entry.

221
docs/mcp.md Normal file
View File

@@ -0,0 +1,221 @@
# MCP (Model Context Protocol)
[← Back to docs index](./README.md)
MCP (Model Context Protocol) is a standard way to attach external tool servers to AI runtimes. Fusion stores trusted MCP server definitions in settings, resolves the effective global/project configuration, materializes any referenced secrets only at use time, and forwards enabled servers to MCP-capable AI lanes so those lanes can use the same operator-approved tools.
<!--
FNXC:McpDocs 2026-06-26-00:00:
This page is the canonical MCP operator guide. Keep CLI flags, settings keys, validation statuses, and dashboard section names aligned with the shipped MCP code so secret-reference behavior is documented once without duplicating full procedures across adjacent docs.
-->
## Overview
MCP support lets Fusion operators configure external stdio, SSE, or streamable HTTP MCP servers once and make them available to AI sessions that support MCP. Enabled servers are treated as **trusted once configured**: after an operator saves a server definition, Fusion may forward it to supported AI lanes without asking again for each session.
MCP configuration lives in the `mcpServers` settings key at both scopes:
- **Global settings** hold shared MCP declarations.
- **Project settings** hold project-specific declarations.
- Effective resolution is project-over-global by server `name`: global servers load first, same-named project servers replace them, and same-named project servers with `enabled:false` disable the inherited global server.
- The project-level `mcpServers.enabled` flag overrides the global flag when it is set. If the effective flag is false, no MCP servers are active.
Expected outcome: when `mcpServers.enabled` resolves to true and at least one enabled server definition is valid, supported AI runtimes receive the effective server set for new sessions.
## Server definitions and transports
Every server has a unique `name`, an optional per-server `enabled` flag, and exactly one transport:
| Transport | Required fields | Optional sensitive fields | Notes |
|---|---|---|---|
| `stdio` | `command` | `env` | `args` may provide command arguments. |
| `sse` | `url` | `headers` | Uses an SSE endpoint. |
| `streamable-http` | `url` | `headers` | The CLI also accepts `http` as an alias and stores `streamable-http`. |
Definitions use these shapes:
```json
{ "name": "local-tools", "enabled": true, "transport": "stdio", "command": "node", "args": ["server.js"], "env": { "API_KEY": { "secretRef": "sec_...", "scope": "project" } } }
{ "name": "docs-sse", "transport": "sse", "url": "https://example.test/sse", "headers": { "Authorization": { "secretRef": "sec_...", "scope": "global" } } }
{ "name": "docs-http", "transport": "streamable-http", "url": "https://example.test/mcp", "headers": { "Authorization": { "secretRef": "sec_...", "scope": "project" } } }
```
Expected outcome: settings validation accepts only the required fields for the selected transport, rejects duplicate server names within one stored settings array, and rejects plaintext sensitive values.
## Secret references
Fusion never persists raw MCP environment values, header values, or token-like material in settings. Sensitive maps store only Fusion-managed secret references:
```json
{ "secretRef": "sec_...", "scope": "project" }
```
Use `scope: "project"` for secrets stored in the current project and `scope: "global"` for secrets stored in the global secrets database. The plaintext value lives in the encrypted [Secrets](./secrets.md) store, not in `mcpServers`.
Fusion materializes MCP secret references only at the use seam:
- when creating an AI session for an MCP-capable runtime;
- when running a bounded validation/reachability probe;
- when importing plaintext Claude Desktop env/header values and immediately creating Fusion secrets.
Expected outcome: API responses, CLI output, settings JSON, exports, and structured logs show secret references or counts/status metadata only; they do not include decrypted env/header values.
## Validation and reachability
The dashboard **Test** control calls `POST /api/mcp/validate` for one server. The route accepts a JSON body with either:
- `name` — resolve a configured server by name in the current project context; or
- `server` / `definition` — validate and probe the supplied server definition.
`timeoutMs` is optional, must be positive, and is capped at 30000 milliseconds. The response is:
```json
{ "status": "valid", "message": "..." }
```
`status` is one of:
| Status | Meaning |
|---|---|
| `valid` | The definition resolved, secrets materialized, and the bounded probe reached the server. |
| `unreachable` | The definition resolved, but the probe could not reach the server within the bounded check. |
| `error` | Validation, secret resolution, spawn, fetch, or protocol setup failed. |
Expected outcome: validation returns only `{ status, message? }`; resolved `env` and `headers` values are never returned.
Note: `fn mcp validate` currently validates stored definitions and reports whether they satisfy Fusion's schema. It does not perform the dashboard/API reachability probe.
## Managing servers in the dashboard
1. Open **Settings → Global → MCP Servers** for shared defaults, or **Settings → Project → MCP Servers** for project-specific servers. Expected outcome: the **Global MCP servers** or **Project MCP servers** card appears.
2. Turn on **Enable MCP servers for this scope**. Expected outcome: the current scope's `mcpServers.enabled` draft becomes true; project scope overrides global enablement when saved.
3. Click **Add server**. Choose `stdio`, `SSE`, or `HTTP`, then enter the required `command` or `url`. Expected outcome: the editor only asks for fields used by that transport and saves `HTTP` as `streamable-http`.
4. Add sensitive values under **Environment secret refs** for `stdio` or **Header secret refs** for `sse` / `streamable-http`. Choose an existing secret or create a new secret with **Create secret**. Expected outcome: the settings draft receives only `{ secretRef, scope }`; the plaintext creation value is stored in Secrets, not in settings.
5. Save the server. Expected outcome: the row appears with its transport, state badge, and validation status of **Not tested**.
6. In project settings, review inherited rows from global settings. Use **Override** to replace an inherited server or **Disable** to add a same-named project disabled entry. Expected outcome: state badges identify inherited, overridden, project-local, and disabled-global behavior before you save.
7. Click **Test** on a server row. Expected outcome: the row shows **Testing…** while pending, then `valid`, `unreachable`, or `error` with the returned message.
8. Use the **Import** pane to paste JSON or choose **Upload JSON**. Expected outcome: Claude Desktop-style servers are added to the draft, duplicate names are rejected, and plaintext env/header values are converted into newly created Fusion secrets plus secret references.
9. Use **Copy Fusion MCP JSON** and then **Download JSON** when needed. Expected outcome: the export contains Fusion MCP JSON with secret references, and no plaintext secret values.
10. Save the Settings modal. Expected outcome: the selected global or project `mcpServers` settings are persisted and used by subsequent MCP-capable AI sessions.
## Managing servers from the CLI
1. List configured servers:
```bash
fn mcp list [--project <name>] [--json]
```
Expected outcome: Fusion prints global, project, and effective servers with secret summaries such as `project secret`, never decrypted values.
2. Add a stdio server:
```bash
fn mcp add local-tools --scope project --transport stdio --command node --arg server.js --env API_KEY=my-existing-secret --secret-scope project
```
Expected outcome: Fusion resolves `my-existing-secret` by id or key, stores it as `{ secretRef, scope }`, and prints `✓ Added MCP server "local-tools" to project scope`.
3. Add an SSE or streamable HTTP server:
```bash
fn mcp add docs --scope global --transport sse --url https://example.test/sse --header Authorization=docs-token --secret-scope global
fn mcp add http-docs --scope project --transport http --url https://example.test/mcp --secret-ref docs-token --secret-scope project
```
Expected outcome: `sse` stores an SSE server, `http` is normalized to `streamable-http`, and `--secret-ref` supplies a token-like default secret field when no explicit `--env` or `--header` is present.
4. Create secrets while adding or editing:
```bash
fn mcp add private-docs --scope project --transport streamable-http --url https://example.test/mcp --create-secret-header Authorization=Bearer-token-value
```
Expected outcome: the CLI creates a Fusion secret with a suggested MCP key and persists only the new secret reference in settings.
5. Edit a scoped server:
```bash
fn mcp edit local-tools --scope project --command node --args '["server.js","--verbose"]'
```
Expected outcome: only the selected global or project declaration changes; effective project-over-global behavior is recomputed later by name.
6. Enable or disable a server:
```bash
fn mcp enable local-tools --scope project
fn mcp disable local-tools --scope project
```
Expected outcome: Fusion flips the selected declaration's `enabled` flag. At project scope, disabling a same-named inherited server masks the global declaration without deleting it.
7. Remove a scoped declaration:
```bash
fn mcp remove local-tools --scope project
```
Expected outcome: the selected declaration is deleted. If you remove a project override, a same-named global server may become effective again.
8. Validate stored definitions:
```bash
fn mcp validate [--scope global|project|effective] [--json]
```
Expected outcome: Fusion reports whether stored definitions satisfy the MCP settings schema. Use the dashboard **Test** control or `POST /api/mcp/validate` for reachability.
## Importing Claude Desktop configuration
1. Prepare a Claude Desktop-style JSON file or paste payload:
```json
{
"mcpServers": {
"docs": {
"command": "node",
"args": ["server.js"],
"env": { "API_KEY": "plaintext-from-claude-config" }
}
}
}
```
Expected outcome: Fusion recognizes the `mcpServers` object and maps each entry to a named MCP server.
2. Import from the dashboard by opening **Settings → Global → MCP Servers** or **Settings → Project → MCP Servers**, pasting the JSON into **Import**, or choosing **Upload JSON**, then clicking **Import**. Expected outcome: plaintext env/header values are converted into Fusion secrets with `prompt` access policy and settings receive only secret references.
3. Import from the CLI:
```bash
fn mcp import ./claude_desktop_config.json --scope project --yes
```
Expected outcome: the CLI prints an import summary, creates Fusion secrets for plaintext env/header values, replaces them with secret references, and imports the definitions into the selected scope.
4. Review the imported rows with `fn mcp list` or the dashboard card before saving/applying broader settings changes. Expected outcome: duplicate names are visible, secret fields show as references, and effective project-over-global behavior is clear.
## Exporting Fusion MCP configuration
1. Export from the dashboard by opening the MCP settings card and clicking **Copy Fusion MCP JSON**. Expected outcome: the JSON is copied when clipboard access is available, and the export text appears for manual copy.
2. Click **Download JSON** after generating the dashboard export. Expected outcome: the browser downloads `fusion-mcp-servers.json`.
3. Export from the CLI:
```bash
fn mcp export --scope effective --output fusion-mcp-servers.json
fn mcp export --scope global --json
```
Expected outcome: `--output` writes the JSON to a file; without `--output`, Fusion prints JSON to stdout. `global`, `project`, and `effective` choose stored global declarations, stored project declarations, or resolved project-over-global output.
4. Inspect the exported secret fields. Expected outcome: env/header values remain `{ secretRef, scope }` references and are not decrypted into plaintext.
## How MCP servers reach AI lanes
When an AI lane starts a session, Fusion resolves the effective `mcpServers` settings, materializes secret references through the scoped secrets store, and passes the resulting in-memory server declarations to runtimes that support MCP. The forwarding path covers chat/planning, executor, reviewer, validator, merger, workflow model nodes, summarization, evaluator, research, cron/automation, mission, and reflection paths.
Runtime support is guarded. Claude/pi/ACP-compatible runtimes receive MCP servers; mock or unsupported runtimes skip forwarding and emit only structured count/provider/runtime metadata. Skipped forwarding is not a settings error: it means the selected runtime does not accept MCP server declarations.
Expected outcome: enabling a server makes it available to subsequent supported AI sessions, while unsupported sessions continue without MCP tools and without logging secret-bearing server definitions.
See [Settings Reference](./settings-reference.md) for the `mcpServers` settings contract and [Agents](./agents.md) for runtime/model lane behavior.

View File

@@ -41,7 +41,7 @@ Threat-model baseline:
- MCP server settings store only secret references for sensitive env/header/token fields; imports surface plaintext as secret-creation descriptors instead of persisting it in settings.
- MCP server secret references are materialized only at session/probe creation time for MCP-capable AI lanes and `POST /api/mcp/validate`; responses and structured logs include status/count metadata only, never resolved env/header values.
See also: [Storage](./storage.md), [Multi-project](./multi-project.md), [Architecture](./architecture.md), [Settings reference](./settings-reference.md).
See also: [Storage](./storage.md), [Multi-project](./multi-project.md), [Architecture](./architecture.md), [Settings reference](./settings-reference.md), and [MCP](./mcp.md) for MCP-specific secret-reference workflows.
## Architecture

View File

@@ -166,6 +166,8 @@ Secret rule: `env` and `headers` maps are sensitive. Values must be Fusion secre
`POST /api/mcp/validate` validates an MCP server definition or configured server name against the current project context. The route resolves and materializes the target server with the same secret rules, then performs a bounded reachability probe (`stdio` supervised spawn, `sse`/`streamable-http` bounded fetch) and returns only `{ status, message? }` without resolved env/header contents.
See [MCP](./mcp.md) for the full configuration and usage guide, including dashboard, CLI, Claude Desktop import, Fusion export, and reachability procedures.
### Notification providers (pluggable)
Fusion now supports a provider-list notification model via `notificationProviders` while keeping legacy flat ntfy/webhook settings intact.

View File

@@ -0,0 +1,65 @@
// @vitest-environment node
import { describe, expect, it } from "vitest";
import { readFileSync } from "node:fs";
import path from "node:path";
const repoRoot = path.resolve(__dirname, "../../../../");
function readDoc(relativePath: string): string {
return readFileSync(path.join(repoRoot, relativePath), "utf8");
}
describe("MCP documentation contract", () => {
it("includes the canonical MCP guide and required cross-references", () => {
const mcpGuide = readDoc("docs/mcp.md");
const docsIndex = readDoc("docs/README.md");
const settingsReference = readDoc("docs/settings-reference.md");
const cliReference = readDoc("docs/cli-reference.md");
const dashboardGuide = readDoc("docs/dashboard-guide.md");
expect(mcpGuide).toContain("# MCP (Model Context Protocol)");
expect(mcpGuide).toContain("## Overview");
expect(mcpGuide).toContain("## Server definitions and transports");
expect(mcpGuide).toContain("## Secret references");
expect(mcpGuide).toContain("## Validation and reachability");
expect(mcpGuide).toContain("## Managing servers in the dashboard");
expect(mcpGuide).toContain("## Managing servers from the CLI");
expect(mcpGuide).toContain("## Importing Claude Desktop configuration");
expect(mcpGuide).toContain("## Exporting Fusion MCP configuration");
expect(mcpGuide).toContain("## How MCP servers reach AI lanes");
expect(docsIndex).toContain("[MCP](./mcp.md)");
expect(settingsReference).toContain("[MCP](./mcp.md)");
expect(cliReference).toContain("[MCP](./mcp.md)");
expect(dashboardGuide).toContain("[MCP](./mcp.md)");
});
it("keeps documented MCP implementation surfaces aligned with source", () => {
const mcpGuide = readDoc("docs/mcp.md");
const routeSource = readDoc("packages/dashboard/src/routes.ts");
const cliSource = readDoc("packages/cli/src/commands/mcp.ts");
const settingsModalSource = readDoc("packages/dashboard/app/components/SettingsModal.tsx");
expect(routeSource).toContain('router.post("/mcp/validate"');
expect(routeSource).toContain("server?: unknown");
expect(routeSource).toContain("definition?: unknown");
expect(routeSource).toContain("timeoutMs?: unknown");
expect(mcpGuide).toContain("POST /api/mcp/validate");
expect(mcpGuide).toContain("`valid`");
expect(mcpGuide).toContain("`unreachable`");
expect(mcpGuide).toContain("`error`");
for (const command of ["runMcpList", "runMcpAdd", "runMcpEdit", "runMcpRemove", "runMcpEnable", "runMcpDisable", "runMcpImport", "runMcpExport", "runMcpValidate"]) {
expect(cliSource).toContain(`export async function ${command}`);
}
for (const flag of ["--scope", "--transport", "--secret-ref", "--secret-scope", "--output", "--json", "--yes"]) {
expect(mcpGuide).toContain(flag);
}
expect(settingsModalSource).toContain('id: "global-mcp"');
expect(settingsModalSource).toContain('id: "mcp"');
expect(mcpGuide).toContain("Settings → Global → MCP Servers");
expect(mcpGuide).toContain("Settings → Project → MCP Servers");
});
});