SQL over what your agents did, on the copy they search.
Push traces, tool calls and events as documents, or import CSV, NDJSON and Parquet. Ask SQL with GROUP BY, exact counts and full-text predicates over REST or MCP. No warehouse to keep running: idle data costs only its bytes in S3.
One copy for search and SQL
The documents your agents search are the rows SQL counts. contains() and grep() in WHERE use the text index, so a needle in millions of log lines skips the blocks that can’t match.
Nothing to keep warm
Columns live in object storage next to the text. An idle analytics namespace costs only its bytes; the first query reads what it needs and the server’s cache keeps the rest close.
Bounded and metered
Each query has a time limit, a memory cap and a row cap, so a bad GROUP BY gets an error instead of taking a server down. Every answer reports its time, blocks scanned and object-store reads.
Log it, then ask
Each document’s metadata keys become typed columns; missing values are SQL NULL. The text stays searchable and filterable alongside.
EV=https://api.skimmerdb.com/v1/namespaces/agent-events # one document per tool call: the text is searchable, the metadata is columns curl $EV/docs -H "Authorization: Bearer $KEY" -d '{"docs": [ {"id": "run-981:3", "text": "TimeoutError: upstream took 30s (GET /v2/orders)", "meta": {"agent": "support-bot", "tool": "http_get", "ok": false, "ms": 30012, "tokens": 1840, "at": "2026-10-16 09:12:44"}}]}' # or a whole export at once: CSV, NDJSON or Parquet, up to 2 GB a request curl "$EV/import?format=parquet&id=call_id&text=error" \ -H "Authorization: Bearer $KEY" --data-binary @tool_calls.parquet
# failures, latency and tokens per agent and tool curl $EV/sql -H "Authorization: Bearer $KEY" -d @- <<'EOF' {"sql": "SELECT agent, tool, COUNT(*) AS calls, SUM(CASE WHEN ok = 0 THEN 1 ELSE 0 END) AS failed, AVG(ms) AS avg_ms, MAX(ms) AS worst_ms, SUM(tokens) AS tokens FROM docs GROUP BY agent, tool ORDER BY calls DESC LIMIT 20"} EOF # the text index and SQL together: which tools time out, for whom curl $EV/sql -H "Authorization: Bearer $KEY" -d @- <<'EOF' {"sql": "SELECT agent, tool, COUNT(*) AS n FROM docs WHERE contains(text, 'timeout') AND ok = 0 GROUP BY agent, tool ORDER BY n DESC"} EOF
Measured
ClickBench’s 43 queries over its 10M-row web analytics table, imported through the API. Hot = best of runs 2–3.
| ClickBench, 10M rows | Result |
|---|---|
| Hot geomean, 43 queries, through HTTP (auth, limits, metering, JSON) | 9.3 ms |
| clickhouse-local, same box, official MergeTree schema | 28.5 ms |
| COUNT(*) over 10M rows, through HTTP | 0.6 ms |
| First run of all 43 after import, namespace in S3 | 2.2 s in all |
| Answers vs the column engine run standalone | 33 of 43 identical; 10 differ only in tied rows |
| Import through the API | Rows / s |
|---|---|
| Parquet, 10 × 1M rows (1.5 GB) | 488–512k rows/s |
| CSV with a schema, 10 × 1M rows (7.9 GB) | 436k rows/s, 343 MB/s |
| NDJSON, 1M rows (2.3 GB) | 175k rows/s, 408 MB/s |
EC2 m7gd.2xlarge (8 vCPU, 32 GB), every engine on the same box one after another, 8 threads; ClickHouse 26.10 as clickhouse-local with the official schema. One benchmark on one machine, not a claim about every workload: broad filters and frequently changing dictionaries are still slower than ClickHouse and DuckDB in our comparisons, and SkimmerDB SQL has no joins yet.
Safe to hand to an agent
An agent with a read-only key can run SQL over its own runs through the sql_query MCP tool. Whatever it writes, the server stays up:
- Time limit per query from the plan (10 s free, 30 s pro).
- Memory caps on groups, distinct values and remote reads: past one, an error, not an outage.
- Concurrency per tenant and per server; answers capped at 10,000 rows.
- Isolation. Only
FROM docs, the namespace the key may read.
What SQL covers today
SELECT … FROM docs with WHERE, GROUP BY, HAVING, ORDER BY, LIMIT/OFFSET; COUNT, COUNT(DISTINCT), SUM, AVG, MIN, MAX, CASE WHEN, COALESCE, LIKE, IN, IS NULL, DATE_TRUNC, REGEXP_REPLACE; contains(text, …) and grep(text, …); SHOW COLUMNS.
Not yet: joins, subqueries, window functions. They are next on the roadmap.
Questions
What is agent analytics?
Questions about what your agents did: which tools fail and how often, latency and token use per agent, which errors are new, how a change moved success rates. It needs fast aggregation over traces and events, and text search over their messages.
What SQL does SkimmerDB support?
One SELECT over a namespace’s documents (FROM docs) with WHERE, GROUP BY, HAVING, ORDER BY, LIMIT and OFFSET; COUNT, COUNT(DISTINCT), SUM, AVG, MIN, MAX, CASE WHEN, COALESCE, LIKE, IN, IS NULL, DATE_TRUNC and REGEXP_REPLACE, with SQL NULL semantics; plus contains() and grep() over the text. Joins, subqueries and window functions are not supported yet: they are on the roadmap.
How do I get data in?
Write documents as your agents run (each one’s metadata becomes columns, typed from the values), or import a file: CSV, NDJSON or Parquet, up to 2 GB and 1M rows a request, all visible together or not at all.
Is it faster than ClickHouse or DuckDB?
On ClickBench’s 43 queries over 10M rows, warm, on one 8-vCPU box, SkimmerDB through its HTTP API had a geometric mean of 9.3 ms against 28.5 ms for clickhouse-local. That is one benchmark, not every workload: in our own comparisons broad filters and frequently changing dictionaries are still slower than ClickHouse and DuckDB, and both have far wider SQL.
Can an agent query its own analytics?
Yes. The sql_query tool on the MCP server runs the same SQL. Give the agent a read-only key limited to its namespaces and it can check its own failure rates before choosing a tool.
What does a query cost?
A SQL query is metered as one query: $0.50 per million on pro, 100k a month free, plus object-store reads made by cold queries. Storage is $0.10 per GB-month.
Point your agents’ logs at it
Free up to 1 GB stored and 100k queries a month. No card to start.