使用说明
MCP监视器
代理AI管道的透明可观察性。
MCP Monitor拦截AI代理发出的每个工具调用——无论代理是否使用 模型上下文协议(MCP) 或者直接调用Python函数,并通过本地web仪表板显示指标、会话回放、令牌和成本估算以及错误分类。
您的代理没有任何更改。MCP服务器无任何更改。
为什么存在
调用工具的代理是一个穿着风衣的分布式系统。模型决定调用什么,工具决定是否回答,你通常看到的唯一一件事就是最终的响应。当出现问题时——它很慢,很贵,它循环,它失败了——有趣的证据已经在某个地方的日志中滚动过去了。
大多数“LLM可观察性”工具都要求您包装模型客户端或采用跟踪SDK。MCP Monitor采取了相反的赌注: 工具边界是观察代理最便宜的地方。 代理采取的每一个有意义的行动最终都会成为线上的工具调用。如果你坐在边界处——MCP传输——你可以在不接触其代码的情况下观察整个代理。
本README的其余部分是围绕以下内容组织的 指标 MCP监视器记录,对于每个记录, 为什么值得记录 --不仅仅是它是什么。
特性
- ⚡ 直播 --所有工具调用的实时SSE驱动流,带有状态徽章和延迟
- 📋 会话回放+差异 --浏览会话、查看通话时间线,以及 区分任意两个电话 并排观察到底发生了什么变化
- 📊 工具分析 --通过Chart.js查看P50/P95/P99延迟图、呼叫量和错误率趋势
- 💰 代币和成本 --预计代币使用和支出,细分 每个代理节点 (服务器+工具)
- 🚨 错误分类 --每一次失败都像 幻觉, 刀具故障,或 超时
- 🖥️ 服务器运行状况 --具有自动刷新功能的每台服务器状态卡(正常/降级/停机)
- 🔔 警报 --可配置的P95延迟和错误率阈值,带有基于冷却的警报
- 🔒 秘密消毒 -自动编辑存储参数中的令牌、密码和API密钥
- 🐍 开发包 --零依赖pip包,用于监控任何Python代理(QwenAgent、LangChain、自定义)
- 💾 SQLite存储 --具有WAL模式的单文件数据库,可实现快速并发读取
指标及其重要性
潜伏期百分位数(P50/P95/P99)
什么: 对于每种工具,挂钟持续时间的中位数、第95百分位和第99百分位。
为什么不是平均值? 平均值与尾部行为有关。一个调用工具100次的代理感觉和它的速度一样慢 最慢的 调用,因为模型会阻塞每个调用。P95/P99是用户可见疼痛的地方;平均值隐藏了它。我们对原始延迟进行排序并对其进行索引,而不是计算一个连续的平均值,正是这样,一些缓慢的调用就不能被平均掉。
令牌使用量——估计值和每 节点
什么: 对于我们估计的每个工具调用 input_tokens (模型发出的参数)和 output_tokens (模型必须读回的响应),然后按 节点 一 (server, tool) 配对——以及每种代理类型。
为什么要估计呢? MCP是一种工具协议,而不是LLM协议。线路上的字节是JSON-RPC,以及 其中没有任何内容具有真正的LLM令牌计数。 但这些字节不是任意的:参数是 确切地 模型生成了什么,以及响应是什么 确切地 模型在下一轮将摄取什么。因此,有效载荷大小是令牌压力的忠实代理。我们使用 tokens ≈ ceil(bytes / charsPerToken) 随着 charsPerToken 违约 4 --英语文本的长期经验法则。这是一个 估计,故意:它不需要任何成本,不增加依赖性,并且足够一致,可以相互比较节点并观察随时间推移的趋势。
为什么是每个节点? 因为“我的代理人使用了很多代币”是不可操作的,但“ github/search_issues 节点负责我70%的上下文预算”。代理中的令牌成本几乎从来都不是统一的——一个返回巨大JSON blob的聊天工具可以主导其他一切。将令牌归因于生成它们的特定工具是将数字转化为修复的关键(对该工具进行分页、修剪其响应、缓存)。
微妙的是——截断前的大小。 响应被截断为10KB 存储,但令牌估计使用 原始的
sizeBytes,在截断之前捕获。截断是显示器/磁盘问题;模型仍然消耗了全部有效载荷,因此会计必须反映全部有效载荷。
成本——预算,而非账单
什么: 每个呼叫的估计令牌乘以可配置的价格,按节点、会话和时间聚合。
为什么? 代币是你衡量的单位,但美元是你衡量自己的单位 决定 与。成本将“这个工具很健谈”重新定义为“这个工具每天花费40美元”,这是优先修复的句子。定价是 inputPerMillion / outputPerMillion (每100万代币美元),并附带合理的违约($3 / $15),与 每台服务器覆盖 --因为不同的MCP服务器通常位于不同的支持模型之前,一个统一的费率会错误地分配支出。将美元数字视为 定向预算,而不是发票:它继承了令牌估计的模糊性。它的价值在于 相对的 比较和 趋势,而不是将提供者的声明与美分进行匹配。
错误分类——需要三种不同修复的三种故障
什么: 每个不成功的呼叫都会被转换为以下之一:
| 课堂 | 这意味着什么 | 谁有错 | 解决办法 |
|---|---|---|---|
| 幻觉 | 该模型发明了一个不存在的工具,或者编造了模式拒绝的参数(JSON-RPC -32601 / -32602,或“未知工具”消息) |
提示 | 拧紧工具说明/少量射击 |
| 工具故障 | 该工具存在且调用正确,但内部出错(500、文件丢失、上游停机) | 后备服务 | 固定或硬化工具 |
| 超时 | 截止日期内未收到回复 | 容量/延迟 | 缩放、缓存或添加预算 |
为什么要费心把它们分开? 因为原始的“错误率”是一个你无法操作的数字。12%的错误率就是这样 幻觉 这是一个提示问题——你的模型正在触及不存在的东西,无论多少基础设施工作都无济于事。12%的错误率,仅此而已 工具故障 这是一个完全不同建筑中的运营问题。12%的比率 超时 这是一场能力对话。相同的标题编号,三个无关的随叫随到响应。将它们分解为一个指标会丢弃关于失败的最有用的信息: 这是什么样的失败。 分类是在摄取时从JSON-RPC错误代码(如果可用)中导出的,并回落到不携带代码的源(如Python SDK)的消息启发式。
会话重播和差异——因为代理是不确定的
什么: 每个会话都可以作为有序的通话时间线进行回放;您可以展开任何调用以检查其完整的参数和响应,以及 选择任意两个调用diff 他们一行一行。还有一个点击“与之前调用同一工具的差异”
为什么是diff? 综合指标告诉你 那 行为改变;差异告诉你 什么 改变。代理是不确定的——同一个工具,用“相同”的意图调用两次,可以接收微妙不同的参数并返回微妙不同的结果。当一个工具开始出现错误时,最快的理解方法通常是将一个好调用和一个坏调用放在一起,并读取增量。模型是否开始传递格式错误的参数?工具的响应形状是否漂移?一条线差使五分钟的调查变成五秒的调查。这是调试器的“比较两种状态”本能,应用于代理跟踪。
建筑
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
每条摄取路径都会聚在 单一富集点 在仪表板流程中: collector.handle()在那里,论点被净化,回应被截断, 估计令牌、计算成本和分类错误 --一次,集中,因此多路复用器、每服务器代理和Python SDK都得到了相同的处理,而没有每路径代码。
多路转换方式 推荐的方法是:在MCP配置中添加一个条目并监视所有服务器。这 serve 命令生成每个配置的服务器,合并它们的工具,路由调用,并记录所有内容。
每服务器代理模式 封装单个服务器——当您希望对监视的服务器进行细粒度控制时非常有用。
快速开始
先决条件
- Node.js 18+
- npm
通过GitHub软件包安装
- 对GitHub包进行身份验证:您需要一个个人访问令牌(经典)
read:packages范围。 - 告诉npm在哪里可以找到包:
echo "@partha-sust16:registry=https://npm.pkg.github.com" >> ~/.npmrc - 全局安装软件包:
npm install -g @partha-sust16/mcp-monitor
您现在已准备好跑步 mcp-monitor start!
从源代码安装
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
仪表板将在 http://localhost:4242.
发送测试事件
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"
}'
仪表板将自动估算此调用的令牌和成本。要查看实际的错误分类,请发布一个带有 "status": "error", "errorCode": -32601 (归类为幻觉)或 "status": "timeout".
连接代理
多路转换方式
监视器 全部 具有单个配置项的MCP服务器。无需单独包装每台服务器。
步骤1。 在中列出您的服务器 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 }
}
步骤2。 将代理配置中的所有MCP服务器条目替换为一个:
{
"mcpServers": {
"mcp-monitor": {
"command": "mcp-monitor",
"args": ["serve", "-c", "/absolute/path/to/mcp-monitor.config.json"]
}
}
}
步骤3。 单独启动仪表板服务器:
mcp-monitor start
代理看到一个包含所有工具的MCP服务器。MCP Monitor在内部生成每个真实服务器,路由每个 tools/call 并记录通话。
关于工具名称的说明:为了防止不同MCP服务器之间发生命名冲突,这些服务器恰好暴露了相同的工具,多路复用器会在所有工具名称前加上其原始服务器的名称。例如,如果你
filesystem服务器有一个名为的工具read_file,LLM将看到它暴露为filesystem_read_file.
每服务器代理模式
或者,通过替换命令来包装单个服务器:
{
"mcpServers": {
"filesystem": {
"command": "mcp-monitor",
"args": ["proxy", "--name", "filesystem",
"--cmd", "npx @modelcontextprotocol/server-filesystem /tmp"]
}
}
}
Python代理(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
通用Python工具
from agent_monitor import monitor
@monitor(server_name="my-tools")
def query_database(sql: str) -> dict:
...
Python SDK安装
cd sdk/python
pip install -e .
SDK具有 零外部依赖 --它只使用Python stdlib(urllib, threading, json).
配置
创建 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" }
},
{
"name": "remote-tools",
"transport": "http",
"targetUrl": "https://my-mcp-server.com",
"listenPort": 4243
}
],
"dashboard": {
"port": 4242
},
"alerts": {
"latencyP95Ms": 2000,
"errorRatePercent": 10,
"cooldownMinutes": 5
},
"pricing": {
"charsPerToken": 4,
"inputPerMillion": 3.0,
"outputPerMillion": 15.0,
"perServer": {
"github": { "inputPerMillion": 1.0, "outputPerMillion": 5.0 }
}
}
}
中支持环境变量替换 env 字段-- $VAR_NAME 被替换为 process.env.VAR_NAME.
定价块
| 字段 | 含义 |
|---|---|
charsPerToken |
令牌估计值的每个令牌的字节数除数(默认值 4).对于令牌密集的内容(代码,CJK),降低它;对于散文,提高它。 |
inputPerMillion |
每100万美元 输入 (参数)标记 |
outputPerMillion |
每100万美元 输出 (响应)令牌 |
perServer |
可选的每台服务器速率覆盖——当不同的MCP服务器支持不同的型号时使用 |
定价存在于仪表板过程中。 因为每条摄入路径都指向仪表板的单个
collector.handle(),只需在您运行的地方配置定价mcp-monitor start代理/服务进程自动继承丰富。
回填说明。 令牌、成本和错误类列通过幂等迁移自动添加到现有数据库中。记录的行数 之前 您将默认值升级为零令牌/成本和非机密错误状态——在新数据累积之前,历史聚合将被低估。
CLI命令
# 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]
# Show estimated token usage & cost per node and session
mcp-monitor cost [--format json|csv] [--since 1h|6h|24h|7d]
# Export data
mcp-monitor export [--format json|csv] [--since 24h] [--output file.json]
REST API
| 端点 | 描述 |
|---|---|
GET /api/overview |
汇总统计数据:总呼叫数、错误率、平均延迟/p95、总令牌和成本、最近的呼叫数 |
GET /api/sessions |
带有呼叫计数的分页会话列表(?limit=20&offset=0) |
GET /api/sessions/:id/calls |
按时间顺序对会话的所有工具调用(包括令牌、成本、错误类) |
GET /api/tools/stats |
每个工具的延迟百分比和错误率(?since=24h) |
GET /api/tokens/usage |
按节点和代理类型细分的令牌使用情况(?since=24h) |
GET /api/cost/breakdown |
按节点、会话和时间估算的成本(?since=24h) |
GET /api/errors/classification |
按类别、趋势和最近错误分类的错误计数(?since=24h) |
GET /api/servers |
根据最后5分钟的数据得出的服务器运行状况 |
GET /api/alerts |
已触发警报历史记录(?limit=50&offset=0) |
GET /api/stream |
SSE端点--推送 tool_call 和 alert 实时事件 |
POST /api/ingest |
接受 CollectorEvent JSON(Python SDK使用) |
项目结构
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 + aggregations + migration
│ │ ├── Collector.ts # Sanitize → truncate → estimate tokens/cost → classify → persist → emit
│ │ ├── classify.ts # Error classifier (hallucination / tool_failure / timeout)
│ │ ├── 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 (+ error codes)
│ │ │ ├── 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 (overview, sessions, tools, tokens, cost, errors, ...)
│ └── ui/ # React + Vite dashboard
│ └── src/
│ ├── lib/diff.ts # Zero-dep LCS line diff
│ └── pages/
│ ├── LiveFeed.tsx
│ ├── SessionReplay.tsx # + call diffs
│ ├── ToolAnalytics.tsx
│ ├── TokenCost.tsx # tokens & cost per node
│ ├── Errors.tsx # error classification
│ ├── 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
技术栈
| 层 | 技术 |
|---|---|
| MCP代理 | TypeScript(子进程,JSON-RPC解析) |
| 核心 | TypeScript+Express 5 |
| 数据库 | SQLite通过 better-sqlite3 (WAL模式) |
| 仪表板UI | React 19+Vite+Chart.js |
| 实时推送 | 服务器发送事件(SSE) |
| Python SDK | Python 3.9+(仅限stdlib) |
| CLI | Commander.js |
会话管理
会话是自动创建和管理的:
- MCP连接: 每一天都有一个新的会议开始
initializeJSON-RPC消息 - 空闲超时: 如果两次工具调用之间相隔5分钟以上,则会创建一个新会话
- 显式会话ID: 集
MCP_MONITOR_SESSION_IDenv-var用于确定性会话分组 - Python SDK: 每个Python进程都会获得一个唯一的UUID会话或设置
AGENT_MONITOR_SESSION_ID - 会话结束: 标记代理进程退出或连接关闭的时间
警报系统
AlertEngine完全由事件驱动-- 无投票它倾听每一个 tool_call 事件总线中的事件,并实时评估阈值:
- P95延迟 每工具→ 火灾,如果以上
latencyP95Ms阈值 - 错误率 每工具→ 火灾,如果以上
errorRatePercent阈值(需要≥5个电话)
冷却逻辑可防止同一警报在内部重新触发 cooldownMinutes (默认值:5分钟)。警报被持久化到SQLite,并通过SSE推送到仪表板。
许可证
麻省理工学院
