Build an Agent Observability MCP Server for Production Diagnostics
Ship a TypeScript FastMCP server that ingests OpenTelemetry OTLP spans and gives Claude or Cursor agent-run tracing, loop detection, token-cost dashboards, and budget gates.
Deepak Bagada
CEO, SaaSNext
- Production agent fleets fail in ways a log file cannot show — traces, token spend, and loop counts are the real signals.
- A TypeScript FastMCP server turns OpenTelemetry OTLP ingestion into queryable MCP tools any agent can call.
- Loop detection and budget-gate tools let Claude or Cursor remediate stuck agents before the bill runs away.
- OAuth 2.1 with PKCE or scoped API keys keeps observability data secure while agents self-diagnose.
By Deepak Bagada, CEO at SaaSNext & Principal AI Architect.
Your agents are in production. They draft code, run support queues, triage incidents, and talk to customers — and for the first time in the software era, the thing failing is not your stack but your software's behavior over time. A bug reproduces; an agent loops. A crash has a stack trace; a confused agent has a thousand tool calls and a mounting token bill. In August 2026, dedicated agent observability platforms like Cekura — with a $30/month tier aimed squarely at this problem — went mainstream because teams realized the old monitoring stack cannot see the new failure modes.
This guide builds a production-ready agent observability MCP server in TypeScript with FastMCP. It exposes tools for agent-run tracing, token and cost dashboards, loop detection, and budget gates — so Claude Desktop, Cursor, or any MCP client can inspect an agent fleet and remediate it in real time. The server ingests OpenTelemetry OTLP spans, stores them, and answers the diagnostic questions agents ask of themselves. You can compare it against other diagnostics tooling in the MCP Directory, wire it into agent workflow templates, and follow latest AI news for the monitoring market as it evolves.
Why agents need observability, not just monitoring
Classic observability answers "what broke." Agent observability answers "what is it doing right now, and where is it drifting?" A stuck agent looks healthy in your CPU graphs: it is active, retrying, calling tools in a loop, generating tokens. The signals that matter are semantic — repetition, token velocity, cost trajectory, escalation rate. Cekura's $30/month tier and similar products made these signals cheap and mainstream in 2026, and the MCP angle goes one step further: instead of opening a dashboard, the agent itself can query its own state and act. The observability MCP server makes that self-inspection possible from inside the loop, without granting the agent broader infrastructure access than it needs.
What Cekura-style platforms changed
The August 2026 wave of agent-observability platforms made two changes. First, pricing: a $30/month tier gave small teams dashboard-grade visibility without an enterprise contract, normalizing the category. Second, semantics: these tools track loops, token velocity, tool-call diversity, and escalation rates — not just latency and errors. The insight is that an agent's health is a behavioral property, not a resource property. The MCP server we build here captures the same semantics but puts them inside the agent's own tool loop, so diagnosis is one call away rather than one login away.
Server architecture
The server sits between MCP clients and the telemetry pipeline:
- Ingestion — accepts OpenTelemetry OTLP traces over HTTP.
- Storage — keeps spans, events, and token counters queryable.
- Tools — trace queries, usage aggregation, loop detection, budget gates.
- Actions — pause agents, raise alerts, and escalate to humans.
The design deliberately keeps remediation actions explicit: the server can pause a runaway agent, but a human must resume it. Everything is a query or an action; nothing hides behind a proprietary format, which keeps the surface small and auditable.
Ingesting OTLP spans
Any agent runtime that exports OpenTelemetry — LangChain, CrewAI, custom orchestrators, and increasingly native MCP clients — can send traces to the OTLP endpoint. Each run becomes a trace; each tool call becomes a span with attributes for model, tokens, cost, and outcome. We also enrich spans with an agent_id and project attribute at the edge so later queries can group by fleet or team.
OTLP payload walkthrough
A minimal OTLP export is a batch of spans. Each span carries a trace ID, parent ID, operation name, timestamps, and attributes. For agent observability we add three attributes at the edge: agent_id, model, and tokens.total (or tokens.prompt and tokens.completion). The server reads these on ingestion and upserts counters into a time-series store. Because OTLP is an open standard, any runtime — LangChain callbacks, CrewAI pipeline hooks, or a hand-rolled wrapper around your model call — can emit spans without vendor lock-in. That is why OTLP ingestion, not a bespoke SDK, is the backbone of this server.
Building the server
Install the dependencies:
npm install fastmcp zod
Then create observability-server.ts:
// observability-server.ts
import { FastMCP } from "fastmcp";
import { z } from "zod";
const OTEL_URL = "https://otel.production.example.com/v1/traces";
const API_KEY = process.env.OBSERVABILITY_API_KEY;
const server = new FastMCP({
name: "agent-observability",
version: "1.0.0",
});
async function queryTracer(path: string, params?: { [key: string]: any }) {
const res = await fetch(`${OTEL_URL}${path}`, {
headers: { Authorization: `Bearer ${API_KEY}` },
});
if (!res.ok) throw new Error(`telemetry error: ${res.status}`);
return res.json();
}
server.addTool({
name: "list_runs",
description: "List recent agent runs filtered by agent, status, or window.",
inputSchema: z.object({
agent_id: z.string().optional(),
status: z.enum(["running", "completed", "failed", "looping"]).optional(),
since: z.string().datetime().optional(),
limit: z.number().int().min(1).max(100).default(20),
}),
execute: async (args) => queryTracer("/runs", { ...args }),
});
server.addTool({
name: "get_trace",
description: "Return the full span tree for a single agent run.",
inputSchema: z.object({
trace_id: z.string(),
include_payloads: z.boolean().default(false),
}),
execute: async (args) =>
queryTracer(`/traces/${args.trace_id}`, { payloads: args.include_payloads }),
});
server.addTool({
name: "detect_loops",
description: "Find repeated tool-call cycles indicating a stuck agent.",
inputSchema: z.object({
since: z.string().datetime().optional(),
min_cycles: z.number().int().min(2).max(50).default(3),
}),
execute: async (args) => queryTracer("/anomalies/loops", args),
});
server.addTool({
name: "usage_dashboard",
description: "Aggregate token and cost by agent, model, or tool.",
inputSchema: z.object({
group_by: z.enum(["agent", "model", "tool"]).default("agent"),
window_hours: z.number().int().min(1).max(720).default(24),
}),
execute: async (args) => queryTracer("/usage", args),
});
server.addTool({
name: "set_budget_alert",
description: "Create or update a daily spend budget alert.",
inputSchema: z.object({
project: z.string().optional(),
agent_id: z.string().optional(),
daily_limit_usd: z.number().positive(),
notify_channel: z.string().default("slack"),
}),
execute: async (args) => queryTracer("/budgets", args),
});
server.addTool({
name: "pause_agent",
description: "Halt a looping or runaway agent until a human resumes it.",
inputSchema: z.object({
agent_id: z.string(),
reason: z.string(),
}),
execute: async (args) => {
await queryTracer("/agents/pause", args);
return { status: "paused", agent_id: args.agent_id, reason: args.reason };
},
});
server.start();
The z.object schemas ARE the inputSchema — FastMCP converts them to JSON Schema and advertises them in tools/list, so clients validate arguments before a call is dispatched. Here is the contract registered for detect_loops:
{
"name": "detect_loops",
"inputSchema": {
"type": "object",
"properties": {
"since": {"type": "string", "format": "date-time"},
"min_cycles": {"type": "integer", "minimum": 2, "maximum": 50}
}
}
}
Tool walkthrough
list_runs and get_trace give an agent full visibility: which runs are live, which failed, and where every token went. detect_loops scans spans for repeated tool-call signatures — the classic pathology where an agent retries the same failing call, escalating cost while making no progress. usage_dashboard aggregates tokens and dollars by agent, model, or tool so a cost question is answered in one call. set_budget_alert creates daily spend limits with notification routing. pause_agent is the remediation tool: it stops a runaway run and records the reason for the post-mortem.
Anomaly detection beyond loops
Loop detection is the flagship tool, but the same span corpus supports subtler signals. Sudden drops in tool-call diversity can indicate a model settling into repetitive behavior. Token velocity spikes often precede runaway spend. Escalation-rate changes flag degraded reasoning. The server keeps these as queries rather than hard-coded rules, so agents and operators can tune thresholds per project — a research project tolerates different behavior than a customer-facing one.
Connecting clients with mcpServers
Add the server to Claude Desktop's claude_desktop_config.json or Cursor's .cursor/mcp.json:
{
"mcpServers": {
"agent-observability": {
"command": "npx",
"args": ["tsx", "/opt/observability/server.ts"],
"env": {
"OBSERVABILITY_API_KEY": "${OBSERVABILITY_API_KEY}"
}
}
}
}
Keep the key in your shell environment — never in the config file.
Security: OAuth 2.1 and API-key + sandbox
Telemetry is sensitive — it contains prompts, customer data, and internal tool payloads. The server ships with three protections:
- API-key transport. A scoped key authenticates the server to the telemetry backend; never embed it in client config.
- OAuth 2.1. For multi-team fleets, front the server with an OAuth 2.1 gateway (PKCE, short-lived tokens) so every MCP client has its own identity and audit trail.
- Read-only sandbox. Default to query-only tools (
list_runs,get_trace,usage_dashboard); grantpause_agentandset_budget_alertonly to operator roles.
Apply the same discipline you use for every tool in the MCP Directory: least privilege, short-lived credentials, and full audit logging. Because these tools can stop an agent, log every pause_agent and set_budget_alert call with the calling client's identity, so a "who paused it and why" question is answerable in one query.
Loop detection and budget gates in practice
Two incidents make the case. In the first, a coding agent retried a failing test suite 214 times in 40 minutes, burning tokens in a cycle that detect_loops flagged after three identical signatures. In the second, a support agent drifted from triage into open-ended research; usage_dashboard showed spend accelerating, and set_budget_alert tripped the $50 daily gate, pausing the run before the monthly invoice arrived. Neither incident needed a human at a terminal at 3 a.m. — the agent fleet diagnosed itself and left a precise handoff. That self-remediation loop is the pattern teams are standardizing on, and it composes well with workflow templates built for unattended operations.
Tracing your own orchestrator
If your runtime does not export OTLP, add a lightweight span wrapper. Set a global tracer, attach agent_id and project attributes on every tool call, and emit tokens.total when the model response arrives. Ten lines of plumbing makes any fleet visible to the observability server — and to the agents that query it. Keep the wrapper thin and synchronous; if the telemetry endpoint is down, the agent should degrade gracefully rather than fail the run.
Manual dashboards vs. agent observability MCP
| Concern | Manual dashboard | Agent observability MCP |
|---|---|---|
| Who checks | On-call human opens a UI | Agent self-diagnoses |
| Loop detection | Regex on logs, late | Semantic span analysis, early |
| Budget enforcement | Reactive, after the bill | Proactive gate, before spend |
| Remediation | Human clicks pause | Agent calls pause_agent |
| Access model | Broad VPN access | Scoped OAuth 2.1/API keys |
Production checklist
Apply these five rules before the fleet grows past a handful of agents:
- Enrich every span with
agent_id,project, andmodelat the edge. - Store OTLP traces with a retention window; budget alerts need history, post-mortems need detail.
- Test loop detection against a deliberately stuck agent before it matters.
- Start every deployment in read-only sandbox; grant remediation tools per role.
- Send budget alerts to the channel your on-call actually reads.
Agent monitoring in 2026 is a self-service diagnostic loop, not a passive screen. Cekura's $30/month tier made the economics accessible; MCP makes the capability usable — your agents can now see themselves, catch their own loops, and stop their own spend. Build the server, start in sandbox, and let the fleet report for duty.
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 an Evo 2 Genomics MCP Server for Agentic Scientific Discovery
Next Story →LangChain Deep Agents v0.7: Cutting Agent Input Tokens by 65%
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-...