MCP tools
What your AI can call
Every tool and resource the mcpdata MCP server offers at https://[you].mcpdata.org/mcp
(streamable HTTP, Authorization: Bearer <key>), generated from the server's own definitions.
Machine-readable: mcp-tools.json.
Tools
- list_tables()
- List every table in CDB's registry: name, row count, last-updated time. Call this first when you don't already know a table's exact name.
- describe_table(table_name)
- Describe one table's columns (name + dtype) and row count. Always call this before querying a table you haven't seen before -- guessing column names or types wastes a round trip on a QuerySpecError.
- query(table_name, columns?, filters?, group_by?, agg?, sort?, limit?, cursor?)
- Run a structured query against one table. No raw SQL -- build the query from these arguments instead: - columns: which columns to return (omit for all columns). - filters: list of {"column": str, "op": str, "value": ...}. op is one of eq/ne/gt/gte/lt/lte/in/not_in/like/is_null/is_not_null/between. - group_by: list of column names to group by. - agg: required if group_by is set. list of {"func": "sum"/"avg"/"count"/"min"/"max"/"stdev"/"var", "column": str, "alias": optional str}. - sort: list of {"column": str, "desc": bool}. - limit: max rows to return this call (default 1000, hard cap 10000). A page is also capped at about 64 KB of rows, so wide rows give fewer per page. - cursor: pass back a previous response's next_cursor, with the SAME other arguments, to get the next page. The response always includes truncated (bool) and next_cursor (str or null) -- check truncated before assuming you have the whole answer; when known, total_rows is the full result's size. Pages of a plain table read cost the same however far in they are; a filtered/sorted/grouped result is kept on the server for 15 minutes of paging (up to 100,000 rows -- past that, narrow it or use export_query). See cdb://skills/query-guide.
- list_views()
- List the stored views you can run with query_view: name, @parameters (types and defaults) and description. A view is saved SQL -- joins, CASE, UNION, AS OF time travel, multi-step scripts -- so it answers questions the one-table query tool can't.
- query_view(name, params?, limit?, cursor?)
- Run a stored view and return its rows, paged like query(): at most limit rows (default 1000, hard cap 10000) and about 64 KB per page, with truncated / next_cursor / total_rows. - name: the view's name, from list_views (e.g. "reports.daily_pnl"). - params: values for its @parameters, e.g. {"from": "2026-01-01", "desk": "Credit"}; a parameter with a default can be left out. - cursor: a previous response's next_cursor (same name and params) for the next page. The view runs once; its result is kept on the server for 15 minutes of paging (up to 100,000 rows). Works with a Read key. To make a new view, create_view (ReadWrite).
- export_query(table_name, columns?, filters?, group_by?, agg?)
- Export the FULL (uncapped) result of a structured query to a Parquet file plus a JSON manifest, and return their paths. Use this instead of paging through query() when you genuinely need every row, not a sample -- e.g. before bulk-processing a large result outside CDB. Takes the same filters/group_by/agg arguments as query(). Needs the ReadWrite role (it writes a file on the server).
- Export a structured query's result as a shareable artifact and return a share_id another agent can pass to import_shared to get their own real, queryable copy. Takes the same filters/group_by/agg arguments as query(). Needs the ReadWrite role (it writes a file on the server).
- Import a dataset another agent shared via share_dataset, given its share_id. Ingests it as a real table in your own tenant (via the same ingest pipeline any other data upload uses) and returns the new table's name -- query it like any other table afterward.
- create_table(name, kind, columns, shard_by?, width?, rows?, request_id?)
- Create a table of your own to keep structured data in -- then query it with query/describe_table like any table. Names must be under agent.* (e.g. agent.findings, agent.prices.daily); nothing else can be written. - kind: "log" (append_rows adds rows; reads return every row ever appended -- events, findings, records) or "snapshot" (replace_rows writes the full current contents; reads return only the latest -- a current list, a reference table). Every write is kept as a version, so history is never lost. - columns: [{"name": "price", "type": "double"}, ...]; types: string, int64, double, bool, datetime. - shard_by (snapshot only): a column such as "book" -- each key's rows are kept and replaced on their own, so replace_rows with book B12's rows updates B12 and leaves every other book as it was. - width (log only, default 1): write every append as exactly this many shards -- only worth raising for very large appends, which then read in parallel; small appends at width > 1 just make more small files. - rows: optional first rows (objects keyed by column name). - request_id: optional; repeating a call with the same id (e.g. after a timeout) won't write twice.
- append_rows(table, rows, add_columns?, drop_columns?, request_id?)
- Append rows to one of your log tables (agent.*). rows: objects keyed by column name (a missing column is NULL). At most 10,000 rows per call -- batch several records per call rather than one call per record. add_columns: [{"name", "type"}] to add new columns (earlier rows read them as NULL); drop_columns: names to stop writing (earlier rows keep their values). A column's type can never change. request_id: repeat-safe retries.
- replace_rows(table, rows, add_columns?, drop_columns?, remove_keys?, request_id?)
- Replace the contents of one of your snapshot tables (agent.*): rows are the new full contents; reads return only this latest version (earlier versions are kept as history). For a table created with shard_by, only the keys present in rows are replaced -- every other key keeps its rows; remove_keys: keys to empty. add_columns / drop_columns as in append_rows; types never change. At most 10,000 rows per call. request_id: repeat-safe.
- create_view(name, select_sql, params?, description?)
- Create (or replace) a stored view — a named, saved query that reports and other queries reuse. This is how you turn a user's natural-language request into a durable definition: translate what they asked into CDB SQL for select_sql, then call this. Use list_tables / describe_table first to get exact table and column names rather than guessing. - name: the view's name, optionally namespaced, e.g. "reports.daily_pnl". - select_sql: the query the view returns, e.g. "SELECT underlying, SUM(notional) AS notional FROM DeribitTrades WHERE ts >= @from GROUP BY underlying". It may reference any @parameters declared in params. - params: optional [{"name","type","default"?}] (type: STRING/INT/DOUBLE/BOOL/DATETIME) so a report can bind them, e.g. [{"name":"from","type":"DATETIME"}]. - description: optional human-readable description. Needs the ReadWrite role. Then create_report(view=name, ...) renders and schedules it. Prefer a GROUP BY / aggregated view over raw rows — a report should return a small, already-summarised result. Read the cdb://skills/view-guide resource for CDB's SQL dialect and view syntax before composing select_sql; if a call fails, the engine's error says what to fix.
- drop_view(name)
- Delete a stored view by name. Any report pointing at it will fail until the view is recreated.
- list_reports()
- List this instance's scheduled reports, each with its schedules and recent runs. A report is a saved view rendered on a cadence and emailed. Call this before scheduling or running one, to get its report_id.
- create_report(name, view, params?, format?, chart?, pivot?)
- Define a report: a stored view (its definition, which must already exist -- see SHOW VIEWS / the workbench) rendered to `format` (html, csv or xlsx). params binds the view's @parameters, e.g. {"desk": "EMEA"}. Returns {"report_id"}. This only defines the report; use schedule_report to send it on a cadence or run_report to send it once now. The report's data stays a live view -- each run re-queries it. chart (html only): add an interactive chart above the table, over the same rows. It is the chart block body: {"chart": "bar"|"line"|"area"|"scatter"|"pie"|"histogram", "x": <column>, "y": <column>, "color": <column>?, "title": <str>?}. e.g. {"chart":"bar","x":"strategy","y":"notional"}. For pie, y is the slice size and x (or color) the category; for histogram, only x is needed (it is binned and counted). Charts are self-contained (the Vega library is inlined, so the file opens offline) but need a browser that runs scripts; email clients don't, so the table always carries the numbers, as do csv/xlsx. For the full data -> views -> reports -> pivot workflow and when a pivot makes sense, read cdb://skills/reporting-guide. pivot (optional): attach a pivot preset so the report shows an "Open in pivot" button that opens the workbench pivot pre-arranged. A pivot-config fragment, e.g. {"rows":["Category"],"columns":["Gender"],"values":[["SalesAmount","sum"]],"table":"aw.InternetSales"}; omit table/view to pivot the report's own view. Reports without a pivot preset show no pivot button.
- schedule_report(report_id, cron, recipients, enabled?)
- Schedule a report by cron (5 fields "min hour day-of-month month day-of-week", or @daily/@weekly/@hourly; UTC). recipients are emailed the rendered report each run. e.g. cron "0 8 * * 1" = every Monday 08:00 UTC. Returns {"schedule_id"}. Scheduling is an outbound email on a timer -- set recipients deliberately.
- run_report(report_id, recipients?)
- Run a report once now: re-query its view, render it, and (if recipients are given) email it. Returns the run result {"status", "rows", ...}. Use this to preview a report before scheduling it, or for an ad-hoc send.
- list_report_runs(report_id, limit?)
- The run history of one report (newest first): status (ok/failed/running), row count, duration, any error. This is how you check whether scheduled sends are succeeding.
- delete_report(report_id)
- Delete a report and all its schedules. Past run records and already-rendered output files are left alone.
Resources
- cdb://skills/query-guide
- Static, hand-authored guidance on querying CDB efficiently: when to filter, when to aggregate instead of fetching raw rows, how pagination and truncation work, and when to reach for export_query/share_dataset.
- cdb://skills/view-guide
- How to write a correct CDB view with create_view: the view DDL, CDB's SQL dialect (aggregates, WHERE, GROUP BY/HAVING/ORDER BY, JOIN, AS OF time-travel with #date# literals), view @parameters, and how to shape a good report view (aggregate, parameterise the date range). Read this before composing select_sql for create_view.
- cdb://skills/reporting-guide
- The end-to-end recipe for turning data into a full set of reports: upload (create_table/append_rows, or an out-of-band load), shape aggregated parameterised views (create_view), create reports with charts (create_report), add a pivot preset WHERE IT MAKES SENSE (create_report pivot=, base-table source), and schedule/run them. Read this before building a reporting dataset.
- cdb://schema
- Every table currently in the registry, with row counts -- a quick catalog to scan before drilling into one table's own schema.
- cdb://schema/{table_name}
- One table's columns, types, and row count -- generated live from the same data describe_table exposes, formatted for a quick read.