slaude Docs

Field notes

2026-05-11 — external MCP via mcp.json

  • Client-supplied MCP servers shipped. New $SLAUDE_HOME/mcp.json (override path via SLAUDE_MCP_CONFIG env) declares external stdio/sse/http MCP servers in the same shape Claude Code's mcp.json uses. Loader is src/config/mcp.ts:loadExternalMcp() — read once at boot in src/gateway/slack/adapter.ts and spread into the resolver alongside the in-process slaude_slack + slaude_skills servers. Restart slaude after editing the file; there is no hot-reload (deliberate — server lifetimes are tied to SDK Query lifetimes).
  • Shape: { "mcpServers": { "<name>": { command, args?, env? } | { type: "http" | "sse", url, headers? } } }. Stdio is implicit when command is present (matches Claude Code's mcp.json — no type: "stdio" required). HTTP/SSE require explicit type.
  • ${VAR} substitution applied to command, every entry of args[], every value of env, url, every value of headers. Missing vars expand to empty string (POSIX shell semantics) so a malformed env doesn't crash the boot — typo-detection is the operator's job, not the loader's.
  • Reserved-name defence: slaude_slack and slaude_skills are dropped at load time with a warning so user config cannot shadow the in-process Slack output server (would deadlock the agent — only path to user-visible output).
  • Permission posture: mcp__<external>__* tools are NOT auto-allowed by PermissionGate (only mcp__slaude_slack__* plus read-only mcp__slaude_skills__{list,read}_skill). External tool calls fall through to the standard Block Kit approve/deny prompt. "Always allow" still grants session-scoped auto-approval on first click.
  • Env propagation: stdio child processes inherit process.env (Bun default). Loader does not whitelist — operators who care about secret isolation should set explicit env: {…} on the server config.
  • Test file: tests/config-mcp.test.ts covers missing file → {}, malformed JSON, missing mcpServers root, stdio (implicit type + env+arg expansion), http (header expansion), sse, missing ${VAR} → empty, stdio without command, http without url, non-object entry, non-array args, reserved-name drop (both names), SLAUDE_MCP_CONFIG override. 14 tests; full suite 189 pass. New module at 100% func / 100% line.