bino mcp
bino mcp runs a Model Context Protocol server over
stdio, exposing bino's introspection, validation, authoring, and build surface to AI agent
clients such as Claude Code, Claude Desktop, and Cursor.
Because a bino report is plain-text YAML + SQL, an agent can author it directly — and bino's schema and validation become the guardrails that tell the agent precisely whether what it wrote is correct.
bino mcp [--work-dir DIR]Most agent clients are configured with a command, so bino mcp is the entrypoint you
register once. Under the hood:
- Proxy mode — if a
bino daemonis already running for the project (for example because VS Code is open),bino mcpconnects to that daemon's/mcpendpoint and forwards to it, so the agent reuses the daemon's already-loaded DuckDB session and file watcher instead of starting a second one. - Standalone mode — with no daemon running,
bino mcploads the project itself and serves the MCP directly.
Flags:
--work-dir,-w– report bundle directory (default:.).--no-proxy– always run standalone, even if a daemon is running.--allow-publish– also expose theregistry_publishtool (see Security). Whenbino mcpproxies to a running daemon, the daemon's--mcp-allow-publishflag decides instead.
Clients that speak MCP over HTTP can instead point straight at a running daemon's endpoint
(http://127.0.0.1:<port>/mcp; the port is recorded in .bino/daemon.json). The daemon mounts
/mcp by default on loopback — pass bino daemon --mcp=false to disable it.
Configure your client
Section titled “Configure your client”Claude Code
Section titled “Claude Code”claude mcp add bino -- bino mcp --work-dir /path/to/reportClaude Desktop / Cursor
Section titled “Claude Desktop / Cursor”Add an entry to the client's MCP config (Claude Desktop:
claude_desktop_config.json; Cursor: .cursor/mcp.json):
{
"mcpServers": {
"bino": {
"command": "bino",
"args": ["mcp", "--work-dir", "/path/to/report"]
}
}
}Resources
Section titled “Resources”Read-only context an agent can pull in:
| URI | Contents |
|---|---|
bino://schema | The full merged JSON Schema for all manifest kinds (built-in + plugin). |
bino://schema/{kind} | The self-contained spec schema for one kind (e.g. bino://schema/Table). |
bino://kinds | Every manifest kind and its capability category (data / layout / embeddable / artefact / config). |
bino://documents | The project index: every document → {kind, name, file, position}. |
bino://packages | The project's registry packages: bino.lock merged with bino.toml [dependencies], with installed state. |
Understand & inspect
Section titled “Understand & inspect”| Tool | What it does |
|---|---|
list_kinds | Every manifest kind with its capability category. |
outline_kind(kind) | A compact outline of one kind's spec: every field with its path, type, required flag, enum, default and description. Layout kinds report the allowed child kinds as childKinds. Start here. |
scaffold_kind(kind) | A minimal YAML document for one kind with every required spec field pre-filled. |
describe_kind(kind) | The full spec JSON Schema for one kind. Large (up to ~20k tokens for layout kinds); prefer outline_kind. |
describe_project() | All documents in the project. |
describe_document(file) | The documents declared in one file. |
get_columns(name) | Column names of a DataSet or DataSource (prefix $ to force a DataSource). |
get_rows(name, limit?) | A sample of rows from a DataSet or DataSource. |
graph_deps(kind, name, direction?, max_depth?) | Dependencies (out), dependents (in), or both. |
Validate
Section titled “Validate”| Tool | What it does |
|---|---|
validate_project(execute_queries?) | Validate the project on disk; optionally execute datasets for data-validation warnings. |
validate_draft(yaml) | Validate manifest YAML in memory (no disk write) — the pre-write guardrail. |
introspect_source(spec, sheet?, limit?) | Probe a not-yet-registered CSV/Excel/DB source: columns, sample rows, sheets, detected CSV options. |
Author
Section titled “Author”| Tool | What it does |
|---|---|
create_manifest(kind, name, spec, ...) | Create any manifest from a spec object; validated and written atomically, auto-placed by project convention. |
write_manifest(file, yaml, append?) | Persist a full manifest document, validated before writing. |
edit_manifest(file, position, patch) | Edit one document in place via dotted-path edits, preserving comments and key order. |
scaffold_source(dataSource, dataSet?) | Scaffold a DataSource and an optional typed DataSet in one step. |
init_bundle(...) | Bootstrap a new starter report bundle. |
Every write validates against the merged JSON Schema before persisting, so an agent cannot write a structurally invalid manifest. Writes are atomic (temp file + rename).
| Tool | What it does |
|---|---|
build(artefacts?, out_dir?) | Render artefacts by running bino build — streams progress and returns the exit code, output, and produced artefacts. Subject to the usual engine-compatibility check. |
Registry
Section titled “Registry”| Tool | What it does |
|---|---|
registry_packages() | The project's dependencies, offline: bino.lock merged with bino.toml [dependencies], whether each is installed under .bino/registry/, and the params each package declares. |
registry_search(query, kinds?, scopes?, tags?, page?, per_page?) | Search the registry for published packages (network). Use it before authoring a component from scratch. |
registry_info(spec) | Resolve one package spec (@scope/name, optionally @version or @tag): version, kinds, files, dependencies, and the installed version when the project already locks it. |
registry_auth_status() | The resolved registry URL, whether a credential is available, and where credentials are stored. Never returns the token. |
registry_add(specs) | Add packages as dependencies: resolves the transitive closure, verifies every download, then writes bino.toml, bino.lock and .bino/registry/. Reports name collisions with local documents. |
registry_update(packages?) | Re-resolve tag-following dependencies to their newest versions and rewrite bino.lock; exact pins are held. |
registry_remove(packages) | Remove dependencies from bino.toml and delete every locked package no longer reachable (offline). |
registry_install() | Re-materialize .bino/registry/ exactly as pinned in bino.lock, without re-resolving. |
registry_publish(bump?, dry_run?, visibility?) | Run bino publish --json for the project's [package]. Only registered when the server was started with the opt-in flag — see Security. |
The authoring loop
Section titled “The authoring loop”1. outline_kind({kind:"Table"}) → the Table spec fields, types, enums, defaults
2. scaffold_kind({kind:"Table"}) → a minimal Table document to fill in
3. get_columns({name:"sales"}) → {columns:["region","ac1","pl1",...]}
4. draft the Table manifest using those columns
5. validate_draft({yaml:"..."}) → {valid:false, diagnostics:[{message:"...", ...}]}
6. fix and re-validate → {valid:true}
7. create_manifest({kind:"Table", name:"q3", spec:{...}})
8. build({artefacts:["q3"]}) → q3.pdfSecurity
Section titled “Security”The server runs SQL, reads the filesystem, and (via build) launches headless Chrome. This is
intended for local use over stdio and a loopback HTTP endpoint. Exposing the daemon on a
non-loopback address (bino daemon --listen-addr) is out of scope for this surface and would
require additional hardening.
registry_publish is off by default. It is only registered when a human starts the server with
bino mcp --allow-publish, or bino daemon --mcp-allow-publish when bino mcp proxies to a running
daemon. Publishing is irreversible — it mints an immutable version that cannot be deleted — and a
first publish with visibility: public makes the package public, so an agent must not be able to
trigger it by accident. The other registry tools only change the local project and never return a
credential; when registry_auth_status reports none, the human runs bino registry login.