Skip to main content
Workflows Library MCP Directory Realtime AI News Sponsor Tier Subscribe
Front Page / AI Tools / Deep Dive

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

Deepak Bagada

CEO, SaaSNext

Sep 09, 2026 Published
|
Sep 09, 2026 Updated
|
7 Minutes Reading Time
Core Takeaways for Founders & Builders
  • 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

  1. MCP analytics server provides end-to-end agent observability — every tool call tracked with timing, outcome, and model version metadata.
  2. DuckDB-powered time-series analytics handle millions of tool call records with sub-second aggregate query performance.
  3. 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.

Executive Briefing

Enjoyed this breakdown? Get our morning dispatch in your inbox.

Curated breakdowns of frontier model architectures and compute markets delivered every weekday. Zero fluff.

🎉 Thank You for Subscribing!

Frequently Asked Questions
Each tool call recording adds approximately 5ms overhead. For production deployments with latency-sensitive agents, the server supports batch mode where records are buffered for 10 calls or 2 seconds before flushing. Batch mode reduces overhead to sub-1ms per call at the cost of slightly delayed metrics availability.
Yes — the DuckDB database file can be queried directly by any DuckDB-compatible tool. The server also supports CSV and Parquet export via MCP tools. For real-time dashboards, the analytics server exposes a Prometheus metrics endpoint that feeds into Grafana dashboards.
Pass the prompt template identifier as the prompt_template parameter when calling record_tool_call. The analytics server stores this field and exposes a cohort analysis tool that groups sessions by template, comparing success rates, latency, and tool usage patterns across templates. This enables A/B testing of prompt engineering strategies.
DuckDB persists to disk by default. All recorded tool calls and session metrics survive server restarts. The database file is stored at agent_analytics.duckdb in the server directory and can be backed up, archived, or relocated as needed. For high-availability deployments, configure a shared DuckDB file on a network filesystem.
Deepak Bagada
Author Profile

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.

Related Intelligence Analysis

Briefing AI Tools

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...

Deepak Bagada Deepak Bagada
12m read
Breaking AI Tools

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...

Deepak Bagada Deepak Bagada
4m read
Audio Briefing
Accessibility Preferences
High Contrast Mode
Accessible Reading Font

Keyboard Shortcuts

Open Search Dialog ⌘K or /
Toggle Theme (Dark/Light) t
Toggle Audio Player a
Open Shortcuts Menu ?
Close Active Dialog Esc