MCP サーバー

AI エージェントがプロジェクトのデータを直接読み書きできます。ドキュメント全体をコピーして AI ツールに貼り付けてください。

技術ドキュメントは英語のままにしています — AI コーディングツールは英語の原文で最も正確に動作します。このページをそのまま AI ツールに貼り付けてください。

@cradler/mcp

An MCP (Model Context Protocol) server that lets AI agents — Claude Desktop, Cursor, Claude Code, and any other MCP client — read and write the data in a single Cradler project.

It runs over stdio and exposes eight tools backed by the Cradler data gateway: list_collections, get_schema, query, aggregate, insert, upsert, update and delete.

Configuration

The server is configured entirely through three environment variables:

VariableDescription
CRADLER_API_URLGateway base URL, e.g. https://gateway.cradler.ai
CRADLER_PROJECT_IDYour project slug
CRADLER_API_KEYA project API key (anon or service)

If any are missing, the server exits immediately with a clear error message.

You can find these values in the Cradler dashboard, on your project's page.

Adding it to an AI tool

MCP clients are configured with an mcpServers block. Add an entry that runs @cradler/mcp and passes your project's credentials as env:

{
  "mcpServers": {
    "cradler": {
      "command": "npx",
      "args": ["-y", "@cradler/mcp"],
      "env": {
        "CRADLER_API_URL": "https://gateway.cradler.ai",
        "CRADLER_PROJECT_ID": "your-project-slug",
        "CRADLER_API_KEY": "your-project-api-key"
      }
    }
  }
}

Where this JSON lives depends on the tool:

  • Claude Desktop — claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/claude_desktop_config.json).
  • Cursor — ~/.cursor/mcp.json (global) or .cursor/mcp.json (per project).
  • Claude Code — claude mcp add, or a .mcp.json file in the project root.

After saving, restart the tool. The agent will then have the Cradler tools.

Running a local build instead

If you have cloned this repo and run pnpm build, you can point the client at the built file directly:

{
  "mcpServers": {
    "cradler": {
      "command": "node",
      "args": ["/absolute/path/to/cradler-mcp/dist/index.js"],
      "env": {
        "CRADLER_API_URL": "https://gateway.cradler.ai",
        "CRADLER_PROJECT_ID": "your-project-slug",
        "CRADLER_API_KEY": "your-project-api-key"
      }
    }
  }
}

Tools

ToolWhat it does
list_collectionsList every collection with its columns. Use it to discover what exists.
get_schemaReturn one collection's columns (name → type).
queryRead rows with optional filters, sorting, column projection and paging.
aggregatecount / sum / avg / min / max over every matching row, optionally grouped.
insertInsert one or more rows. New collections and columns are created automatically.
upsertInsert, or update the row with the same onConflict values (default id).
updatePatch every row matching the given filters (filters required).
deleteDelete every row matching the given filters (filters required — never the lot).

Filters

query, aggregate, update and delete accept filter objects of the form { field, op, value }. Valid operators:

eq, neq, gt, gte, lt, lte, like, ilike, in, is_null.

For in, value is an array; for is_null, value is a boolean. Every other operator requires a value — a filter without one is rejected rather than silently matching nulls.

A comparison never matches a row where the field is null — use is_null for that. A field that has never been written to the collection is null on every row: filters on it match nothing (on update / delete too, so they change nothing), is_null: true on it is refused for update / delete, and the result carries a warnings entry naming the field.

query defaults to a limit of 100 rows (maximum 1000) so large tables are not pulled in full. Because of that cap, never compute a statistic from query rows — use aggregate, which runs in the database over every matching row:

{
  "collection": "orders",
  "groupBy": ["status"],
  "aggregates": [
    { "fn": "count" },
    { "fn": "sum", "field": "amount", "as": "revenue" }
  ],
  "filters": [{ "field": "created_at", "op": "gte", "value": "2026-01-01" }]
}

Each result row holds the groupBy fields plus one key per aggregate — its as, or count / <fn>_<field> (sum_amount) by default. sum and avg need a numeric field; min and max a number, text, date or time. limit (default 100, max 1000) caps the number of groups returned, not the rows aggregated.

update and delete require a non-empty filters array — there is no way to update or delete an entire collection in one call.

Unique fields

upsert matches stored rows on onConflict (default ["id"]) and updates only the fields each row includes; ignoreDuplicates: true keeps a stored row unchanged and returns it. insert takes an optional unique list, and a duplicate of those fields fails with constraint_violation.

Both need the fields to be unique in the collection already (id always is). The MCP tools never make a field unique on their own, unlike the SDK: doing so is permanent — every later duplicate insert fails, the app's own included — and an agent working on live data could pin a field that is meant to repeat. To create it, pass createUnique: true, and only for values that must never repeat (emails, external ids, idempotency keys). If existing rows already repeat the value, the call fails and says how many. Only a service key can make a field unique.

Development

pnpm install
pnpm build       # bundle to dist/index.js with tsup
pnpm typecheck   # tsc --noEmit
pnpm dev         # rebuild on change

License

MIT