####################################
Tutorial 6: MCP Server
####################################
.. include:: ../links.ref
.. include:: ../tags.ref
.. include:: ../abbrs.ref
============ ==========================
**Abstract** Model Context Protocol 扩展 AI 能力
**Authors** Walter Fan
**Status** WIP
**Updated** |date|
============ ==========================
.. contents::
:local:
什么是 MCP
==========
MCP(Model Context Protocol)是 Anthropic 推出的开放协议,用于连接 AI 模型与外部数据源和工具。
.. code-block:: text
MCP 架构
┌─────────────────────────────────────────────────────────────────┐
│ AI 应用 (Cursor) │
│ │ │
│ MCP Client │
│ │ │
└──────────────────────────────┼──────────────────────────────────┘
│
┌─────────────┼─────────────┐
│ │ │
┌─────▼─────┐ ┌─────▼─────┐ ┌─────▼─────┐
│ MCP Server│ │ MCP Server│ │ MCP Server│
│ (文件系统) │ │ (数据库) │ │ (API) │
└───────────┘ └───────────┘ └───────────┘
MCP 的核心概念
--------------
1. **Resources(资源)**: 数据源,如文件、数据库记录
2. **Tools(工具)**: AI 可以调用的函数
3. **Prompts(提示)**: 预定义的提示模板
4. **Sampling(采样)**: 让服务器请求 AI 补全
为什么需要 MCP
--------------
- **扩展能力**: 让 AI 访问外部系统
- **标准化**: 统一的协议,一次开发到处使用
- **安全**: 受控的访问方式
- **灵活**: 可以连接任何数据源
Cursor 中使用 MCP
=================
配置 MCP Server
---------------
在 Cursor 中配置 MCP Server:
1. 打开 Settings
2. 找到 MCP 配置部分
3. 添加 Server 配置
配置文件位置:``~/.cursor/mcp.json``
.. code-block:: json
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/allowed/dir"]
},
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "your-token"
}
}
}
}
常用 MCP Server
---------------
.. list-table::
:header-rows: 1
:widths: 25 35 40
* - Server
- 功能
- 使用场景
* - filesystem
- 文件系统访问
- 读写项目外的文件
* - github
- GitHub API
- 管理仓库、Issues、PR
* - postgres
- PostgreSQL 数据库
- 查询和修改数据库
* - sqlite
- SQLite 数据库
- 本地数据库操作
* - brave-search
- 网络搜索
- 搜索最新信息
* - memory
- 持久化记忆
- 跨会话保存信息
安装和使用 MCP Server
=====================
Filesystem Server
-----------------
让 AI 访问指定目录的文件:
**安装**:
.. code-block:: bash
npm install -g @modelcontextprotocol/server-filesystem
**配置**:
.. code-block:: json
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/Users/yourname/Documents",
"/Users/yourname/Projects"
]
}
}
}
**使用**:
在 Cursor Chat 中::
@mcp 读取 /Users/yourname/Documents/notes.txt 的内容
GitHub Server
-------------
管理 GitHub 仓库:
**配置**:
.. code-block:: json
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_xxxxxxxxxxxx"
}
}
}
}
**使用**:
::
@mcp 列出我的 GitHub 仓库
@mcp 创建一个新的 Issue:
仓库:myrepo
标题:Bug: 登录失败
内容:用户反馈登录时出现 500 错误
PostgreSQL Server
-----------------
连接 PostgreSQL 数据库:
**配置**:
.. code-block:: json
{
"mcpServers": {
"postgres": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres"],
"env": {
"POSTGRES_CONNECTION_STRING": "postgresql://user:pass@localhost:5432/mydb"
}
}
}
}
**使用**:
::
@mcp 查询 users 表中最近注册的 10 个用户
@mcp 统计每个月的订单数量
Memory Server
-------------
跨会话保存信息:
**配置**:
.. code-block:: json
{
"mcpServers": {
"memory": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-memory"]
}
}
}
**使用**:
::
@mcp 记住:项目使用 Python 3.11 和 FastAPI
@mcp 我们项目使用什么技术栈?
开发自定义 MCP Server
=====================
MCP Server 可以用 Python 或 TypeScript 开发。
Python MCP Server
-----------------
**安装依赖**:
.. code-block:: bash
pip install mcp
**基本结构**:
.. code-block:: python
# my_server.py
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Resource, Tool, TextContent
# 创建服务器
server = Server("my-server")
# 定义资源
@server.list_resources()
async def list_resources():
return [
Resource(
uri="myapp://config",
name="Application Config",
description="Current application configuration"
)
]
@server.read_resource()
async def read_resource(uri: str):
if uri == "myapp://config":
return TextContent(
type="text",
text='{"debug": true, "version": "1.0.0"}'
)
# 定义工具
@server.list_tools()
async def list_tools():
return [
Tool(
name="get_weather",
description="Get weather for a city",
inputSchema={
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "City name"
}
},
"required": ["city"]
}
)
]
@server.call_tool()
async def call_tool(name: str, arguments: dict):
if name == "get_weather":
city = arguments["city"]
# 实际实现中调用天气 API
return TextContent(
type="text",
text=f"Weather in {city}: Sunny, 25°C"
)
# 运行服务器
async def main():
async with stdio_server() as (read_stream, write_stream):
await server.run(read_stream, write_stream)
if __name__ == "__main__":
import asyncio
asyncio.run(main())
**配置使用**:
.. code-block:: json
{
"mcpServers": {
"my-server": {
"command": "python",
"args": ["/path/to/my_server.py"]
}
}
}
TypeScript MCP Server
---------------------
**安装依赖**:
.. code-block:: bash
npm init -y
npm install @modelcontextprotocol/sdk
**基本结构**:
.. code-block:: typescript
// src/index.ts
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
const server = new Server(
{ name: "my-server", version: "1.0.0" },
{ capabilities: { tools: {}, resources: {} } }
);
// 定义工具
server.setRequestHandler("tools/list", async () => ({
tools: [
{
name: "calculate",
description: "Perform a calculation",
inputSchema: {
type: "object",
properties: {
expression: { type: "string" }
},
required: ["expression"]
}
}
]
}));
server.setRequestHandler("tools/call", async (request) => {
if (request.params.name === "calculate") {
const expr = request.params.arguments.expression;
const result = eval(expr); // 注意:实际使用中要安全处理
return { content: [{ type: "text", text: String(result) }] };
}
});
// 启动服务器
const transport = new StdioServerTransport();
server.connect(transport);
实用 MCP Server 示例
====================
项目文档 Server
---------------
让 AI 访问项目文档:
.. code-block:: python
# docs_server.py
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Resource, TextContent
import os
from pathlib import Path
server = Server("docs-server")
DOCS_DIR = Path("/path/to/project/docs")
@server.list_resources()
async def list_resources():
resources = []
for doc_file in DOCS_DIR.rglob("*.md"):
rel_path = doc_file.relative_to(DOCS_DIR)
resources.append(
Resource(
uri=f"docs://{rel_path}",
name=str(rel_path),
description=f"Documentation: {rel_path}"
)
)
return resources
@server.read_resource()
async def read_resource(uri: str):
if uri.startswith("docs://"):
path = uri.replace("docs://", "")
file_path = DOCS_DIR / path
if file_path.exists():
content = file_path.read_text()
return TextContent(type="text", text=content)
return TextContent(type="text", text="Resource not found")
async def main():
async with stdio_server() as (read_stream, write_stream):
await server.run(read_stream, write_stream)
if __name__ == "__main__":
import asyncio
asyncio.run(main())
API 测试 Server
---------------
让 AI 测试 API:
.. code-block:: python
# api_tester_server.py
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent
import httpx
import json
server = Server("api-tester")
@server.list_tools()
async def list_tools():
return [
Tool(
name="http_request",
description="Make an HTTP request",
inputSchema={
"type": "object",
"properties": {
"method": {
"type": "string",
"enum": ["GET", "POST", "PUT", "DELETE"]
},
"url": {"type": "string"},
"headers": {"type": "object"},
"body": {"type": "object"}
},
"required": ["method", "url"]
}
)
]
@server.call_tool()
async def call_tool(name: str, arguments: dict):
if name == "http_request":
async with httpx.AsyncClient() as client:
response = await client.request(
method=arguments["method"],
url=arguments["url"],
headers=arguments.get("headers", {}),
json=arguments.get("body")
)
result = {
"status_code": response.status_code,
"headers": dict(response.headers),
"body": response.text
}
return TextContent(
type="text",
text=json.dumps(result, indent=2)
)
async def main():
async with stdio_server() as (read_stream, write_stream):
await server.run(read_stream, write_stream)
if __name__ == "__main__":
import asyncio
asyncio.run(main())
MCP 最佳实践
============
1. 安全考虑
-----------
- 限制文件系统访问范围
- 使用环境变量存储敏感信息
- 验证工具输入
- 记录操作日志
2. 性能优化
-----------
- 缓存频繁访问的资源
- 使用连接池
- 异步处理 I/O 操作
3. 错误处理
-----------
.. code-block:: python
@server.call_tool()
async def call_tool(name: str, arguments: dict):
try:
# 工具逻辑
pass
except ValueError as e:
return TextContent(
type="text",
text=f"Invalid input: {e}"
)
except Exception as e:
return TextContent(
type="text",
text=f"Error: {e}"
)
4. 文档和描述
-------------
为工具和资源提供清晰的描述:
.. code-block:: python
Tool(
name="search_logs",
description="""
Search application logs.
Examples:
- Search for errors: {"query": "ERROR", "limit": 100}
- Search by time: {"query": "timeout", "start": "2024-01-01"}
""",
inputSchema={...}
)
调试 MCP Server
===============
1. 查看日志
-----------
.. code-block:: python
import logging
logging.basicConfig(level=logging.DEBUG)
2. 测试工具
-----------
使用 MCP Inspector:
.. code-block:: bash
npx @modelcontextprotocol/inspector python my_server.py
3. 在 Cursor 中调试
-------------------
- 检查 MCP 配置是否正确
- 查看 Cursor 的开发者工具
- 检查 Server 进程是否运行
小结
====
本教程介绍了 MCP Server:
- **MCP 概念**: 连接 AI 与外部系统的协议
- **常用 Server**: filesystem、github、postgres
- **自定义开发**: Python 和 TypeScript 实现
- **最佳实践**: 安全、性能、错误处理
关键要点:
1. MCP 扩展了 AI 的能力边界
2. 可以连接任何数据源和服务
3. 开发自定义 Server 很简单
下一步
------
在下一个教程中,我们将学习 Spec-Driven Development(规格驱动开发)。
练习
====
1. 配置 filesystem MCP Server
2. 配置 GitHub MCP Server 并管理 Issues
3. 开发一个简单的自定义 MCP Server
4. 将 MCP Server 集成到你的工作流程中
参考资源
========
- `MCP 官方文档 `_
- `MCP GitHub 仓库 `_
- `MCP Server 列表 `_
- `Cursor MCP 配置 `_