跳到主要内容

MCP 调试指南

开发 MCP 服务器时,有效的调试技巧和工具能够帮助你快速定位问题。本章介绍 MCP 开发中常用的调试方法和工具。

MCP Inspector

MCP Inspector 是官方提供的调试工具,可以独立运行,用于测试和调试 MCP 服务器。

安装和使用

使用 npx 直接运行 Inspector:

npx @modelcontextprotocol/inspector python server.py

或者指定完整的命令:

npx @modelcontextprotocol/inspector uv --directory /path/to/project run server.py

Inspector 功能

Inspector 提供以下功能:

  • 连接测试:验证服务器能否正常启动和连接
  • 工具测试:查看可用工具列表并测试调用
  • 资源浏览:查看和读取服务器提供的资源
  • 提示模板:测试提示模板的参数和输出
  • 消息日志:查看所有 JSON-RPC 消息的详细内容

使用场景

当服务器无法正常工作时,首先使用 Inspector 进行基本测试:

  1. 确认服务器可以正常启动
  2. 验证初始化流程是否正确
  3. 测试各个工具是否按预期工作
  4. 检查资源读取是否正常

日志处理

STDIO 服务器的日志

STDIO 传输的服务器必须将日志输出到 stderr,因为 stdout 用于传输 MCP 消息。

Python 示例

import sys
import logging

# 配置日志输出到 stderr
logging.basicConfig(
stream=sys.stderr,
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)

logger = logging.getLogger("mcp-server")

@app.call_tool()
async def call_tool(name: str, arguments: dict | None):
logger.debug(f"Received tool call: {name} with args: {arguments}")

try:
result = await process_tool(name, arguments)
logger.info(f"Tool {name} executed successfully")
return result
except Exception as e:
logger.error(f"Tool {name} failed: {e}", exc_info=True)
raise

TypeScript 示例

// 使用 console.error 输出日志
console.error("Server starting...");

// 或使用专门的日志库
import pino from 'pino';
const logger = pino({ level: 'debug' });

server.tool("example", async (args) => {
logger.debug({ args }, "Tool called");
// ...
});

常见日志问题

问题:服务器无响应或崩溃

检查日志输出:

# 直接运行服务器查看错误
python server.py

# 或使用 Claude Desktop 的日志
# macOS: ~/Library/Logs/Claude/mcp*.log
# Windows: %APPDATA%\Claude\Logs\mcp*.log

问题:stdout 污染导致协议错误

确保没有代码写入 stdout:

# ❌ 错误 - 会破坏 MCP 协议
print("Debug message")

# ✅ 正确
print("Debug message", file=sys.stderr)
logging.info("Debug message")

错误处理

协议错误

MCP 定义了一组标准错误码:

错误码名称说明
-32700Parse errorJSON 解析错误
-32600Invalid Request无效的请求对象
-32601Method not found方法不存在
-32602Invalid params无效的参数
-32603Internal error服务器内部错误

在 Python SDK 中抛出协议错误:

from mcp.types import McpError

# 工具不存在
raise McpError(
code=-32601,
message=f"Tool '{name}' not found"
)

# 参数无效
raise McpError(
code=-32602,
message=f"Missing required parameter: {param}"
)

工具执行错误

工具执行中的错误应该返回带有 isError: true 的结果:

@app.call_tool()
async def call_tool(name: str, arguments: dict | None):
try:
result = await execute_tool(name, arguments)
return [TextContent(type="text", text=result)]
except ValueError as e:
# 业务逻辑错误
return [TextContent(
type="text",
text=f"错误: {str(e)}"
)]
except Exception as e:
# 意外错误
logger.exception("Unexpected error in tool execution")
return [TextContent(
type="text",
text=f"执行失败: {str(e)}"
)]

测试策略

单元测试

为 MCP 服务器的核心逻辑编写单元测试:

import pytest
from unittest.mock import AsyncMock, patch

@pytest.mark.asyncio
async def test_create_note_tool():
"""测试创建笔记工具"""
# 模拟工具调用
arguments = {
"title": "测试笔记",
"content": "这是测试内容"
}

result = await handle_create_note(arguments)

# 验证返回格式
assert len(result) == 1
assert result[0].type == "text"
assert "创建成功" in result[0].text

@pytest.mark.asyncio
async def test_search_notes_empty():
"""测试搜索空结果"""
with patch('server.list_all_notes', return_value=[]):
result = await handle_search_notes({"query": "不存在的内容"})
assert "未找到" in result[0].text

