MCP server
folio-mcp is a Model Context Protocol server. It mounts the Python SDK
under nine MCP tools so any MCP-compatible runtime — Claude Desktop, IDE
plugins, custom agent loops — can read and write Folio sheets through a
standard transport.
Quickstart
folio-mcp serve --root ./sheets --actor agent:hostedBy default this serves over stdio — the MCP transport for desktop clients. To serve over HTTP (e.g. for a remote agent runtime):
folio-mcp serve --root ./sheets --actor agent:hosted \ --transport http --bind 127.0.0.1 --port 3001--root is a directory that contains one or more sheet sub-directories.
Tool calls take a sheet_path argument that is resolved against the root;
paths that escape the root are rejected.
What gets exposed
Nine SDK tools — all wrappers over the Python SDK (Tools reference):
| Tool | SDK |
|---|---|
get_contract | sheet.get_contract |
query | sheet.query |
list_records | sheet.list_records (supports format=toon) |
get_record | sheet.get_record |
upsert_records | sheet.upsert_records |
delete_records | sheet.delete_records |
materialize | sheet.materialize |
materialization_status | sheet.materialization_status |
provenance | sheet.provenance |
Each tool has a “when-to-use” docstring so the model picks the right one without further prompting.
Plus one prompt per skill — for every <sheet>/skills/<name>.md
discovered under the MCP root, Folio publishes an MCP prompt named
<sheet-id>:<skill-name>. Standard prompts/list returns every
skill across every sheet; prompts/get renders the markdown body
with the skill’s declared arguments substituted into {name}
placeholders. See the
Sheet skills specification for the file
format.
Path safety
Every tool’s sheet_path is resolved with Path.resolve() against
--root. Any resolved path that doesn’t live under the root is rejected
with FolioError. There’s no symlink trap because Python’s
Path.resolve() follows symlinks before the prefix check.
Actor
--actor sets the default actor for write tools when the caller doesn’t
pass one explicitly. For read tools (get_contract, query,
list_records, …), no actor is needed.
AI client injection
The materialize tool routes through Sheet.materialize, which honors
the ai_client argument from the SDK. To inject a stub (or a custom
adapter) at server-start time:
from folio_mcp import build_serverfrom folio._ai_kind import StubAIClient
stub = StubAIClient()stub.prepare("Industry of Acme", "Manufacturing")
server = build_server( root="./sheets", default_actor="agent:hosted", ai_client=stub,)server.run(transport="stdio")That’s the same code the offline MCP smoke test uses — so the in-process test path matches the production path.
When to use folio-mcp vs the CLI
Folio’s CLI and MCP server are two channels into the same SDK — both
go through Sheet.* and write the same provenance.jsonl, so picking
one over the other is a question of integration cost, not of
capability.
| Aspect | folio CLI | folio-mcp |
|---|---|---|
| Transport | shell stdin/stdout, one verb at a time | MCP (stdio or HTTP) |
| Caller | humans, scripts, agents that can shell out | MCP-compatible runtimes |
| Actor | --actor per call | --actor server-default + per-call override |
| Output | JSON on stdout | Structured MCP results |
| Stateful | no — one subprocess per call | session-scoped — one process per client |
| Discoverability | the agent has to know verb / flag names | tools + per-skill prompts auto-surface via tools/list and prompts/list |
Reach for folio CLI when…
- The agent runs on the same machine as the sheet and can shell out
(Claude Code, Cursor, Cline, Codex, Windsurf, Aider, Copilot, Gemini —
basically every coding agent today). Install the
folio-agent-skillspack withnpx skills add nyuta01/folioand the agent picks upSKILL.mdfiles that tell it which CLI verbs to invoke. This is the de-facto path for local agent work — no daemon to keep running, every action is auditable from shell history, and the loop matches the documented examples. - Shell scripts, CI jobs, and ad-hoc human use.
Reach for folio-mcp when…
- The agent is remote or runs in a sandbox without shell access to
the sheet —
--transport httplets it talk to Folio over the network. - You’re embedding Folio inside an MCP-native runtime that has no shell tool but speaks MCP natively (Claude Desktop’s built-in MCP surface, MCP-only IDE plugins, custom orchestrators built on the MCP SDK).
- You’re calling Folio from another service (a backend that exposes Folio reads to its own users, an evals harness, etc.) and want typed tool results instead of stdout parsing.
- The same long-lived session reads/writes the sheet thousands of times — process-startup cost per CLI call adds up.
For most coding-agent workflows on a developer laptop, the CLI + skills.sh path is faster to set up and easier to debug. MCP is the right tool when shell access isn’t available or the call volume makes subprocess startup the bottleneck.
See also
- Tools — the nine SDK tools, with arguments and return shapes.
- Sheet skills — how skills become MCP prompts.
- Deploy — Claude Desktop config, HTTP transport, security notes.