主题
连接本地 MCP 服务器
本指南介绍如何在开发环境中连接和使用本地 MCP 服务器。
概述
本地 MCP 服务器通常通过标准输入/输出 (stdio) 传输方式运行,这是开发和测试阶段最常用的连接方式。
快速开始
1. 安装 MCP 服务器
Python 服务器示例
bash
# 安装 MCP Python SDK
pip install mcp
# 创建简单的服务器
cat > weather_server.py << 'EOF'
import asyncio
from mcp.server import Server
from mcp.server.stdio import stdio_server
app = Server("weather-server")
@app.list_tools()
async def list_tools():
return [
{
"name": "get_weather",
"description": "Get weather information for a location",
"inputSchema": {
"type": "object",
"properties": {
"location": {"type": "string"}
},
"required": ["location"]
}
}
]
@app.call_tool()
async def call_tool(name: str, arguments: dict):
if name == "get_weather":
location = arguments.get("location", "")
return f"The weather in {location} is sunny and 72°F"
raise ValueError(f"Unknown tool: {name}")
async def main():
async with stdio_server() as streams:
await app.run(*streams)
if __name__ == "__main__":
asyncio.run(main())
EOF
TypeScript 服务器示例
bash
# 安装 MCP TypeScript SDK
npm install @modelcontextprotocol/sdk
# 创建服务器文件
cat > weather-server.ts << 'EOF'
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
const server = new Server(
{
name: "weather-server",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
server.setRequestHandler("tools/list", async () => {
return {
tools: [
{
name: "get_weather",
description: "Get weather information for a location",
inputSchema: {
type: "object",
properties: {
location: { type: "string" },
},
required: ["location"],
},
},
],
};
});
server.setRequestHandler("tools/call", async (request) => {
const { name, arguments: args } = request.params;
if (name === "get_weather") {
const location = args.location;
return {
content: [
{
type: "text",
text: `The weather in ${location} is sunny and 72°F`,
},
],
};
}
throw new Error(`Unknown tool: ${name}`);
});
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
}
main().catch(console.error);
EOF
# 编译 TypeScript
npx tsc weather-server.ts
2. 配置客户端连接
Claude Desktop 配置
编辑 ~/Library/Application Support/Claude/claude_desktop_config.json
:
json
{
"mcpServers": {
"weather": {
"command": "python",
"args": ["/path/to/weather_server.py"]
}
}
}
自定义客户端配置
javascript
const client = new MCPClient({
name: "example-client",
version: "1.0.0"
});
await client.connect({
transport: {
type: "stdio",
command: "python",
args: ["/path/to/weather_server.py"]
}
});
开发工作流
1. 服务器开发
项目结构
my-mcp-server/
├── src/
│ ├── server.py # 主服务器文件
│ ├── tools/ # 工具实现
│ │ ├── __init__.py
│ │ ├── weather.py
│ │ └── calculator.py
│ └── resources/ # 资源提供
│ ├── __init__.py
│ └── files.py
├── tests/ # 测试文件
├── requirements.txt # 依赖声明
└── README.md # 文档
实现工具
python
# tools/weather.py
import requests
from typing import Dict, Any
class WeatherTool:
def __init__(self, api_key: str):
self.api_key = api_key
async def get_weather(self, location: str) -> Dict[str, Any]:
"""获取指定位置的天气信息"""
url = f"https://api.openweathermap.org/data/2.5/weather"
params = {
"q": location,
"appid": self.api_key,
"units": "metric"
}
response = requests.get(url, params=params)
response.raise_for_status()
data = response.json()
return {
"location": data["name"],
"temperature": data["main"]["temp"],
"description": data["weather"][0]["description"],
"humidity": data["main"]["humidity"]
}
实现资源
python
# resources/files.py
import os
from pathlib import Path
from typing import List, Dict, Any
class FileResource:
def __init__(self, base_path: str):
self.base_path = Path(base_path)
async def list_files(self) -> List[Dict[str, Any]]:
"""列出可用的文件资源"""
resources = []
for file_path in self.base_path.rglob("*.txt"):
resources.append({
"uri": f"file://{file_path}",
"name": file_path.name,
"description": f"Text file: {file_path.name}",
"mimeType": "text/plain"
})
return resources
async def read_file(self, uri: str) -> str:
"""读取文件内容"""
file_path = Path(uri.replace("file://", ""))
if not file_path.is_relative_to(self.base_path):
raise ValueError("Access denied: file outside allowed directory")
return file_path.read_text(encoding="utf-8")
2. 测试和调试
使用 MCP Inspector
bash
# 安装 MCP Inspector
npm install -g @modelcontextprotocol/inspector
# 启动 Inspector 连接到本地服务器
mcp-inspector python /path/to/weather_server.py
单元测试
python
# tests/test_weather.py
import pytest
from unittest.mock import patch, Mock
from src.tools.weather import WeatherTool
@pytest.fixture
def weather_tool():
return WeatherTool(api_key="test_key")
@patch('requests.get')
async def test_get_weather(mock_get, weather_tool):
# 模拟 API 响应
mock_response = Mock()
mock_response.json.return_value = {
"name": "San Francisco",
"main": {"temp": 20, "humidity": 65},
"weather": [{"description": "clear sky"}]
}
mock_get.return_value = mock_response
# 测试工具调用
result = await weather_tool.get_weather("San Francisco")
assert result["location"] == "San Francisco"
assert result["temperature"] == 20
assert result["description"] == "clear sky"
集成测试
python
# tests/test_integration.py
import asyncio
import json
from src.server import create_server
async def test_server_integration():
server = create_server()
# 模拟初始化请求
init_request = {
"jsonrpc": "2.0",
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {},
"clientInfo": {"name": "test-client", "version": "1.0.0"}
},
"id": 1
}
response = await server.handle_request(init_request)
assert response["result"]["protocolVersion"] == "2025-06-18"
调试技巧
1. 日志记录
python
import logging
# 配置日志
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('/tmp/mcp_server.log'),
logging.StreamHandler()
]
)
logger = logging.getLogger(__name__)
# 在代码中添加日志
@app.call_tool()
async def call_tool(name: str, arguments: dict):
logger.info(f"Tool called: {name} with args: {arguments}")
try:
result = await execute_tool(name, arguments)
logger.info(f"Tool result: {result}")
return result
except Exception as e:
logger.error(f"Tool execution failed: {e}")
raise
2. 错误处理
python
from mcp.server.exceptions import McpError
@app.call_tool()
async def call_tool(name: str, arguments: dict):
try:
if name == "get_weather":
location = arguments.get("location")
if not location:
raise McpError(
code=-32602,
message="Invalid params",
data={"param": "location", "error": "Location is required"}
)
return await get_weather_data(location)
else:
raise McpError(
code=-32601,
message="Method not found",
data={"method": name}
)
except Exception as e:
logger.exception("Unexpected error in tool execution")
raise McpError(
code=-32603,
message="Internal error",
data={"error": str(e)}
)
3. 性能监控
python
import time
from functools import wraps
def monitor_performance(func):
@wraps(func)
async def wrapper(*args, **kwargs):
start_time = time.time()
try:
result = await func(*args, **kwargs)
duration = time.time() - start_time
logger.info(f"{func.__name__} completed in {duration:.3f}s")
return result
except Exception as e:
duration = time.time() - start_time
logger.error(f"{func.__name__} failed after {duration:.3f}s: {e}")
raise
return wrapper
@monitor_performance
async def get_weather_data(location: str):
# 实现获取天气数据的逻辑
pass
常见问题
1. 连接失败
问题: 客户端无法连接到服务器 解决方案:
- 检查服务器脚本路径是否正确
- 确认 Python/Node.js 环境配置
- 查看服务器日志文件
2. 工具调用失败
问题: 工具调用返回错误 解决方案:
- 验证工具参数格式
- 检查工具实现逻辑
- 添加详细的错误日志
3. 性能问题
问题: 服务器响应缓慢 解决方案:
- 使用异步操作避免阻塞
- 实现结果缓存机制
- 优化数据库查询
最佳实践
1. 代码组织
- 使用模块化设计分离关注点
- 实现清晰的错误处理策略
- 添加全面的测试覆盖
2. 安全考虑
- 验证所有输入参数
- 限制文件系统访问范围
- 使用环境变量管理敏感信息
3. 性能优化
- 实现异步操作
- 使用连接池管理外部资源
- 添加适当的缓存机制
相关资源
- 构建 MCP 服务器 - 服务器开发详细指南
- MCP Inspector - 调试工具使用指南
- 安全最佳实践 - 安全实现指南