Build an MCP Analytics Server: Product Analytics & Evals for AI Agent Sessions [2026]
Build an MCP analytics server that provides product analytics and evaluation metrics for AI agent sessions. Track every tool call, measure latency and success rates, compute aggregate performance metrics, and enable data-driven agent optimization — all through MCP tools.
Deepak Bagada
CEO, SaaSNext
- MCP analytics server provides end-to-end agent observability — every tool call tracked with timing and outcome
- DuckDB-powered time-series analytics handle millions of tool call records with sub-second queries
- A/B evaluation tools enable data-driven agent optimization across model versions and configurations
The MCP analytics server, scoring 42 points on Hacker News, provides product analytics and evaluation infrastructure for AI agent sessions. Unlike traditional APM tools that focus on server-side metrics, this server tracks agent-specific telemetry: which tools agents call, how fast they respond, what errors they encounter, which tool chains produce the best outcomes, and how agent behavior evolves over time.
- Tracks every agent tool call with timing, parameters, and result metadata
- Computes session-level metrics: success rate, latency p50/p99, tool usage frequency
- Exposes analytics as MCP tools that agents and developers can query
- Supports cohort analysis: compare agent behavior across model versions, prompt templates, or tool configurations
Architecture: Agent Observability Stack
flowchart TB
subgraph Agents
A[Agent Session]
B[Agent Session]
C[Agent Session]
end
subgraph MCP_Analytics
D[Telemetry Collector]
E[Metrics Engine]
F[Analytics API]
G[Eval Runner]
end
subgraph Storage
H[(Time-Series DB)]
I[(Session Store)]
end
A -->|MCP tool call| D
B -->|MCP tool call| D
C -->|MCP tool call| D
D --> H
D --> E
E --> F
F -->|analytics tools| A
F -->|analytics tools| B
G --> I
E --> G
Implementation
Step 1: Setup
mkdir mcp-analytics && cd mcp-analytics
python -m venv .venv && source .venv/bin/activate
pip install fastmcp==4.0 duckdb pandas
Step 2: Telemetry Ingestion
# telemetry_collector.py
from fastmcp import FastMCP
from datetime import datetime, timezone
import json
import duckdb
mcp = FastMCP("mcp-analytics")
# Local DuckDB for time-series storage
DB_PATH = "agent_analytics.duckdb"
def init_db():
conn = duckdb.connect(DB_PATH)
conn.execute("""
CREATE TABLE IF NOT EXISTS tool_calls (
session_id TEXT,
tool_name TEXT,
params TEXT,
result TEXT,
duration_ms FLOAT,
success BOOLEAN,
error TEXT,
timestamp TIMESTAMP,
model_name TEXT,
prompt_template TEXT
)
""")
conn.execute("""
CREATE TABLE IF NOT EXISTS session_metrics (
session_id TEXT PRIMARY KEY,
total_calls INTEGER,
success_rate FLOAT,
avg_duration_ms FLOAT,
model_name TEXT,
task_type TEXT,
start_time TIMESTAMP,
end_time TIMESTAMP
)
""")
conn.close()
init_db()
@mcp.tool()
def record_tool_call(
session_id: str,
tool_name: str,
params: str,
duration_ms: float,
success: bool,
error: str | None = None,
model_name: str | None = None
) -> dict:
"""Record a single tool call with timing and outcome"""
conn = duckdb.connect(DB_PATH)
conn.execute("""
INSERT INTO tool_calls VALUES (
?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, ?, ?
)
""", [session_id, tool_name, params, "", duration_ms, success, error, model_name, ""])
conn.close()
return {"recorded": True, "session": session_id, "tool": tool_name}
@mcp.tool()
def end_session(
session_id: str,
total_calls: int,
success_rate: float,
avg_duration_ms: float,
model_name: str | None = None,
task_type: str | None = None
) -> dict:
"""Record summary metrics for a completed session"""
conn = duckdb.connect(DB_PATH)
conn.execute("""
INSERT OR REPLACE INTO session_metrics VALUES (
?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP
)
""", [session_id, total_calls, success_rate, avg_duration_ms, model_name, task_type])
conn.close()
return {"session": session_id, "metrics_recorded": True}
Step 3: Analytics API Tools
# analytics_api.py
@mcp.tool()
def get_session_summary(session_id: str) -> dict:
"""Get complete analytics for a single session"""
conn = duckdb.connect(DB_PATH)
# Tool breakdown
tools = conn.execute("""
SELECT tool_name, COUNT(*) as calls,
AVG(duration_ms) as avg_duration,
SUM(CASE WHEN success THEN 1 ELSE 0 END) * 100.0 / COUNT(*) as success_rate
FROM tool_calls WHERE session_id = ?
GROUP BY tool_name
""", [session_id]).fetchdf()
# Session metrics
session = conn.execute("""
SELECT * FROM session_metrics WHERE session_id = ?
""", [session_id]).fetchdf()
conn.close()
return {
"session_id": session_id,
"tools": tools.to_dict(orient="records"),
"metrics": session.to_dict(orient="records")[0] if not session.empty else {}
}
@mcp.tool()
def get_aggregate_metrics(
time_window_hours: int = 24,
model_name: str | None = None
) -> dict:
"""Get aggregate metrics across all sessions"""
conn = duckdb.connect(DB_PATH)
query = """
SELECT
COUNT(DISTINCT session_id) as total_sessions,
COUNT(*) as total_tool_calls,
AVG(duration_ms) as avg_duration_ms,
MEDIAN(duration_ms) as p50_duration_ms,
PERCENTILE_CONT(0.99) WITHIN GROUP (ORDER BY duration_ms) as p99_duration_ms,
SUM(CASE WHEN success THEN 1 ELSE 0 END) * 100.0 / COUNT(*) as overall_success_rate
FROM tool_calls
WHERE timestamp >= NOW() - INTERVAL ? HOUR
"""
params = [time_window_hours]
if model_name:
query += " AND model_name = ?"
params.append(model_name)
result = conn.execute(query, params).fetchdf()
conn.close()
return result.to_dict(orient="records")[0] if not result.empty else {}
@mcp.tool()
def get_top_tools(limit: int = 10) -> list[dict]:
"""Get most frequently called tools"""
conn = duckdb.connect(DB_PATH)
result = conn.execute("""
SELECT tool_name, COUNT(*) as call_count,
AVG(duration_ms) as avg_duration,
SUM(CASE WHEN success THEN 1 ELSE 0 END) * 100.0 / COUNT(*) as success_rate
FROM tool_calls
GROUP BY tool_name
ORDER BY call_count DESC
LIMIT ?
""", [limit]).fetchdf()
conn.close()
return result.to_dict(orient="records")
Step 4: Evaluation Runner
The evaluation runner enables systematic A/B testing of agent configurations:
Model Comparisons: Compare GPT-6 Astra vs Qwen3.8-27B on the same test suite — measure latency, success rate, and cost per task. The eval runner queries the telemetry store to compute comparative metrics across model versions.
Prompt Template A/B: Test different prompt templates with the same model. The analytics server groups sessions by prompt_template and computes per-template metrics. This enables data-driven prompt engineering — find which template produces the highest success rate for each task type.
Tool Configuration Testing: Compare agent behavior with different tool sets. Run 50 test cases with tool set A and 50 with tool set B, then compare tool usage patterns, success rates, and average completion time.
Regression Detection: Run a baseline evaluation after every agent framework update. If key metrics (success rate, average latency) regress compared to the stored baseline, the eval runner alerts the development team before the change reaches production.
Step 4: Evaluation Runner
# eval_runner.py
@mcp.tool()
def run_eval(
eval_name: str,
model_a: str,
model_b: str,
test_cases: int = 50
) -> dict:
"""Run an A/B evaluation comparing two model versions"""
conn = duckdb.connect(DB_PATH)
result = conn.execute("""
SELECT
model_name,
COUNT(*) as calls,
AVG(duration_ms) as avg_latency,
SUM(CASE WHEN success THEN 1 ELSE 0 END) * 100.0 / COUNT(*) as success_rate
FROM tool_calls
WHERE model_name IN (?, ?)
GROUP BY model_name
""", [model_a, model_b]).fetchdf()
conn.close()
return {
"eval": eval_name,
"results": result.to_dict(orient="records"),
"recommendation": "Compare metrics to determine better model"
}
Production Reality Check & Failure Modes
1. Storage Growth
Agent sessions generate thousands of tool call records. DuckDB efficiently handles millions of rows but query performance degrades above 100M rows. Implement weekly partitioning by timestamp and prune sessions older than 90 days.
2. Metrics Latency
The analytics server adds ~5ms per recorded tool call. For latency-sensitive agents, batch records and flush every 10 calls. The context-slim MCP server shows similar batching patterns.
3. Session Correlation
Agents must pass a consistent session_id for accurate analytics. Enforce session ID generation in the agent framework rather than trusting individual agent implementations. The MCP God control plane can inject session IDs transparently via proxy middleware.
Key Takeaways
- MCP analytics server provides end-to-end agent observability — every tool call tracked with timing, outcome, and model version metadata.
- DuckDB-powered time-series analytics handle millions of tool call records with sub-second aggregate query performance.
- A/B evaluation tools enable data-driven agent optimization — compare model versions, prompt templates, and tool configurations side by side.
By Deepak Bagada, CEO at SaaSNext & Principal AI Architect. Explore more MCP tools in the MCP Server Directory and agent workflows in the workflows directory.
Last tested & verified: September 2026 with Python 3.12, FastMCP 4.0, DuckDB 1.0.
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
CEO, SaaSNext
Deepak Bagada is the CEO of SaaSNext and founder of Daily AI World. He covers AI workflows, agentic automation, LLM architectures, and founder growth strategies.
Build a peerd Browser-Based Agent Harness Workflow: In-Browser AI Agents with LangGraph [2026]
Next Story →Build a Golf Scanner MCP Server: Discover & Audit Every MCP Server on Your Machine [2026]
Related Intelligence Analysis
Vercel AI SDK Tool Calling React: 5 Steps (2026)
Vercel AI SDK tool calling React integration is a programming pattern that executes server-side functions based on large language model decisions and streams the results to a React frontend. By combining streamText with...
Fact-Density vs. Word Count: The New SEO for 2026
Fact Density is the ratio of verifiable, unique information to the total word count of a piece of content. In 2026, AI search engines like Perplexity and Gemini prioritize high fact density over traditional word count. A...
NVIDIA Audex vs Qwen3.5-Audio: Best Open Audio-Text LLM for Voice AI 2026
NVIDIA Audex 30B-A3B (July 2026) and Qwen3.5-35B-A3B are the two leading open audio-text LLMs. Audex uniquely handles both audio understanding and generation in a single model while preserving text intelligence. Qwen3.5-...