在 Python 中构建多功能 MCP 服务器
Source: Dev.to
(请提供需要翻译的正文内容,我将为您翻译成简体中文并保持原有的格式。)
聊天机器人孤立时代的终结
长期以来,大型语言模型(LLM)一直生活在封闭的花园中——出色的推理引擎被限制在聊天界面后,无法访问本地文件、数据库,或在你的机器上执行代码。模型上下文协议(MCP) 完全改变了这一范式。它提供了一种标准化方式,将 AI 助手连接到实际工作发生的系统。
然而,许多开发者停留在 MCP 的 “Hello World” 阶段:仅暴露一个简单的 API 工具。虽然有用,但这仅仅触及了协议潜力的表面。一个真正健壮、易用的服务器不仅提供工具;它通过 资源(Resources) 提供 上下文,并通过 提示(Prompts) 提供结构化的工作流。
在本指南中,我们将从零构建一个完整的基于 Python 的 MCP 服务器。我们将超越简单脚本,打造一个 多工具架构,包括:
- 数学运算能力
- 文档访问
- 用于会议分析的动态提示模板
我们还将采用现代的 vibe‑coding 工作流——使用 LLM 来构建 LLM 工具——并介绍使用 MCP Inspector 进行调试的关键且常被忽视的技巧。
在打开终端之前:我真的需要构建它吗?
高级工程不仅是写代码,更是要知道何时 不写。如果你只需要集成标准服务(例如 Google Drive、Slack),通常没有必要开发一个可能已经在社区生态中存在的服务器。冗余是效率的敌人。
对于简单的自动化,低代码解决方案(如 n8n)可能提供更快速的价值路径。然而,当以下情况出现时,构建自定义的 Python MCP 服务器就变得不可谈判:
| 原因 | 适用情形 |
|---|---|
| 复杂性 | 你需要特定的、复杂的逻辑(自定义计算、数据转换),而标准 API 无法满足。 |
| 上下文 | 你需要将本地、专有的文档(资源)注入模型的上下文窗口。 |
| 标准化 | 你想为团队强制执行特定的交互模式(提示),例如统一的“会议摘要”结构。 |
如果你的使用场景符合这些条件,是时候开始编码了。
“Vibe Coding” – 利用 LLM 辅助开发
手动编写每一行样板代码的时代已经过去。现在 不是 1999 年。为了高效构建此服务器,我们将采用 vibe‑coding 方法论——利用像 Cursor 这样集成 Claude 的 IDE,根据高质量文档生成脚手架。
先决条件
| 工具 | 版本 / 备注 |
|---|---|
| Python | 3.12 或更高 |
| Package manager | uv(快速、可靠的依赖管理) |
| SDK | mcp(Python SDK) |
| Framework | FastMCP(简化服务器创建的高级包装器) |
为 AI 辅助设定上下文
-
索引文档 – 创建一个
llms.txt(或类似文件),其中包含:- 核心 MCP 规范
- Python SDK README
- FastMCP 使用指南
-
将上下文传递给 Cursor – 在 IDE 中索引这些文件,使模型能够了解您所使用的确切 SDK 版本。
这种准备工作可以让您向模型提出高层次的架构请求(例如,“创建一个带有计算器工具的服务器”),而不是与语法错误纠缠。
Source: …
工具:我们服务器的基石
工具是 LLM 可以调用以执行操作的函数。我们将构建一个 计算器服务器,但其结构能够处理各种逻辑运算。
实现策略
FastMCP 让定义工具看似非常简单。关键在于 描述(docstring),因为它们会成为模型读取的 API 文档,用以决定何时调用工具。
from mcp.server.fastmcp import FastMCP
import math
# Initialize the server
mcp = FastMCP("calculator-server")
@mcp.tool()
def add(a: float, b: float) -> float:
"""Add two numbers together."""
return a + b
@mcp.tool()
def divide(a: float, b: float) -> float:
"""Divide the first number by the second number. Includes zero checks."""
if b == 0:
return "Error: Cannot divide by zero"
return a / b
关键洞察 – docstring("""Add two numbers together.""")不是给你看的,而是给 LLM 看的。如果描述含糊,模型可能会产生幻觉式的功能或在需要时未能调用工具。
你可以将此模式扩展到包含减法、乘法、幂运算、平方根、百分比计算等。只需使用 @mcp.tool() 包装每个函数,我们就会自动处理协议所需的 JSON‑RPC 通信。
使用 MCP Inspector 进行调试
在没有调试工具的情况下编写 MCP 服务器就像盲目飞行。MCP Inspector 让你能够在隔离环境中测试服务器, 将后端逻辑与前端客户端(Claude)解耦。
运行 Inspector
uv run mcp-inspector server.py
这会启动一个本地网页界面(通常是 http://localhost:),你可以在其中:
- 列出工具 – 验证服务器是否真的暴露了你编写的函数。
- 测试执行 – 手动输入参数(例如
a=10, b=2),查看原始输出或错误追踪。 - 检查连接 – 验证传输协议(
stdio与HTTP)。
安全提示 – Inspector 启动时可能会生成带有安全令牌的 URL。如果你尝试通过没有该令牌的普通 localhost URL 进行连接,连接将被拒绝。务必使用终端日志中提供的具体链接,以确保已认证的访问。
使用 Inspector 严格测试边界情况——比如除以零——在 将服务器连接到真实客户端之前。
资源:为模型提供只读访问
工具让模型 执行。资源让模型 读取。
一个常见的错误是把 LLM 当作静态知识库。通过集成资源,你可以让模型直接、只读地访问机器上的特定数据——日志、API 文档或代码库。
实现基于文件的资源
from mcp.server.fastmcp import FastMCP
from pathlib import Path
# Initialize the server (if not already done)
mcp = FastMCP("calculator-server")
# Register a file resource
@mcp.resource()
def documentation() -> str:
"""Read the local documentation file."""
doc_path = Path("docs/mcp_specification.md")
return doc_path.read_text(encoding="utf-8")
现在,LLM 可以请求 documentation() 来获取你的 MCP 规范的完整文本,使其能够利用最新的、特定领域的信息进行推理。
综合示例
下面是一个 最小、可运行的示例,它将工具、资源和检查器组合在一起:
# server.py
from mcp.server.fastmcp import FastMCP
from pathlib import Path
mcp = FastMCP("calculator-server")
# ---- Tools ----
@mcp.tool()
def add(a: float, b: float) -> float:
"""Add two numbers together."""
return a + b
@mcp.tool()
def divide(a: float, b: float) -> float:
"""Divide the first number by the second number. Returns an error string on division by zero."""
if b == 0:
return "Error: Cannot divide by zero"
return a / b
# ---- Resources ----
@mcp.resource()
def documentation() -> str:
"""Read the local MCP documentation file."""
return Path("docs/mcp_specification.md").read_text(encoding="utf-8")
# Run the server (this line is optional; the inspector can start it)
if __name__ == "__main__":
mcp.run()
启动调试:
uv run mcp-inspector server.py
在检查器 UI 中,你可以:
- 列出
add、divide和documentation端点。 - 调用
add(a=5, b=7)→ 返回12。 - 调用
divide(a=10, b=0)→ 返回"Error: Cannot divide by zero"。 - 调用
documentation()→ 返回你的规范文件的原始 Markdown 内容。
下一步
- 添加更多工具(例如
multiply、sqrt、percentage)。 - 创建更丰富的资源(例如 JSON 配置文件、CSV 数据集)。
- 设计提示,以协调多个工具调用(例如 “总结会议记录并计算总时长”)。
- 在向外部客户端暴露之前,确保服务器安全(TLS、身份验证令牌)。
有了 工具、资源 和 提示 的坚实基础,您的 MCP 服务器将成为 LLM 推理与现实系统之间的强大桥梁。祝编码愉快!
MCP 资源示例
import Context, Resource
# Define the path to your knowledge base
resource_path = "./docs/typescript_sdk.md"
@mcp.resource("mcp://docs/typescript-sdk")
def get_typescript_sdk() -> str:
"""Provides access to the TypeScript SDK documentation."""
with open(resource_path, "r") as f:
return f.read()
当您添加此代码时,MCP Inspector 将显示一个新的 Resources 选项卡。 在真实场景中(例如 Claude Desktop),用户现在可以 附加 此资源到聊天。 模型会立即获得该文件的上下文,而无需您将成千上万的文字复制‑粘贴到提示窗口中。
Strategic Value:
这使您的服务器成为一个动态库。更新本地文件后,模型的知识会立即更新。
Source: …
提示层
工具是被动的(模型决定何时使用它们)。
提示是主动的——它们是标准化的、由用户发起的模板,旨在强制执行特定的工作流。
一个完美的用例是 “会议摘要”。 与其每次都输入冗长的指令,不如将该结构写进服务器。
创建动态提示模板
{{date}}, {{transcript}}) as our template.
模板 (prompt.md)
You are an executive assistant. Analyze the following meeting transcript.
**Date:** {{date}}
**Title:** {{title}}
**Transcript:**
{{transcript}}
Please provide a summary with:
1. Overview
2. Key Decisions
3. Action Items
实现方式
@mcp.prompt()
def meeting_summary(date: str, title: str, transcript: str) -> str:
"""Generates a meeting summary based on a transcript."""
# Logic to read the template and replace placeholders
# Returns the formatted prompt to the client
return render_template(
"prompt.md",
date=date,
title=title,
transcript=transcript,
)
使用提示进行调试的过程
list_prompts和get_prompt功能现在由现代 FastMCP 框架自动处理,消除了冗余和冲突。- LLM 生成的代码有时会把
model或temperature等参数注入到提示逻辑中。- 洞察: 配置(使用哪个模型、温度等)属于客户端设置,而不是 提示模板。清理代码即可去除这些幻觉。
在 Inspector(以及最终的 Claude Desktop)中测试时,这一功能会创建一个 表单式 UI:用户选择“会议摘要”,填写字段,LLM 随即收到一个完美构造的上下文包。
Source: …
将服务器桥接到客户端
一旦服务器拥有 Tools、Resources 和 Prompts,就必须通过 claude_desktop_config.json 文件将其连接到客户端。
传输协议:stdio 与 HTTP
stdio(标准输入/输出)
客户端启动服务器进程,并通过终端流与其通信。这种方式安全、快速,非常适合本地开发。
{
"mcpServers": {
"my-python-server": {
"command": "uv",
"args": ["run", "server.py"],
"env": {
"PYTHONPATH": "."
}
}
}
}
HTTP(SSE – 服务器发送事件)
随着规模的扩大,你可能希望通过网络暴露服务器。使用 HTTP 上的 SSE 可以将服务器的生命周期与客户端解耦,允许多个客户端连接到同一个持久的服务器实例。
关于演进的说明: 该协议现在区分了传输类型。虽然 SSE 曾是一个独立的概念,但现代实现通常使用 可流式的 HTTP 端点(例如使用 Starlette 或 FastAPI)。客户端连接到
http://localhost:8000/sse。
回顾:构建 MCP 服务器
从 AI 使用者转变为 AI 工作流架构师涉及三个核心层面:
| 层 | 它为模型提供的内容 | 示例 |
|---|---|---|
| 工具 | 用于执行计算的“手” | @mcp.tool 函数 |
| 资源 | 用于阅读本地文档的“眼睛” | @mcp.resource 函数 |
| 提示 | 标准操作流程的剧本 | @mcp.prompt 模板 |
不要被“氛围编码”蒙蔽。 严谨之处在于:
- 清晰描述你的工具
- 深思熟虑地构建你的提示
- 验证你的资源
建议的工作流程:
- 从
stdio开始,以保持简洁。 - 严格使用 Inspector 来验证逻辑。
- 在一切稳定后 将其整合到你的日常工作流中。
代码是容易的部分。真正的工程在于定义上下文。