MCP 实战:从零搭建 Model Context Protocol Server,让 AI 连接你的工具
·
TL;DR:MCP(Model Context Protocol)是 Anthropic 推出的开放协议,让 AI 模型能标准化地连接外部工具和数据源。本文从协议原理讲起,手把手带你用 Python 搭建一个 MCP Server,并接入 Claude / Cursor 使用。
1. 为什么需要 MCP
过去让 AI 调用工具,每个应用都要自己实现一遍集成:
- 想让 Claude 读数据库 → 自己写一套工具调用逻辑
- 想让 Cursor 访问文件系统 → 再写一套
- 想让自研 Agent 调 API → 又写一套
结果就是 N 个模型 × M 个工具 = N×M 个集成,每个都是定制化代码。
MCP 的出现解决了这个问题:
MCP 架构
┌──────────────┐ MCP 协议 ┌──────────────┐
│ AI 应用 │ ←────────────→ │ MCP Server │
│ (Claude/Cursor│ (stdio/HTTP) │ (你的工具) │
│ /Agent) │ │ - 数据库 │
└──────────────┘ │ - 文件系统 │
│ - API │
└──────────────┘
模型只需要支持 MCP 协议,就能连接任意 MCP Server。一次开发,处处可用。
2. MCP 的核心概念
| 概念 | 说明 |
|---|---|
| Host | AI 应用本身(Claude Desktop、Cursor、自研 Agent) |
| Client | Host 内部的 MCP 客户端,负责和 Server 通信 |
| Server | 提供能力的服务(工具、资源、提示词模板) |
| Tool | 可调用的函数(如查询数据库、发送邮件) |
| Resource | 可读取的数据(如文件、API 响应) |
| Prompt | 预定义的提示词模板 |
3. 搭建第一个 MCP Server
3.1 环境准备
bash
pip install mcp
pip install uvicorn # 如果用 HTTP 模式
# 验证安装
python -c "import mcp; print('MCP installed')"
3.2 用 FastMCP 写一个简单 Server
Python - mcp_server.py
from mcp.server.fastmcp import FastMCP
import sqlite3
import os
# 创建 MCP Server
mcp = FastMCP("company-db-server")
DB_PATH = "./company.db"
def get_db_connection():
conn = sqlite3.connect(DB_PATH)
conn.row_factory = sqlite3.Row
return conn
# ========== Tool 1: 查询员工 ==========
def query_employee(name: str) -> str:
"""根据姓名查询员工信息
Args:
name: 员工姓名(支持模糊匹配)
"""
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute(
"SELECT * FROM employees WHERE name LIKE ?",
(f"%{name}%",)
)
rows = cursor.fetchall()
conn.close()
if not rows:
return f"未找到匹配 '{name}' 的员工"
result = []
for row in rows:
result.append(
f"姓名: {row['name']}, 部门: {row['department']}, "
f"职位: {row['title']}, 入职: {row['hire_date']}"
)
return "\n".join(result)
# ========== Tool 2: 统计部门人数 ==========
def count_department(department: str) -> str:
"""统计某个部门的员工人数
Args:
department: 部门名称
"""
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute(
"SELECT COUNT(*) as count FROM employees WHERE department = ?",
(department,)
)
count = cursor.fetchone()["count"]
conn.close()
return f"部门 '{department}' 共有 {count} 名员工"
# ========== Resource: 数据库 schema ==========
"schema://employees")
def get_schema() -> str:
"""返回 employees 表的字段定义"""
return """employees 表结构:
- id: 主键
- name: 姓名
- department: 部门
- title: 职位
- salary: 薪资
- hire_date: 入职日期"""
# 启动 Server(stdio 模式)
if __name__ == "__main__":
mcp.run()
3.3 在 Claude Desktop 中配置
claude_desktop_config.json
{
"mcpServers": {
"company-db": {
"command": "python",
"args": ["/path/to/mcp_server.py"]
}
}
}
重启 Claude Desktop 后,它会自动发现你的 MCP Server,你就能直接对话:
你:「公司技术部有多少人?」
Claude:(自动调用count_department("技术部"))→ 「技术部共有 15 名员工」
4. 进阶:带鉴权的 MCP Server
生产环境需要安全控制。给 MCP Server 加上 API Key 鉴权:
Python - 带鉴权的 MCP Server
from mcp.server.fastmcp import FastMCP
from mcp.server import Server
from mcp.types import Tool, TextContent
import os
EXPECTED_TOKEN = os.getenv("MCP_TOKEN", "default-secret")
# 自定义 Server,拦截请求做鉴权
server = Server("secure-db-server")
@server.list_tools()
async def list_tools() -> list[Tool]:
return [
Tool(
name="query_employee",
description="查询员工信息",
inputSchema={
"type": "object",
"properties": {
"name": {"type": "string"}
}
}
)
]
@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
# 这里简化:真实场景从请求头读取 token
if name == "query_employee":
result = query_employee(arguments["name"])
return [TextContent(type="text", text=result)]
return [TextContent(type="text", text="Unknown tool")]
# 启动
if __name__ == "__main__":
import mcp.server.stdio
async def main():
async with mcp.server.stdio.stdio_server() as (read, write):
await server.run(read, write, server.create_initialization_options())
asyncio.run(main())
5. 实战:构建一个 GitHub MCP Server
让 AI 能查 Issue、建 PR、读代码:
Python - github_mcp_server.py
from mcp.server.fastmcp import FastMCP
import requests
import os
mcp = FastMCP("github-server")
GITHUB_TOKEN = os.getenv("GITHUB_TOKEN")
BASE_URL = "https://api.github.com"
HEADERS = {
"Authorization": f"token {GITHUB_TOKEN}",
"Accept": "application/vnd.github.v3+json"
}
def list_open_issues(repo: str, limit: int = 10) -> str:
"""列出仓库的开放 Issue
Args:
repo: 格式为 "owner/repo"
limit: 返回数量限制
"""
response = requests.get(
f"{BASE_URL}/repos/{repo}/issues",
headers=HEADERS,
params={"state": "open", "per_page": limit}
)
issues = response.json()
result = []
for issue in issues[:limit]:
result.append(f"#{issue['number']} {issue['title']} ({issue['html_url']})")
return "\n".join(result) if result else "没有开放 Issue"
def create_issue(repo: str, title: str, body: str = "") -> str:
"""创建新的 GitHub Issue
Args:
repo: 格式为 "owner/repo"
title: Issue 标题
body: Issue 内容
"""
response = requests.post(
f"{BASE_URL}/repos/{repo}/issues",
headers=HEADERS,
json={"title": title, "body": body}
)
if response.status_code == 201:
data = response.json()
return f"✅ 已创建 Issue #{data['number']}: {data['html_url']}"
return f"❌ 创建失败: {response.status_code} {response.text}"
if __name__ == "__main__":
mcp.run()
6. MCP vs Function Calling
| 维度 | MCP | Function Calling |
|---|---|---|
| 定位 | 标准化协议(连接方式) | 模型能力(调用方式) |
| 复用性 | 一次开发,多模型使用 | 每个模型各自实现 |
| 生态 | 跨应用共享 Server | 绑定单一应用 |
| 复杂度 | 需要 Server 进程 | 直接传函数定义 |
| 适用 | 工具生态建设 | 简单工具调用 |
⚠️ 注意:MCP 和 Function Calling 不是对立的。MCP Server 底层也是用 Function Calling 实现工具调用,只是封装了一层标准化协议,让工具可以跨模型、跨应用复用。
7. 总结
MCP 解决了 AI 工具集成的「N×M 问题」:
- 过去:每个 AI 应用自己写工具集成,重复劳动
- 现在:写一次 MCP Server,所有支持 MCP 的 AI 应用都能用
适合用 MCP 的场景:
- 你有一套内部工具/API,想让多个 AI 应用都能调用
- 你做 Agent 平台,想让用户自己贡献工具
- 你需要统一的安全/鉴权层管理工具访问
入门建议:先用 FastMCP 写一个简单 Server(如查询数据库),接入 Claude Desktop 体验。熟悉后再做鉴权、HTTP 模式、多工具编排。
如果对你有帮助,欢迎在评论区聊聊你用 MCP 踩过的坑。
更多推荐


所有评论(0)