MCP servers
MCP (Model Context Protocol) is Anthropic's open standard for exposing tools to LLM agents over JSON-RPC. Fred ships a stdio MCP client; configure servers in .dsc/settings.json and Fred auto-registers their tools at startup.
The point: bolt extra capabilities onto Fred without modifying its source. Read files in a directory you whitelisted, query a Postgres replica, hit the GitHub API, drive a headless browser. The agent sees a normal tool; the work happens in a subprocess Fred speaks to over stdin/stdout.
Why MCP
- Reuse the ecosystem. Filesystem, GitHub, Postgres, Slack, browser automation — there are dozens of servers already, and the registry is growing.
- Custom tools without forking. Write a 50-line Node script that exposes one tool, drop it in your settings, restart. No need to clone Fred.
- Tool isolation. Each MCP server is its own subprocess. A crash stays contained — the tool goes unavailable, the rest of your session keeps running.
Configuration
MCP servers live in .dsc/settings.json next to hooks and permissions. The mcp array:
{
"mcp": [
{
"name": "filesystem",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/me/projects"]
},
{
"name": "github",
"command": "node",
"args": ["/usr/local/lib/github-mcp/dist/index.js"],
"env": { "GITHUB_TOKEN": "$GITHUB_TOKEN" }
},
{
"name": "pg",
"command": "uvx",
"args": ["mcp-server-postgres", "postgres://readonly@localhost:5432/app"],
"init_timeout": 20
}
]
}Fields:
| Field | Required | Description |
|---|---|---|
name | yes | Server identifier. Used in tool names and the /mcp listing. Must be unique. |
command | yes | Executable to spawn. Looked up on PATH. |
args | no | Argv passed to the command. Strings, ints, and floats are accepted. |
env | no | Extra env vars. Values support $VAR substitution from the parent env at spawn time. Missing vars become empty strings. |
cwd | no | Working directory for the subprocess. Default: Fred's cwd. |
init_timeout | no | Seconds to wait for the initialize + tools/list handshake. Default 10, capped at 120. |
type | no | Transport. Only stdio is supported in v1; missing defaults to stdio. |
Transport
Stdio only, currently. The server speaks JSON-RPC 2.0 over stdin/stdout, one frame per line. Fred's client handshakes on startup (advertising MCP protocol version 2025-03-26), sends notifications/initialized, then fetches tools/list. Stderr is captured for diagnostics — never shown to the model, never written to your terminal directly.
Tool naming
Remote tools register as mcp__<server>__<tool> to avoid clashes with Fred's native tools. So a read_file tool from the filesystem server appears as mcp__filesystem__read_file; the GitHub server's create_issue becomes mcp__github__create_issue.
That naming is also what permission rules and hook matchers use — so an allowlist rule like Tool(mcp__filesystem__*) trusts every tool from one server without trusting another.
Listing connected servers
At any prompt, type /mcp to see every server, its tool count, and its status:
> /mcp
filesystem connected 9 tools init 412ms
github connected 17 tools init 901ms
pg crashed 0 tools init failed: timed out waiting for initialize
Three statuses: connected (handshake succeeded, tools registered), disconnected (configured but not started yet), crashed (handshake failed or the subprocess exited mid-session). Crashed servers stay in the listing with their failure reason so you don't have to re-run anything to debug.
Permissions
MCP tools go through the same permission gate as native tools. The first time the model calls mcp__github__create_issue, you'll see the y/n/a/d prompt like any other tool. Use /perms add or pre-seed .dsc/settings.local.json to allowlist a server wholesale:
// .dsc/settings.local.json
{
"permissions": {
"allow": [
"mcp__filesystem__*",
"mcp__github__list_issues"
]
}
}Failure modes
- Spawn failed. Wrong command, missing binary, broken args. Logged to
/mcpas crashed, status surfaced at startup. Other servers keep running. - Handshake timed out. Server started but didn't respond to
initializewithininit_timeout. Bump the timeout or fix the server. - Crashed mid-session. Subprocess exited or stdout closed. Tools from that server are marked unavailable; the next model call returns a structured
Error: MCP server X crashed; ...message. Other servers and native tools keep working. - Tool returned an error. Server responded with a JSON-RPC error envelope (e.g. invalid args, upstream API failure). Surfaced to the model as an
Error: ...string so it can react.
Popular servers to try
@modelcontextprotocol/server-filesystem— sandboxed file ops in a directory you whitelisted. Useful when you want the model to read/write files outside the cwd.@modelcontextprotocol/server-github— issues, PRs, releases. Authenticate with a personal access token via theenvfield.@modelcontextprotocol/server-postgres— read-only SQL against a connection string. Pair with a read-only role; the server doesn't enforce read-only on its own.@modelcontextprotocol/server-puppeteer— headless browser automation for scraping, screenshots, and DOM-aware testing.
Full registry: github.com/modelcontextprotocol/servers.
Schema loading
By default Fred ships a stub description for each MCP tool in the system prompt — name plus a one-liner — and fetches the full JSON schema lazily on first invocation. This keeps the prefix small (saves ~5K tokens per server). To force eager loading set FRED_MCP_SCHEMAS=eager in your environment; the full schemas land in the catalog at startup, at the cost of a fatter prompt every turn.
Project + user merging
Same rules as hooks. Both ~/.dsc/settings.json and .dsc/settings.json load; the closest project layer wins. A layer silent on mcp leaves the lower layer intact, so you can keep a global filesystem server in your home settings and add a project-specific pg entry without losing the filesystem one — just include both in the project file's mcp array.
Related
Configuration — the full settings file. Permissions — how MCP tools are gated. Hooks — the other extension point.
MCP server processes are spawned at session start and live for the whole REPL. They die when you exit. Anything stateful (caches, connections, in-memory indexes) gets a fresh start each session — the server is ephemeral, not a daemon.
MCP servers run with your shell's privileges and inherit your full environment, including FRED_API_KEY and any tokens. Trust the source before adding one — apply the same scrutiny you would to a VS Code extension or a shell function in your .zshrc.