mcp-coda
# mcp-coda
[](https://pypi.org/project/mcp-coda/)
[](https://pypi.org/project/mcp-coda/)
[](https://pypi.org/project/mcp-coda/)
[](https://opensource.org/licenses/MIT)
[](https://github.com/vish288/mcp-coda/actions/workflows/tests.yml)
[](https://registry.modelcontextprotocol.io)
<!-- mcp-name: io.github.vish288/mcp-coda -->
**Install:** `uvx mcp-coda` | [PyPI](https://pypi.org/project/mcp-coda/) | [MCP Registry](https://registry.modelcontextprotocol.io) | [Changelog](https://github.com/vish288/mcp-coda/releases)
```bash
uvx mcp-coda # with writes
MCP_READ_ONLY=true uvx mcp-coda # locked read-only: write tools are not listed
```
**mcp-coda** is a [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server for [Coda](https://coda.io). It lets an AI assistant read and write Coda docs, pages, tables, and rows through the [Coda API](https://coda.io/developers/apis/v1). It also runs formulas, manages permissions and folders, publishes docs, and triggers automations. The server exposes **54 tools**, **12 resources**, and **5 prompts**. It supports the MCP 2026-07-28 specification (often called MCP 2.0) and stays compatible with 2025-11-25 clients. It works with Claude Desktop, Claude Code, Cursor, Windsurf, VS Code Copilot, and any MCP-compatible client. Runs in one of two modes: with writes, or locked read-only (`MCP_READ_ONLY=true`) so the 20 write tools are never offered to the model.
mcp-coda uses [FastMCP](https://github.com/jlowin/fastmcp), [httpx](https://www.python-httpx.org/), and [Pydantic](https://docs.pydantic.dev/).
## 1-Click Installation
[](https://vish288.github.io/mcp-install?server=mcp-coda&install=cursor)
[](https://vish288.github.io/mcp-install?server=mcp-coda&install=vscode) [](https://vish288.github.io/mcp-install?server=mcp-coda&install=vscode-insiders)
> **Tip:** For other AI assistants (Claude Code, Windsurf, IntelliJ, Gemini CLI), visit the **[Coda MCP Installation Gateway](https://vish288.github.io/mcp-install?server=mcp-coda)**.
<details>
<summary><b>Manual Setup Guides (Click to expand)</b></summary>
<br/>
> Prerequisite: Install `uv` first (required for all `uvx` install flows). [Install uv](https://docs.astral.sh/uv/getting-started/installation/).
### Claude Code
```bash
# with writes
claude mcp add coda -e CODA_API_TOKEN=your-token-here -- uvx mcp-coda
# locked read-only
claude mcp add coda -e MCP_READ_ONLY=true -e CODA_API_TOKEN=your-token-here -- uvx mcp-coda
```
### Windsurf & IntelliJ
**Windsurf:** Add to `~/.codeium/windsurf/mcp_config.json`
**IntelliJ:** Add to `Settings | Tools | MCP Servers`
> **Note:** The actual server config starts at `coda` inside the `mcpServers` object.
> Remove `MCP_READ_ONLY` to allow writes.
```json
{
"mcpServers": {
"coda": {
"command": "uvx",
"args": ["mcp-coda"],
"env": {
"CODA_API_TOKEN": "your-token-here",
"MCP_READ_ONLY": "true"
}
}
}
}
```
### Gemini CLI
```bash
# with writes
gemini mcp add -s user -e CODA_API_TOKEN=your-token-here coda uvx mcp-coda
# locked read-only
gemini mcp add -s user -e MCP_READ_ONLY=true -e CODA_API_TOKEN=your-token-here coda uvx mcp-coda
```
### VS Code
Add to `.vscode/mcp.json`:
```json
{
"servers": {
"coda": {
"type": "stdio",
"command": "uvx",
"args": ["mcp-coda"],
"env": { "CODA_API_TOKEN": "your-token-here", "MCP_READ_ONLY": "true" }
}
}
}
```
### Codex
```bash
# with writes
codex mcp add coda --env CODA_API_TOKEN=your-token-here -- uvx mcp-coda
# locked read-only
codex mcp add coda --env MCP_READ_ONLY=true --env CODA_API_TOKEN=your-token-here -- uvx mcp-coda
```
### pip / uv
```bash
uv pip install mcp-coda
```
Drop `MCP_READ_ONLY` to allow writes. On a shared or hosted server put it in the deployer's env, not the client config.
</details>
## Configuration
| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `CODA_API_TOKEN` | **Yes** | - | Coda API token ([get one here](https://coda.io/account#apiSettings)) |
| `MCP_READ_ONLY` | No | `false` | Shared name across the mcp-* servers. Any value other than `false`/`0`/`no`/`off`/empty locks the server read-only: the 20 write tools and write-workflow prompts are not listed, and nothing can turn it back off at runtime |
| `CODA_READ_ONLY` | No | `false` | Per-server alias for `MCP_READ_ONLY`; either one locks the server read-only. `--read-only` can only raise the lock |
| `CODA_BASE_URL` | No | `https://coda.io/apis/v1` | API base URL |
| `CODA_TIMEOUT` | No | `30` | Request timeout in seconds |
| `CODA_HTTP_BEARER_TOKEN` | No | - | Required to bind an HTTP transport to a non-loopback host. Clients send `Authorization: Bearer <token>`. Minimum 16 characters |
### Supported Token Types
The server checks these environment variables in order — first match wins:
1. `CODA_API_TOKEN`
2. `CODA_TOKEN`
3. `CODA_PAT`
Generate tokens at [coda.io/account#apiSettings](https://coda.io/account#apiSettings). Tokens grant access to all docs accessible by the token owner. Tokens have no scope restrictions. Coda's sharing settings control doc-level access.
## Compatibility
| Client | Supported | Install Method |
|--------|-----------|----------------|
| Claude Desktop | Yes | `claude_desktop_config.json` |
| Claude Code | Yes | `claude mcp add` |
| Cursor | Yes | One-click deeplink or `.cursor/mcp.json` |
| VS Code Copilot | Yes | One-click deeplink or `.vscode/mcp.json` |
| Windsurf | Yes | `~/.codeium/windsurf/mcp_config.json` |
| Any MCP client | Yes | stdio or HTTP transport |
## Protocol support
mcp-coda implements the **Model Context Protocol 2026-07-28** specification, often called MCP 2.0. It also stays compatible with **2025-11-25** clients. A FastMCP client pinned to 2026-07-28 and a legacy client both list all tools and reach the Coda API over stdio. The companion code PR adds a regression test that checks `tools/list` with an in-memory client pinned to 2026-07-28.
- **Spec version**: MCP 2026-07-28 (MCP 2.0), backward compatible with 2025-11-25.
- **Built on**: FastMCP 4.x and the MCP Python SDK 2.x.
- **Transports**: `stdio` (default) and `streamable-http` (recommended for remote). `sse` (HTTP+SSE) still works, but the 2026-07-28 specification deprecates it, so the server prints a warning.
- **Capabilities**: tools, resources, and prompts. The server uses no Roots, Sampling, Logging, elicitation, or resource subscriptions, so the 2026-07-28 deprecations do not affect it.
### Error results
An expected failure — a 404, a 401, a rate limit, or a write that reaches the read-only guard — comes back as a tool result with `isError: true` rather than a value the caller must inspect for an `error` key. (In locked read-only mode the write tools are hidden, so calling one returns an `Unknown tool` error; the error envelope below is the guard's own response, returned only when a client reaches a write tool anyway.) The result body is the text content: a JSON object with `error`, an actionable `hint`, and, where relevant, `status_code`, `body`, and `retry_after`. The result carries no `structuredContent` — the TypeScript MCP SDK validates `structuredContent` against the tool's output schema even when `isError` is true, so an error payload there would make strict clients reject the whole result. Unexpected exceptions (a bug in a tool) are also `isError: true`, their text is `TypeName: message`, and the server logs them with a traceback.
A successful result carries the Coda JSON object as `structuredContent` beside the pretty-printed text, and tools advertise no `outputSchema`: payloads pass the upstream fields through untyped, so strict clients validate nothing and receive the object directly rather than a double-encoded `{"result": "<json>"}` wrapper. A typed schema is opt-in per tool via `@mcp.tool(output_schema=…)`.
## Tools (54)
| Category | Count | Tools |
|----------|-------|-------|
| **Account** | 3 | whoami, resolve browser link, mutation status |
| **Docs** | 5 | list, get, create, update, delete |
| **Pages** | 9 | list, get, create, update, delete, get content, delete content, export, export status |
| **Tables** | 4 | list tables, get table, list columns, get column |
| **Rows** | 7 | list, get, insert/upsert, update, delete, bulk delete, push button |
| **Permissions** | 6 | sharing metadata, list, add, delete, search principals, ACL settings |
| **Folders** | 5 | list, get, create, update, delete |
| **Misc** | 8 | formulas (list, get), controls (list, get), publishing (list categories, publish, unpublish), trigger automation |
| **Analytics** | 7 | doc analytics, doc summary, page analytics, pack analytics, pack summary, formula analytics, analytics updated |
<details>
<summary>Full tool reference (click to expand)</summary>
### Account
| Tool | Description |
|------|-------------|
| `coda_whoami` | Get current user info |
| `coda_resolve_browser_link` | Convert browser URL to API IDs |
| `coda_get_mutation_status` | Check async write status |
### Docs
| Tool | Description |
|------|-------------|
| `coda_list_docs` | List accessible docs |
| `coda_get_doc` | Get doc metadata |
| `coda_create_doc` | Create a new doc |
| `coda_update_doc` | Update doc title/icon |
| `coda_delete_doc` | Delete a doc |
### Pages
| Tool | Description |
|------|-------------|
| `coda_list_pages` | List pages in a doc |
| `coda_get_page` | Get page metadata |
| `coda_create_page` | Create a page |
| `coda_update_page` | Update page name/content |
| `coda_delete_page` | Delete a page |
| `coda_get_page_content` | Read page content |
| `coda_delete_page_content` | Clear page content |
| `coda_export_page` | Export page as HTML or markdown |
| `coda_get_export_status` | Poll an export started by coda_export_page |
### Tables
| Tool | Description |
|------|-------------|
| `coda_list_tables` | List tables and views |
| `coda_get_table` | Get table metadata |
| `coda_list_columns` | List columns in a table |
| `coda_get_column` | Get column metadata |
### Rows
| Tool | Description |
|------|-------------|
| `coda_list_rows` | List and filter rows |
| `coda_get_row` | Get a single row |
| `coda_insert_rows` | Insert or upsert rows |
| `coda_update_row` | Update a row |
| `coda_delete_row` | Delete a row |
| `coda_delete_rows` | Bulk delete rows by filter |
| `coda_push_button` | Push a button column value |
### Formulas
| Tool | Description |
|------|-------------|
| `coda_list_formulas` | List named formulas |
| `coda_get_formula` | Get formula value |
### Controls
| Tool | Description |
|------|-------------|
| `coda_list_controls` | List controls |
| `coda_get_control` | Get control value |
### Permissions
| Tool | Description |
|------|-------------|
| `coda_get_sharing_metadata` | Get sharing config |
| `coda_list_permissions` | List ACL entries |
| `coda_add_permission` | Grant access |
| `coda_delete_permission` | Revoke access |
| `coda_search_principals` | Search users/groups |
| `coda_get_acl_settings` | Get ACL settings |
### Publishing
| Tool | Description |
|------|-------------|
| `coda_list_categories` | List publishing categories |
| `coda_publish_doc` | Publish a doc |
| `coda_unpublish_doc` | Unpublish a doc |
### Folders
| Tool | Description |
|------|-------------|
| `coda_list_folders` | List folders |
| `coda_get_folder` | Get folder details |
| `coda_create_folder` | Create a folder |
| `coda_update_folder` | Rename a folder |
| `coda_delete_folder` | Delete a folder |
### Automations
| Tool | Description |
|------|-------------|
| `coda_trigger_automation` | Trigger an automation rule |
### Analytics
| Tool | Description |
|------|-------------|
| `coda_list_doc_analytics` | Doc usage metrics |
| `coda_get_doc_analytics_summary` | Aggregated doc metrics |
| `coda_list_page_analytics` | Page usage metrics |
| `coda_list_pack_analytics` | Pack usage metrics |
| `coda_get_pack_analytics_summary` | Aggregated pack metrics |
| `coda_list_pack_formula_analytics` | Formula-level metrics |
| `coda_get_analytics_updated` | Analytics freshness timestamp |
</details>
## Resources (12)
The server exposes [MCP resources](https://modelcontextprotocol.io/docs/concepts/resources) that provide ambient context and do not consume tool calls.
### Data Resources (live API)
| URI | Name | Description |
|-----|------|-------------|
| `coda://docs` | Coda Docs | List of docs accessible to the current API token |
| `coda://docs/{doc_id}/schema` | Coda Doc Schema | Table and column definitions for a doc |
### Rules (static knowledge)
| URI | Name | Description |
|-----|------|-------------|
| `resource://rules/coda-doc-structure` | Coda Doc Structure | Doc/page hierarchy, page types, naming, when to split docs vs folders |
| `resource://rules/coda-table-design` | Coda Table Design | Column types, relations, display columns, row limits, table vs view |
| `resource://rules/coda-permissions` | Coda Permission Model | Doc-level vs page-level locking, ACL, domain sharing, principal types |
| `resource://rules/coda-automations` | Coda Automation Patterns | Webhooks, button triggers, rate limits, payload design, idempotency |
| `resource://rules/coda-api-patterns` | Coda API Best Practices | Rate limits, pagination, async mutations, error handling, retry |
### Guides (how-to)
| URI | Name | Description |
|-----|------|-------------|
| `resource://guides/row-operations` | Row Operations Guide | Insert vs upsert, bulk ops, key columns, cell formats, delete strategies |
| `resource://guides/page-content` | Page Content Guide | HTML vs markdown, insert modes, export workflows |
| `resource://guides/formula-controls` | Formulas & Controls Guide | Named formulas, control types, reading values |
| `resource://guides/publishing-analytics` | Publishing & Analytics Guide | Publishing categories, gallery settings, analytics date filtering |
| `resource://guides/folder-organization` | Folder Organization Guide | Folder CRUD, doc-folder relationships, hierarchy, bulk organization |
## Prompts (5)
The server provides [MCP prompts](https://modelcontextprotocol.io/docs/concepts/prompts) — reusable task templates that clients can invoke.
| Prompt | Parameters | Description |
|--------|-----------|-------------|
| `analyze_doc_structure` | `doc_id` | Analyze a doc's page hierarchy, table layout, and organization |
| `design_table_schema` | `description` | Design a table schema from a natural language description |
| `migrate_spreadsheet` | `doc_id`, `source_format` | Guide for migrating CSV/Excel/Sheets data into Coda |
| `setup_automation` | `doc_id`, `trigger_type` | Set up a webhook/button/time automation with error handling |
| `audit_permissions` | `doc_id` | Audit sharing and permissions, suggest tightening |
## Usage Examples
### Docs & Pages
```
"List all my Coda docs"
→ coda_list_docs(is_owner=True)
"Get the content of page 'Sprint Planning' in doc d1"
→ coda_list_pages(doc_id="d1") → find page ID
→ coda_get_page_content(doc_id="d1", page_id_or_name="canvas-abc")
"Create a new doc from a template"
→ coda_create_doc(title="Q1 Planning", source_doc="template-doc-id")
```
### Tables & Rows
```
"List all tables in doc d1"
→ coda_list_tables(doc_id="d1")
"Find rows where Status is 'Done'"
→ coda_list_rows(doc_id="d1", table_id_or_name="Tasks", query="Done")
"Insert a new row into the Tasks table"
→ coda_insert_rows(doc_id="d1", table_id_or_name="Tasks", rows=[{"cells": [{"column": "Name", "value": "New task"}]}])
"Update a row's status"
→ coda_update_row(doc_id="d1", table_id_or_name="Tasks", row_id_or_name="i-abc", row={"cells": [{"column": "Status", "value": "In Progress"}]})
```
### Formulas & Controls
```
"Get the value of the TotalBudget formula"
→ coda_get_formula(doc_id="d1", formula_id_or_name="TotalBudget")
"Check the current value of the DateFilter control"
→ coda_get_control(doc_id="d1", control_id_or_name="DateFilter")
```
### Permissions & Sharing
```
"Share a doc with a teammate"
→ coda_add_permission(doc_id="d1", access="write", principal_email="alice@example.com")
"List who has access to a doc"
→ coda_list_permissions(doc_id="d1")
```
## Security Considerations
- **Token scope**: Coda API tokens grant access to all docs the token owner can access. Use a dedicated service account for production deployments to limit exposure.
- **Read-only mode**: Set `MCP_READ_ONLY=true` (or `CODA_READ_ONLY=true`) in the launch you control and the server starts locked: the 20 write tools and the write-workflow prompts are not listed, a call to one returns "Unknown tool", and the guard refuses any write that still reaches it. Nothing lifts the lock at runtime — not a `false` from another source, not a `.env` file, not tool arguments; any value other than `false`/`0`/`no`/`off`/empty locks, and `--read-only` can only raise it. The lock is only as strong as the launch: set it where users cannot edit it (a managed MCP config such as Claude Code `managed-mcp.json`, a container, k8s or systemd env, or a hosted HTTP deployment). A user who starts the server themselves controls its environment. A Coda token has no scopes, so this lock is the only read-only control.
- **MCP tool annotations**: Each tool declares `readOnlyHint`, `destructiveHint`, and `idempotentHint` for client-side permission prompts.
- **No credential storage**: The server does not persist tokens. It reads credentials from environment variables at startup.
- **HTTP transport auth**: Host/Origin protection is not authentication. Binding an HTTP transport to a non-loopback host requires `CODA_HTTP_BEARER_TOKEN` (min 16 chars); clients send `Authorization: Bearer <token>` and anything else gets a 401. Without it the server refuses to start on a non-loopback host.
## Upgrading to 0.9.0
Two defaults changed. Both are breaking for callers that relied on the old behaviour:
- **`coda_publish_doc` no longer lists docs in the public gallery by default.** `discoverable` now defaults to `False` — publishing alone makes a doc reachable by URL; pass `discoverable=True` to list it in the Coda gallery. `mode` is now restricted to `view`, `play`, or `edit`.
- **An HTTP transport on a non-loopback host now requires a bearer token.** `uvx mcp-coda --transport streamable-http --host 0.0.0.0` (or any non-loopback host) exits with an error unless `CODA_HTTP_BEARER_TOKEN` (min 16 chars) is set; clients then send `Authorization: Bearer <token>`. Binding to `127.0.0.1` is unchanged.
## Rate Limits & Permissions
### Rate Limits
Coda enforces per-token rate limits (varies by plan). When Coda rate-limits a request, the tool returns a 429 error with `retry_after` seconds. The client retries a 429 once when its `Retry-After` is ≤ 5s (GET only, with jitter, also covering 502/503/504); otherwise it surfaces `retry_after` without retrying — wait that many seconds, then retry. Writes are never retried. Most paginated endpoints default to 50 results per page (`coda_list_docs` 25, `coda_list_columns` 100). Use `limit` to adjust.
### Async Mutations
Coda processes write operations (insert, update, delete rows) asynchronously. Tools return a `requestId`. Check it with `coda_get_mutation_status` to confirm completion.
### Required Permissions
| Operation | Minimum Coda Doc Role |
|-----------|----------------------|
| List docs, read pages, tables, rows, formulas, controls | Viewer |
| Read permissions, analytics | Viewer (doc owner for analytics) |
| Create/update pages, insert/update/delete rows | Editor |
| Create/delete docs, manage folders | Editor (or doc owner) |
| Add/remove permissions, publish/unpublish | Doc Owner |
| Trigger automations | Editor |
### Access Control
| Layer | Mechanism |
|-------|-----------|
| Server-level | `CODA_READ_ONLY=true` / `MCP_READ_ONLY=true` hides every write tool (deployer-owned launch required) |
| MCP annotations | `readOnlyHint`, `destructiveHint`, `idempotentHint` for client-side prompts |
| Coda token | Doc-level access enforced by Coda's sharing settings |
## CLI & Transport Options
```bash
# Default: stdio transport (for MCP clients)
uvx mcp-coda
# HTTP transport (SSE or streamable-http)
uvx mcp-coda --transport sse --host 127.0.0.1 --port 8000
uvx mcp-coda --transport streamable-http --port 9000
# Binding to a non-loopback host requires a bearer token
CODA_HTTP_BEARER_TOKEN=$(openssl rand -hex 32) \
uvx mcp-coda --transport streamable-http --host 0.0.0.0 --port 9000
# Clients authenticate with the token:
# curl -H "Authorization: Bearer $CODA_HTTP_BEARER_TOKEN" http://host:9000/mcp
# Config comes from env vars
CODA_API_TOKEN=your-token uvx mcp-coda --read-only
```
`--coda-token` still works but is deprecated and hidden from `--help`: it exposes the token to `ps` and shell history. Use `CODA_API_TOKEN`.
The server loads `.env` files from the working directory automatically via `python-dotenv`.
## Docker
The image is a minimal, non-root `python:3.13-slim` build. The image defaults to locked read-only (`MCP_READ_ONLY=true`); pass `-e MCP_READ_ONLY=false` to allow writes.
```bash
docker build -t mcp-coda .
# stdio (default), locked read-only
docker run -i --rm -e CODA_API_TOKEN=your-token mcp-coda
# HTTP transport on a non-loopback host requires a bearer token (min 16 chars);
# clients then send Authorization: Bearer <token>
BEARER=$(openssl rand -hex 32)
docker run --rm -p 9000:9000 \
-e CODA_API_TOKEN=your-token \
-e CODA_HTTP_BEARER_TOKEN="$BEARER" \
mcp-coda --transport streamable-http --host 0.0.0.0 --port 9000
```
## FAQ
### Does mcp-coda support MCP 2.0 (the 2026-07-28 spec)?
Yes. mcp-coda implements the MCP 2026-07-28 specification, commonly called MCP 2.0. It also works with 2025-11-25 clients.
### Which transports does mcp-coda support?
The server supports `stdio` (the default) and `streamable-http`. It also supports `sse`, but the 2026-07-28 specification deprecates it. Prefer `streamable-http` for remote connections.
### Can I run mcp-coda read-only so the assistant cannot change anything?
Yes. Start it with `MCP_READ_ONLY=true` (or `CODA_READ_ONLY=true`). The 20 write tools and the write-workflow prompts are then not listed at all, so the model never sees them; a call to one returns "Unknown tool", and nothing can turn writes back on while the process runs. Set it in a launch you control — a managed MCP config, a container or systemd env, or a hosted deployment — because a user who starts the server themselves can start it without the variable. A Coda token has no scopes, so this lock is the only read-only control.
### What token scopes does mcp-coda need?
Coda tokens have no scopes. A token reaches every doc its owner can reach. Coda's sharing settings control doc-level access. Use a dedicated service account to limit exposure.
### Does mcp-coda work with self-hosted Coda?
Coda is a hosted service, so there is no self-hosted deployment. Set `CODA_BASE_URL` only to target a proxy or a non-default API base.
### Which clients work with mcp-coda?
mcp-coda works with Claude Desktop, Claude Code, Cursor, Windsurf, VS Code Copilot, and any MCP-compatible client over stdio or HTTP.
### How many tools, resources, and prompts does it provide?
mcp-coda provides 54 tools, 12 resources, and 5 prompts. They cover docs, pages, tables, rows, formulas, controls, permissions, folders, publishing, automations, and analytics.
## Related MCP Servers
- [mcp-gitlab](https://github.com/vish288/mcp-gitlab) — GitLab integration (83 tools, 7 resources, 6 prompts)
- [mcp-atlassian-extended](https://github.com/vish288/mcp-atlassian-extended) — Jira + Confluence integration (22 tools, 15 resources, 5 prompts)
- [mcp-argocd](https://github.com/vish288/mcp-argocd) — Argo CD integration (37 tools, 7 resources, 7 prompts)
## Development
```bash
git clone https://github.com/vish288/mcp-coda.git
cd mcp-coda
uv sync --all-extras
uv run pytest --cov
uv run ruff check .
uv run ruff format --check .
```
## License
MIT
TDQS
Scored across 54 tools
Most tools target distinct resources and actions, and list/get pairs plus analytics summary vs per-item tools are clarified in descriptions. Minor confusion possible between coda_get_page_content/coda_export_page and among several analytics endpoints, but descriptions largely guide selection.
All tools use a coda_ prefix and snake_case, overwhelmingly following a verb_noun pattern (e.g., coda_create_doc, coda_list_rows, coda_delete_permission). The few domain-specific names like coda_whoami still fit the overall convention.
54 tools is well above the 25+ threshold for 'too many' and risks overwhelming an agent. While each maps to a distinct Coda API operation, consolidation of analytics summaries and CRUD families could reduce the surface without losing coverage.
The set covers docs, folders, pages, tables, rows, columns, permissions, analytics, and publishing lifecycles thoroughly. Minor gaps like comments or webhook tools may exist, but core CRUD and administrative operations are well represented.