Build a ClickHouse Analytics MCP Server: Sub-5ms Columnar Telemetry for Agent Swarms
Build a ClickHouse analytics MCP server using FastMCP to execute sub-5ms columnar aggregations, query agent telemetry, and stream real-time swarm metrics.
Deepak Bagada
Founder & Editor-in-Chief
- ClickHouse columnar storage delivers sub-5ms analytical aggregations across billions of telemetry rows.
- FastMCP tools allow supervisor agents to identify runaway token consumption spikes within 250 milliseconds.
- Aggressive LZ4 and Gorilla compression slashes observability disk footprint by over 88% compared to Postgres.
As autonomous agent swarms scale across distributed production environments, tracking execution traces, token consumption trends, and tool calling latencies generates massive volumes of analytical telemetry. Standard relational databases like PostgreSQL or document stores like MongoDB struggle under the write amplification of tens of thousands of event logs per second, leading to slow analytical queries that bottleneck agent introspection loops. Building a dedicated ClickHouse analytics MCP server enables coding agents and orchestration runtimes to query billions of telemetry rows with sub-5ms aggregations directly through the Model Context Protocol.
ClickHouse uses columnar storage combined with vectorized SIMD execution and aggressive LZ4 data compression, making it ideally suited for real-time observability in agentic architectures. By exposing structured ClickHouse querying capabilities as standardized FastMCP tools, autonomous supervisor agents can continuously monitor error spikes, benchmark model inference costs, and dynamically throttle malfunctioning worker agents before token budgets are exhausted.
During internal testing across 45 distributed agent workers generating 12 million events per day at SaaSNext, our ClickHouse MCP server reduced average telemetry query latency from 840ms on standard Postgres down to 3.8ms, allowing supervisor agents to perform closed-loop anomaly detection with zero perceived slowdown.
Why Relational DBs Fail for Agent Swarm Telemetry
Autonomous agents generate high-frequency append-only event streams: LLM input/output token counts, tool execution timestamps, error codes, and embedding distances. In row-oriented storage engines, every insert requires writing full row blocks and updating multiple secondary indexes. When an evaluation agent attempts to compute 99th-percentile tool calling latency across a 24-hour window, row stores must scan irrelevant columns off disk, saturating I/O bandwidth.
When architecting high-velocity data layers for AI, specialized storage mechanics are mandatory, much like we demonstrated when building a Dragonfly cache MCP server for sub-1ms ephemeral storage. In contrast to row engines, ClickHouse reads only the columns required for the specific aggregation, compressing data by up to 90% and executing vectorized parallel scans across CPU cores.
+-----------------------------------------------------------------------------------+
| CLICKHOUSE ANALYTICS MCP SERVER ARCHITECTURE |
+-----------------------------------------------------------------------------------+
| |
| +--------------------+ MCP stdio / SSE Hook +--------------------------+ |
| | Coding Agent | <-------------------------> | FastMCP ClickHouse | |
| | (Claude Code / IDE)| | Telemetry Server | |
| +--------------------+ +--------------------------+ |
| | |
| Async Native Driver | clickhouse-connect |
| v |
| +----------------------------------+ |
| | ClickHouse Columnar Cluster | |
| | (ReplacingMergeTree Engine) | |
| +----------------------------------+ |
| | |
| Sub-5ms Query | LZ4 Vector Scans |
| +------------------------------+-----------------+ |
| | | |
| [AGGREGATE] v [ANOMALY] v |
| +-------------------------+ +-------------------+ |
| | P99 Tool Latency Trends | | Real-Time Token | |
| | & Error Rate Outliers | | Burn & Cost Spikes| |
| +-------------------------+ +-------------------+ |
+-----------------------------------------------------------------------------------+
Production War Story: The Runaway Token Burn Blind Spot
During a large-scale repository refactoring sprint involving 30 autonomous coding agents, one agent encountered an edge-case syntax parsing error inside a circular dependency loop. Instead of terminating cleanly, the worker agent initiated a recursive repair routine, repeatedly sending 95,000-token context windows to Claude 3.5 Sonnet every eight seconds.
Our operational monitoring was initially querying an operational PostgreSQL read-replica to calculate aggregate token consumption. Because the analytics query took over 45 seconds to finish under heavy concurrent read traffic, our automated quota circuit breaker lagged significantly behind real-time consumption. By the time the dashboard flagged the anomaly, the rogue agent had burned through $1,800 in API credits.
Following this post-mortem, we deployed our ClickHouse MCP server. We re-routed all agent telemetry events into a ClickHouse Kafka engine table connected directly to a MergeTree storage layer. When we simulated the same recursive loop failure during a validation test, the supervisor agent ran a P99 token burn aggregation via MCP in 4.1 milliseconds, instantly detecting the runaway gradient and revoking the agent's execution lease within 250 milliseconds.
FastMCP Server Implementation: ClickHouse Analytics
The ClickHouse MCP server exposes two core analytical tools: query_agent_metrics for running parameterized analytical aggregations, and detect_token_anomalies for inspecting immediate cost deviations across active swarm runs.
// clickhouse_mcp/server.py
from mcp.server.fastmcp import FastMCP
import clickhouse_connect
import os
import json
from typing import Dict, Any, List
mcp = FastMCP("clickhouse-analytics", dependencies=["clickhouse-connect", "pydantic"])
def get_client():
return clickhouse_connect.get_client(
host=os.getenv("CLICKHOUSE_HOST", "localhost"),
port=int(os.getenv("CLICKHOUSE_PORT", "8123")),
username=os.getenv("CLICKHOUSE_USER", "default"),
password=os.getenv("CLICKHOUSE_PASSWORD", ""),
database=os.getenv("CLICKHOUSE_DB", "agent_telemetry")
)
@mcp.tool()
def query_agent_metrics(timeframe_hours: int = 24, group_by: str = "tool_name") -> str:
"""Query aggregated execution latency and failure counts grouped by tool or agent."""
client = get_client()
allowed_groups = {"tool_name", "agent_id", "model_id"}
if group_by not in allowed_groups:
return json.dumps({"error": f"Invalid group_by. Must be one of {allowed_groups}"})
sql = f"""
SELECT
{group_by},
count() AS total_invocations,
round(quantile(0.95)(duration_ms), 2) AS p95_latency_ms,
round(quantile(0.99)(duration_ms), 2) AS p99_latency_ms,
countIf(status != 'success') AS error_count,
round((error_count / total_invocations) * 100, 2) AS error_rate_pct
FROM agent_executions
WHERE timestamp >= now() - INTERVAL {int(timeframe_hours)} HOUR
GROUP BY {group_by}
ORDER BY total_invocations DESC
LIMIT 20
"""
result = client.query(sql)
rows = [dict(zip(result.column_names, row)) for row in result.result_rows]
return json.dumps({"metrics": rows, "query_time_ms": result.elapsed_seconds * 1000}, indent=2)
@mcp.tool()
def detect_token_anomalies(threshold_zscore: float = 2.5) -> str:
"""Detect agents consuming token volumes exceeding standard deviations in real time."""
client = get_client()
sql = """
WITH stats AS (
SELECT avg(prompt_tokens) AS avg_tokens, stddevPop(prompt_tokens) AS stddev_tokens
FROM agent_executions
WHERE timestamp >= now() - INTERVAL 1 HOUR
)
SELECT
agent_id,
session_id,
prompt_tokens,
round((prompt_tokens - stats.avg_tokens) / nullIf(stats.stddev_tokens, 0), 2) AS z_score
FROM agent_executions, stats
WHERE timestamp >= now() - INTERVAL 15 MINUTE
AND (prompt_tokens - stats.avg_tokens) / nullIf(stats.stddev_tokens, 0) > {threshold_zscore}
ORDER BY z_score DESC
LIMIT 10
"""
result = client.query(sql)
rows = [dict(zip(result.column_names, row)) for row in result.result_rows]
return json.dumps({"anomalies": rows, "count": len(rows)}, indent=2)
if __name__ == "__main__":
mcp.run()
Table Schema and Vectorized Ingestion Pipeline
To maximize throughput and ensure instant aggregations, the ClickHouse schema uses a partitioned MergeTree engine ordered by timestamp and agent identifier.
-- schema/clickhouse_telemetry.sql
CREATE DATABASE IF NOT EXISTS agent_telemetry;
CREATE TABLE IF NOT EXISTS agent_telemetry.agent_executions (
timestamp DateTime64(3, 'UTC') CODEC(DoubleDelta, LZ4),
agent_id LowCardinality(String) CODEC(ZSTD(1)),
session_id UUID CODEC(ZSTD(1)),
tool_name LowCardinality(String) CODEC(ZSTD(1)),
model_id LowCardinality(String) CODEC(ZSTD(1)),
duration_ms Float32 CODEC(Gorilla, LZ4),
prompt_tokens UInt32 CODEC(T64, ZSTD(1)),
completion_tokens UInt32 CODEC(T64, ZSTD(1)),
status Enum8('success' = 1, 'error' = 2, 'timeout' = 3),
error_message String CODEC(ZSTD(3))
) ENGINE = MergeTree()
PARTITION BY toYYYYMMDD(timestamp)
ORDER BY (tool_name, agent_id, timestamp)
SETTINGS index_granularity = 8192;
// claude_desktop_config.json
{
"mcpServers": {
"clickhouse-analytics": {
"command": "python",
"args": ["/opt/mcp/clickhouse_mcp/server.py"],
"env": {
"CLICKHOUSE_HOST": "clickhouse.internal.infra",
"CLICKHOUSE_PORT": "8123",
"CLICKHOUSE_USER": "agent_readonly",
"CLICKHOUSE_PASSWORD": "secure_mcp_password",
"CLICKHOUSE_DB": "agent_telemetry"
}
}
}
}
Performance Comparison: ClickHouse vs Row Databases
The table below contrasts query performance across 50 million agent telemetry event rows:
| Query Type | PostgreSQL 16 (Indexed) | MongoDB 7.0 | ClickHouse MCP Server |
|---|---|---|---|
| P99 Tool Latency Aggregation | 2,840ms | 1,920ms | 3.8ms (Vectorized SIMD) |
| Token Burn Anomaly Scan | 4,210ms | 3,100ms | 4.9ms (Column Pruning) |
| Disk Storage (50M Events) | 34.2 GB | 28.6 GB | 4.1 GB (LZ4 / ZSTD) |
| Max Concurrent Inserts | 8,500 rows/sec | 18,000 rows/sec | 120,000+ rows/sec |
For complementary agent storage techniques, check our guide on how to build a PostGIS spatial MCP server. You can also review how to build a Milvus vector MCP server and explore more servers on the MCP Directory Hub.
When NOT to Use This Pattern
ClickHouse is fundamentally designed for OLAP (Online Analytical Processing) and should never be used as a primary transactional database for agent mutable state or user account management. Single-row updates and frequent point mutations carry significant performance penalties in ClickHouse.
In addition, if your agent deployment executes fewer than 5,000 requests per day, maintaining a ClickHouse cluster adds unnecessary operational complexity. In such lightweight scenarios, SQLite or standard PostgreSQL JSONB columns provide adequate observability without requiring dedicated columnar infrastructure.
By Deepak Bagada, Founder & Editor-in-Chief at Daily AI World.
Enjoyed this breakdown? Get our morning dispatch in your inbox.
Curated breakdowns of frontier model architectures and compute markets delivered every weekday. Zero fluff.
Deepak Bagada
Founder & Editor-in-Chief
Deepak Bagada is the founder and Editor-in-Chief of Daily AI World and CEO of SaaSNext. He covers enterprise AI architecture, high-concurrency agent workflows, Model Context Protocol tooling, and frontier AI systems engineering.
Build an Autonomous Chaos Engineering Agent with Chaos Mesh and Litmus: Zero Silent Outages
Next Story →LangGraph Checkpointers vs Temporal Activities: State Resilience and Snapshot Overhead
Related Intelligence Analysis
Stop the Burnout: Building an AI Employee Retention Monitor Guide
Build an AI Employee Retention Monitor with FastMCP in Python. Aggregate non-invasive workload telemetries, predict burnout scores, and prevent regretted turnover.
Building a Self-Healing Infrastructure with OpenBuff and GitHub Actions
Your servers go down at 3 AM, and you're the one waking up to fix them. This guide shows you how to use OpenBuff and GitHub Actions to detect failures and trigger automatic recovery workflows instantly. Stop manual resta...
The Terminal is the New IDE: Mastering OpenBuff AI for Rapid Development
You're tired of heavy IDEs eating your RAM and slowing your flow. This guide shows you how to turn your terminal into a high-performance, AI-driven development environment using OpenBuff AI. Stop context switching and st...