MCP Monitor
An observability tool for agentic AI pipelines that intercepts MCP and Python tool calls to provide real-time metrics, session replays, and alerts via a local dashboard. It enables centralized monitoring of multiple MCP servers through multiplexer and proxy modes without requiring changes to existing agent code.
README
MCP Monitor
Transparent observability for agentic AI pipelines.
MCP Monitor intercepts every tool call made by an AI agent ā whether the agent uses the Model Context Protocol (MCP) or calls Python functions directly ā and surfaces metrics, session replays, and alerts through a local web dashboard.
Zero changes to your agent. Zero changes to your MCP servers.

Features
- ā” Live Feed ā Real-time SSE-powered stream of all tool calls with status badges and latency
- š Session Replay ā Browse sessions, view call timelines with a Gantt chart, expand any call to inspect arguments and responses
- š Tool Analytics ā P50/P95/P99 latency charts, call volume, and error rate trends via Chart.js
- š„ļø Server Health ā Per-server status cards (healthy / degraded / down) with auto-refresh
- š Alerts ā Configurable P95 latency and error rate thresholds with cooldown-based alerting
- š Secret Sanitization ā Automatically redacts tokens, passwords, API keys from stored arguments
- š Python SDK ā Zero-dependency pip package to monitor any Python agent (QwenAgent, LangChain, custom)
- š¾ SQLite Storage ā Single-file database with WAL mode for fast concurrent reads
Architecture
Agent (Claude, Cursor, etc.)
ā
āāā Multiplexer mode
ā mcp-monitor serve
ā āāā spawns Server A āāā
ā āāā spawns Server B āāā¤āā POST /api/ingest āāāŗ Dashboard Server āāāŗ SQLite
ā āāā spawns Server C āāā ā
ā EventBus.emit()
āāā Per-server proxy mode ā
ā mcp-monitor proxy SSE push to
ā āāā spawns Server āāāāāāāŗ Dashboard UI
ā
āāā Python SDK āāāŗ POST /api/ingest
Multiplexer mode is the one approach: add one entry to your MCP config and monitor all servers. The serve command spawns every configured server, merges their tools, routes calls, and records everything.
Per-server proxy mode wraps a single server ā useful when you want fine-grained control over which servers are monitored.
Quick Start
Prerequisites
- Node.js 18+
- npm
Install via GitHub Packages
- Authenticate to GitHub Packages: You need a Personal Access Token (classic) with the
read:packagesscope. - Tell npm where to find the package:
echo "@partha-sust16:registry=https://npm.pkg.github.com" >> ~/.npmrc - Install the package globally:
npm install -g @partha-sust16/mcp-monitor
You are now ready to run mcp-monitor start!
Install from Source
git clone https://github.com/Partha-SUST16/mcp_monitor.git
cd mcp_monitor
# Install backend dependencies
npm install
# Build the backend and the dashboard UI automatically
npm run build
# Link globally to use the 'mcp-monitor' command anywhere
npm link
# Start the dashboard server
mcp-monitor start
The dashboard will be available at http://localhost:4242.
Send a Test Event
curl -X POST http://localhost:4242/api/ingest \
-H 'Content-Type: application/json' \
-d '{
"sessionId": "test-session",
"agentType": "python-sdk",
"serverName": "my-server",
"toolName": "read_file",
"method": "read_file",
"arguments": {"path": "/tmp/test.txt"},
"response": null,
"status": "success",
"latencyMs": 150,
"timestamp": "2026-03-09T10:00:00Z"
}'
Connecting Agents
Multiplexer Mode
Monitor all MCP servers with a single config entry. No need to wrap each server individually.
Step 1. List your servers in mcp-monitor.config.json:
{
"servers": [
{ "name": "filesystem", "transport": "stdio", "command": "npx @modelcontextprotocol/server-filesystem /tmp" },
{ "name": "github", "transport": "stdio", "command": "npx @modelcontextprotocol/server-github", "env": { "GITHUB_TOKEN": "$GITHUB_TOKEN" } }
],
"dashboard": { "port": 4242 }
}
Step 2. Replace all MCP server entries in your agent config with one:
{
"mcpServers": {
"mcp-monitor": {
"command": "mcp-monitor",
"args": ["serve", "-c", "/absolute/path/to/mcp-monitor.config.json"]
}
}
}
Step 3. Start the dashboard server separately:
mcp-monitor start
The agent sees one MCP server with all tools combined. MCP Monitor spawns each real server internally, routes every tools/call to the correct child, and records the call.
Note on Tool Names: To prevent naming collisions between different MCP servers that happen to expose identical tools, the Multiplexer prefixes all tool names with their originating server's name. For example, if your
filesystemserver has a tool namedread_file, the LLM will see it exposed asfilesystem_read_file.
Per-Server Proxy Mode
Alternatively, wrap individual servers by replacing their command:
{
"mcpServers": {
"filesystem": {
"command": "mcp-monitor",
"args": ["proxy", "--name", "filesystem",
"--cmd", "npx @modelcontextprotocol/server-filesystem /tmp"]
}
}
}
Python Agent (QwenAgent)
from agent_monitor import patch_qwen_agent
patch_qwen_agent(server_name="my-agent") # call once before creating agent
# rest of agent code unchanged
Generic Python Tool
from agent_monitor import monitor
@monitor(server_name="my-tools")
def query_database(sql: str) -> dict:
...
Python SDK Installation
cd sdk/python
pip install -e .
The SDK has zero external dependencies ā it uses only Python stdlib (urllib, threading, json).
Configuration
Create mcp-monitor.config.json in the project root:
{
"servers": [
{
"name": "filesystem",
"transport": "stdio",
"command": "npx @modelcontextprotocol/server-filesystem /tmp"
},
{
"name": "github",
"transport": "stdio",
"command": "npx @modelcontextprotocol/server-github",
"env": { "GITHUB_TOKEN": "$GITHUB_TOKEN" }
},
{
"name": "remote-tools",
"transport": "http",
"targetUrl": "https://my-mcp-server.com",
"listenPort": 4243
}
],
"dashboard": {
"port": 4242
},
"alerts": {
"latencyP95Ms": 2000,
"errorRatePercent": 10,
"cooldownMinutes": 5
}
}
Environment variable substitution is supported in env fields ā $VAR_NAME is replaced with process.env.VAR_NAME.
CLI Commands
# Start dashboard server + alert engine
mcp-monitor start [-c path/to/config.json]
# Run as a multiplexing MCP server (add as single entry in agent config)
mcp-monitor serve [-c path/to/config.json] [--dashboard-url http://localhost:4242]
# Start a single MCP proxy (wrap one server)
mcp-monitor proxy --name filesystem --cmd "npx @modelcontextprotocol/server-filesystem /tmp"
# List recent sessions
mcp-monitor sessions [--limit 20]
# Replay a session's tool calls
mcp-monitor replay <session-id>
# Show per-tool stats
mcp-monitor stats [--sort latency_p95|error_rate|call_count] [--since 1h|6h|24h|7d]
# Export data
mcp-monitor export [--format json|csv] [--since 24h] [--output file.json]
REST API
| Endpoint | Description |
|---|---|
GET /api/overview |
Aggregated stats: total calls, error rate, avg/p95 latency, recent calls |
GET /api/sessions |
Paginated session list with call counts (?limit=20&offset=0) |
GET /api/sessions/:id/calls |
All tool calls for a session in chronological order |
GET /api/tools/stats |
Per-tool latency percentiles and error rates (?since=24h) |
GET /api/servers |
Server health status derived from last 5 minutes of data |
GET /api/alerts |
Fired alert history (?limit=50&offset=0) |
GET /api/stream |
SSE endpoint ā pushes tool_call and alert events in real time |
POST /api/ingest |
Accepts CollectorEvent JSON (used by Python SDK) |
Project Structure
mcp-monitor/
āāā src/
ā āāā types.ts # All shared TypeScript interfaces
ā āāā config.ts # Config loader with env var substitution
ā āāā cli.ts # Commander.js entry point
ā āāā core/
ā ā āāā Store.ts # SQLite (better-sqlite3) CRUD
ā ā āāā Collector.ts # Sanitize ā truncate ā persist ā emit
ā ā āāā RemoteCollector.ts # HTTP POST to dashboard /api/ingest
ā ā āāā SessionManager.ts # Session lifecycle + idle timeout
ā ā āāā EventBus.ts # Node.js EventEmitter singleton
ā ā āāā AlertEngine.ts # P95 latency & error rate monitoring
ā āāā ingestion/
ā ā āāā mcp/
ā ā ā āāā MuxServer.ts # Multiplexing MCP server (aggregates all servers)
ā ā ā āāā ProtocolInterceptor.ts # JSON-RPC request/response matching
ā ā ā āāā StdioProxy.ts # MCP stdio transport proxy
ā ā ā āāā HttpProxy.ts # MCP HTTP reverse proxy
ā ā āāā IngestEndpoint.ts # POST /api/ingest handler
ā āāā dashboard/
ā āāā server.ts # Express + SSE + static serving
ā āāā routes/ # API route handlers
ā āāā ui/ # React + Vite dashboard
ā āāā src/pages/
ā āāā LiveFeed.tsx
ā āāā SessionReplay.tsx
ā āāā ToolAnalytics.tsx
ā āāā ServerHealth.tsx
ā āāā Alerts.tsx
āāā sdk/python/
ā āāā pyproject.toml
ā āāā agent_monitor/
ā āāā __init__.py
ā āāā collector.py # Fire-and-forget POST to /api/ingest
ā āāā decorators.py # patch_qwen_agent() + @monitor
āāā mcp-monitor.config.json
āāā package.json
āāā tsconfig.json
Tech Stack
| Layer | Technology |
|---|---|
| MCP Proxy | TypeScript (child_process, JSON-RPC parsing) |
| Core | TypeScript + Express 5 |
| Database | SQLite via better-sqlite3 (WAL mode) |
| Dashboard UI | React 19 + Vite + Chart.js |
| Real-time Push | Server-Sent Events (SSE) |
| Python SDK | Python 3.9+ (stdlib only) |
| CLI | Commander.js |
Session Management
Sessions are created and managed automatically:
- MCP connections: A new session starts on every
initializeJSON-RPC message - Idle timeout: If 5+ minutes pass between tool calls, a new session is created
- Explicit session ID: Set
MCP_MONITOR_SESSION_IDenv var for deterministic session grouping - Python SDK: Each Python process gets a unique UUID session, or set
AGENT_MONITOR_SESSION_ID - Session end: Marked when the proxied process exits or the connection closes
Alert System
The AlertEngine is fully event-driven ā no polling. It listens to every tool_call event from the EventBus and evaluates thresholds in real time:
- P95 Latency per tool ā fires if above
latencyP95Msthreshold - Error Rate per tool ā fires if above
errorRatePercentthreshold (requires ā„5 calls)
Cooldown logic prevents the same alert from re-firing within cooldownMinutes (default: 5 min). Alerts are persisted to SQLite and pushed to the dashboard via SSE.
License
MIT
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.