集成测试

使用 MCP 客户端进行集成测试:

import pytest
from mcp import ClientSession
from mcp.client.stdio import stdio_client

@pytest.mark.asyncio
async def test_server_integration():
"""集成测试:测试完整的服务器流程"""
server_params = StdioServerParameters(
command="python",
args=["server.py"]
)

async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
# 初始化
await session.initialize()

# 测试工具列表
tools = await session.list_tools()
assert len(tools.tools) > 0

# 测试工具调用
result = await session.call_tool(
"create_note",
{"title": "测试", "content": "内容"}
)
assert result.isError is False

测试运行

# 运行所有测试
pytest tests/

# 运行特定测试文件
pytest tests/test_tools.py

# 显示详细输出
pytest tests/ -v -s

常见问题排查

服务器无法启动

症状:Claude Desktop 显示服务器连接失败

排查步骤

  1. 检查 Python 环境:
which python  # 确认使用正确的 Python
python --version
  1. 检查依赖安装:
pip list | grep mcp
  1. 手动运行服务器:
python server.py
# 观察是否有错误输出
  1. 检查配置文件路径:
// 确保使用绝对路径
{
"mcpServers": {
"my-server": {
"command": "/full/path/to/python",
"args": ["/full/path/to/server.py"]
}
}
}

工具调用失败

症状:工具调用返回错误或无响应

排查步骤

  1. 使用 Inspector 测试工具
  2. 检查参数格式是否符合 inputSchema
  3. 检查服务器日志中的错误信息
  4. 验证工具逻辑是否正确
# 添加详细日志
@app.call_tool()
async def call_tool(name: str, arguments: dict | None):
logger.debug(f"Tool call: name={name}, args={arguments}")

try:
result = await process(name, arguments)
logger.debug(f"Tool result: {result}")
return result
except Exception as e:
logger.error(f"Tool error: {e}", exc_info=True)
raise

资源读取问题

症状:资源列表为空或读取失败

排查步骤

  1. 检查 list_resources 是否正确返回
  2. 验证 URI 格式是否正确
  3. 检查 read_resource 中的 URI 解析逻辑
@app.read_resource()
async def read_resource(uri: str) -> list[TextContent]:
logger.debug(f"Reading resource: {uri}")

# 添加详细的 URI 解析日志
if not uri.startswith("notes://"):
logger.warning(f"Invalid URI scheme: {uri}")
raise ValueError(f"Invalid URI: {uri}")

note_id = uri[8:] # 移除前缀
logger.debug(f"Extracted note_id: {note_id}")

# ...

性能问题

症状:工具调用响应缓慢

排查步骤

  1. 识别耗时操作:
import time

@app.call_tool()
async def call_tool(name: str, arguments: dict | None):
start = time.time()
result = await process(name, arguments)
elapsed = time.time() - start

if elapsed > 1.0:
logger.warning(f"Slow tool call: {name} took {elapsed:.2f}s")

return result
  1. 优化 I/O 操作:
  • 使用异步 I/O
  • 添加缓存
  • 批量处理请求
  1. 检查网络请求:
# 设置合理的超时
async with httpx.AsyncClient(timeout=30.0) as client:
response = await client.get(url)

调试技巧

使用断点调试

在 VS Code 中配置调试:

// .vscode/launch.json
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug MCP Server",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/server.py",
"console": "internalConsole",
"logToFile": true
}
]
}

消息追踪

记录所有 MCP 消息:

import sys
import json

class MessageLogger:
def __init__(self, original_stream, log_file):
self.original = original_stream
self.log_file = log_file

def write(self, data):
# 记录消息
with open(self.log_file, 'a') as f:
f.write(f"[{time.time()}] {data}\n")
# 原始写入
return self.original.write(data)

def flush(self):
return self.original.flush()

# 在服务器启动前替换 stdout
sys.stdout = MessageLogger(sys.stdout, "mcp_messages.log")

环境变量调试

使用环境变量控制调试输出:

import os

DEBUG = os.getenv("MCP_DEBUG", "false").lower() == "true"

if DEBUG:
logging.basicConfig(
stream=sys.stderr,
level=logging.DEBUG
)

运行时启用调试:

{
"mcpServers": {
"my-server": {
"command": "python",
"args": ["server.py"],
"env": {
"MCP_DEBUG": "true"
}
}
}
}

下一步