Connect over MCP
D2B’s MCP server lives at https://d2b.dev/mcp/ (Streamable HTTP). Auth comes in two forms: the host’s sign-in (OAuth 2.1 — approve in the browser, and the host renews a one-hour token on its own) or a PAT Bearer header. The tools are the same surface as REST / CLI / SDK, each with a typed schema for tool calling (your host’s tool list is always the current one).
The norms come from the server
Section titled “The norms come from the server”On connect, the initialize response carries instructions: the operating norms (which tool when, bring files in by reference, derive with transforms, re-read on 409, commit_snapshot at milestones, delete_workbook to clean up …). If your host truncates instructions, the same text is readable as the d2b://guide resource. Your repo only needs the connection and the choice of path (Use D2B from a coding agent).
Workspace provisioning works over MCP too (provision_workspace / delete_workspace; needs an account-wide key with the workspaces:create / workspaces:delete scopes — a key pinned to one workspace cannot create siblings).
Sign-in (OAuth)
Section titled “Sign-in (OAuth)”Register the URL and nothing else: the host discovers the authorization server from the 401 on https://d2b.dev/mcp/ and opens D2B’s consent page in the browser. The page shows the app’s name, the host it returns to, the permissions it gets (read / write / delete on workbooks plus cloud-files:read) and which account it acts for (your default account or one of your developer accounts). The access token lasts one hour; the host renews it with a refresh token. It appears under Settings > Tokens in the console as oauth:<app name>, so each connection can be revoked on its own.
The app name is self-declared by the host; D2B does not verify it. Deny a consent page you did not expect. For hosts without OAuth support, and for unattended environments such as CI, keep using a PAT Bearer header.
Per-host setup
Section titled “Per-host setup”claude mcp add d2b --transport http https://d2b.dev/mcp/Registered without a header, the host asks you to sign in on first use. Unattended environments pass a PAT instead:
claude mcp add d2b --transport http https://d2b.dev/mcp/ \ --header "Authorization: Bearer $D2B_PAT"~/.cursor/mcp.json (or the project’s .cursor/mcp.json). The URL alone makes Cursor prompt for sign-in; add headers to pin a PAT instead:
{ "mcpServers": { "d2b": { "url": "https://d2b.dev/mcp/", "headers": { "Authorization": "Bearer d2b_pat_..." } } }}.vscode/mcp.json. The URL alone makes VS Code prompt for sign-in (add headers for a PAT):
{ "servers": { "d2b": { "type": "http", "url": "https://d2b.dev/mcp/", "headers": { "Authorization": "Bearer ${input:d2b_pat}" } } }, "inputs": [{ "id": "d2b_pat", "type": "promptString", "password": true, "description": "D2B PAT" }]}Settings → Connectors → Add custom connector with https://d2b.dev/mcp/. Keep the default “Sign in now” (the browser opens D2B’s consent page). To connect with a PAT instead, choose “No sign-in” and set the request header Authorization: Bearer d2b_pat_.... On versions that offer neither, go through mcp-remote:
{ "mcpServers": { "d2b": { "command": "npx", "args": ["-y", "mcp-remote", "https://d2b.dev/mcp/", "--header", "Authorization: Bearer d2b_pat_..."] } }}If you use a PAT, make it least-privilege — workbook-scoped, with a rate limit (Quickstart §1).
Tool map
Section titled “Tool map”| Purpose | Tools |
|---|---|
| Discover | list_my_data / get_schema(include_json_schema) / profile_table / get_lineage / get_downstream / search |
| Read | read_table / query_sql / validate_sql |
| Review | review_table / review_workbook (evidence-backed findings: the source file’s formulas, the table’s invariants, labels vs. definitions; mode="agent" returns a job — follow it with get_job) |
| Bring data in (reference first) | ingest_url / request_upload → ingest_upload / list_cloud_files → import_cloud_file / track_onedrive_file / ingest_file |
| Inspect structure / faithful revise | analyze_source / get_parse_spec / update_parse_spec / materialize_source / revise_source |
| Containers (workbooks, workspaces) | list_my_workspaces / create_workbook / delete_workbook / provision_workspace / delete_workspace |
| Create & fix | create_table / add_transform / list_transforms / upsert_rows / delete_rows / write_a1 / put_sheet / get_sheet / list_sheets |
| Schema | add_column / rename_column / retype_column / drop_column / rename_table |
| Formulas & descriptions | set_formula_column / list_formula_columns / clear_formula_column / set_artifact_description / set_column_description / set_table_style / get_table_style |
| History | list_snapshots / commit_snapshot / restore_snapshot / diff_snapshots / recompute_stale / list_ops / undo_op |
| Conflicts, sync, delivery | list_conflicts / resolve_conflict / export_tables / bind_external_sheet / list_sync_bindings / unbind_external_sheet / list_file_links / sync_file_link |
Typing
Section titled “Typing”Tool inputs are strictly typed (enums / structured models, so a harness can reject bad values at generation time). The one data-dependent input, upsert_rows.rows, can be constrained at runtime with the per-row JSON Schema from get_schema(name, include_json_schema=true).
Errors use the same problem+json vocabulary as REST (with suggested_fix) — Reading errors.
The transport is stateless streamable HTTP — no conversation session is kept server-side; each request is self-contained (safe for retries and horizontal scaling).