Skip to content

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

Terminal window
folio-mcp serve --root ./sheets --actor agent:hosted

By default this serves over stdio — the MCP transport for desktop clients. To serve over HTTP (e.g. for a remote agent runtime):

Terminal window
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):

ToolSDK
get_contractsheet.get_contract
querysheet.query
list_recordssheet.list_records (supports format=toon)
get_recordsheet.get_record
upsert_recordssheet.upsert_records
delete_recordssheet.delete_records
materializesheet.materialize
materialization_statussheet.materialization_status
provenancesheet.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_server
from 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.

Aspectfolio CLIfolio-mcp
Transportshell stdin/stdout, one verb at a timeMCP (stdio or HTTP)
Callerhumans, scripts, agents that can shell outMCP-compatible runtimes
Actor--actor per call--actor server-default + per-call override
OutputJSON on stdoutStructured MCP results
Statefulno — one subprocess per callsession-scoped — one process per client
Discoverabilitythe agent has to know verb / flag namestools + 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-skills pack with npx skills add nyuta01/folio and the agent picks up SKILL.md files 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 http lets 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.