实用笔记:什么是MCP?用Python构建自定义MCP服务器
《实用笔记》操作指南:什么是MCP?如何用Python构建自定义MCP服务器:为采用该模式的团队提供的契约、校验规则以及可直接插入的代码模块。
可将此内容作为《什么是MCP?用Python构建自定义MCP服务器》一文中面向操作员的思路重构版本:清晰的阶段划分、有序的代码模块以及便于交接时参考的恢复说明。在“概览”阶段,若能将其视为可量化的基准,效果会更好——在扩大范围之前,先记录一份最佳操作示例、一个故障案例以及回滚说明。除了功能结果外,还需记录执行时间以及令牌或查询成本。提前了解成本情况,可避免在从演示环境过渡到共享环境时出现意外费用。
90秒了解MCP
在90秒MCP阶段,应在修改代码之前明确输入参数、该步骤的负责人以及结束标准。操作员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 配置应置于应用程序代码之外。环境文件、密钥存储和功能标志应集中存放于一个位置,这样操作员无需查看整个系统结构即可进行审计。 将客户端构建与消息循环分开,这样即便更换提供方,也无需重写对话状态机。
为何以往的每个AI集成成本都是原来的三倍
在每个人工智能集成阶段,修改代码之前都必须明确输入内容、该步骤的负责人以及结束标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 需同时记录正常流程和异常恢复流程。重试机制、人工审核环节以及错误消息处理都是产品本身的组成部分,而非后续需要补充的功能。 应将客户端构建部分与消息循环分离,这样即便更换服务提供商,也无需重新编写对话状态机。
在单个文件中构建站立会议辅助工具
在“构建站立会议辅助工具”阶段,修改代码之前需明确输入参数、该步骤的负责人以及退出标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 优先选择小型、可测试的单元,而非庞大的脚本。当某个步骤失败时,故障应指向单一责任模块,而非复杂的流程链。 将客户端构建与消息循环分开,这样就可以在不重写对话状态机的情况下更换提供者。 在“构建站立会议辅助工具”阶段,修改代码之前需明确输入参数、该步骤的负责人以及退出标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 在功能结果之外还需记录执行时间以及令牌或查询成本。提前了解成本情况,可避免在从演示环境过渡到共享环境时出现意外费用。
。
pip install fastmcp
# standup_server.py
import subprocess
from typing import TypedDict
from fastmcp import FastMCP
mcp = FastMCP("standup-helper")
class StandupSummary(TypedDict):
branch: str
since: str
commit_count: int
commits: list[str]
@mcp.tool()
def summarize_standup(
branch: str = "main",
since: str = "yesterday",
) -> StandupSummary:
"""Summarize recent git activity for a standup.
Reads the local git log on the given branch since the
given time window. Returns commit count and one-line
subjects for each commit. Used by AI clients via MCP.
"""
try:
result = subprocess.run(
[
"git", "log",
f"--since={since}",
"--pretty=format:%h %s",
branch,
],
capture_output=True,
text=True,
timeout=5,
check=True,
)
except (subprocess.CalledProcessError,
subprocess.TimeoutExpired) as exc:
return {
"branch": branch,
"since": since,
"commit_count": 0,
"commits": [f"git error: {exc}"],
}
lines = [
line for line in result.stdout.splitlines() if line
]
return {
"branch": branch,
"since": since,
"commit_count": len(lines),
"commits": lines,
}
# resources and prompts come next
# standup_server.py (continued)
@mcp.resource("recent_commits://main")
def recent_commits_main() -> str:
"""Last 10 commits on the main branch, plain text.
Resources are pulled by the host opportunistically.
They are not invoked by the model the way tools are.
"""
result = subprocess.run(
[
"git", "log",
"-n", "10",
"--pretty=format:%h %ad %s",
"--date=short",
"main",
],
capture_output=True,
text=True,
timeout=5,
)
return result.stdout or "(no commits found)"
@mcp.prompt("standup_template")
def standup_template(focus: str = "shipping work") -> str:
"""Reusable standup question exposed as a prompt
template. Surfaces as a slash command in clients that
expose prompts (e.g. /standup_template in Claude Code).
"""
return (
f"Summarize what I worked on yesterday, focusing on "
f"{focus}. Use the summarize_standup tool to get the "
f"git log, then write a one-paragraph standup note."
)
if __name__ == "__main__":
mcp.run()
传输与认证
在处理传输与认证阶段时,首先列出相关契约:所需的输入参数、成功信号以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改保持一致性。 将配置信息置于应用程序代码之外。环境文件、密钥存储以及功能开关应集中存放于一个位置,以便操作人员无需查看整个系统结构即可进行审计。 在每次调用时记录请求ID、模型ID以及延迟时间。如果没有这些记录,间歇性的服务提供商错误就会被视为应用程序的故障。
# bottom of standup_server.py
if __name__ == "__main__":
# Default transport is stdio. The host (Claude Code,
# Cursor, Claude Desktop, etc.) launches this script
# as a subprocess and talks to it over stdin/stdout.
# No port, no TLS, no auth. The trust boundary is
# whoever launched the host.
mcp.run()
# To expose the same server over the network instead,
# use Streamable HTTP. SSE was deprecated in the
# March 2025 spec update. Do not use it for new code.
#
# Production HTTP also needs an auth layer in front.
# OAuth 2.1 with Dynamic Client Registration is the
# current pattern. See Week 22 for the full flow.
#
# mcp.run(
# transport="streamable-http",
# host="0.0.0.0",
# port=8000,
# )
本地开发循环
在处理“本地开发循环”阶段时,首先写下相关契约:所需的输入参数、成功信号以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改保持一致性。 同时记录正常流程和异常恢复流程。重试机制、人工审核环节以及死信处理都是产品本身的一部分,而非后续的优化工作。 每次调用时都要记录请求ID、模型ID以及延迟时间。如果没有这些记录,间歇性的服务端错误就会被视为应用程序的缺陷。
npx @modelcontextprotocol/inspector python standup_server.py
同一台服务器,三个客户端
在“同一服务器三个客户端”阶段工作时,首先需明确接口规范:所需输入、成功信号以及部分失败时的处理方式。这样的清单能确保后续的代码修改保持一致性。 优先选择小型、可测试的单元,而非冗长的脚本。当某个步骤出错时,错误应指向单一责任模块,而非复杂的流程链。 每次调用时都要记录请求ID、模型ID以及延迟时间。没有这些记录,间歇性的服务错误就会被视为应用程序的缺陷。 在“同一服务器三个客户端”阶段工作时,首先需明确接口规范:所需输入、成功信号以及部分失败时的处理方式。这样的清单能确保后续的代码修改保持一致性。 在功能结果旁还需记录执行时间以及令牌或查询成本。提前了解成本情况,可避免在从演示环境过渡到共享环境时出现意外费用。
{
"mcpServers": {
"standup-helper": {
"command": "python",
"args": ["/Users/you/code/standup_server.py"]
}
}
}
{
"mcpServers": {
"standup-helper": {
"command": "python",
"args": ["/Users/you/code/standup_server.py"]
}
}
}
{
"mcpServers": {
"standup-helper": {
"command": "python",
"args": ["/Users/you/code/standup_server.py"]
}
}
}
不宜使用MCP的场景
“不宜使用”阶段若被视为可度量的指标会更为有效。在扩大应用范围之前,先记录一份最佳实践案例、一个失败案例以及回滚说明。 应将配置置于应用程序代码之外。环境文件、密钥存储和功能开关应集中存放,以便操作人员无需查看整个系统结构即可进行审计。 在讲解循环逻辑之前,先锁定解释器及依赖项。笔记本电脑与持续集成环境之间的差异是API演示中最常见的隐性故障原因。
协议本身很简单,但应用场景却极为广泛。
《协议:小型系统最佳实践》中提到,应将该框架视为可度量的界面来使用。在扩大范围之前,先记录一个成功的用例、一个失败案例以及回滚说明。同时将正常流程与恢复流程都记录下来。重试机制、人工审核环节以及死信处理都是产品本身的一部分,而非后续需要补充的内容。在讲解循环逻辑之前,先锁定解释器及依赖项的锁文件。笔记本电脑与持续集成环境之间的差异是API演示中最常见的隐性故障来源。
继续阅读
将“继续阅读”阶段视为可度量的对象来处理效果最佳。在扩大范围之前,先记录一份理想的运行日志、一个故障案例以及回滚说明。 优先选择小型且可测试的单元,而非庞大的脚本。当某一步骤出现故障时,故障应指向单一责任模块,而非复杂的流程链。 在讲解循环逻辑之前,先固定解释器版本及依赖项锁定文件。笔记本电脑与持续集成环境之间的差异是API演示中最常见的隐性故障来源。 将“继续阅读”阶段视为可度量的对象来处理效果最佳。在扩大范围之前,先记录一份理想的运行日志、一个故障案例以及回滚说明。 除了功能结果外,还需记录执行时间以及令牌或查询成本。尽早了解这些成本信息,就能避免在从演示环境过渡到共享环境时出现意外费用。
操作检查清单
在处理操作检查清单阶段时,首先写下合同条款:所需的输入参数、成功标志以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改保持一致性。
将此阶段视为输入与已验证输出之间的契约。为相关产物命名,明确成功判定标准,杜绝默许的部分完成情况。
每次调用时都要记录请求编号、模型编号以及延迟时间。如果没有这些记录,间歇性的服务端错误就会被误认为是应用程序的缺陷。
提供具有严格结构定义和明确副作用标识的工具。主机需要在自动批准之前知道哪些调用会修改状态。
只要预算允许,就应在持续集成过程中使用测试数据而非真实的付费 API 来执行能够覆盖关键路径的冒烟测试。
请同时记录正常流程和故障恢复流程。重试机制、人工审核环节以及死信处理都是产品不可或缺的部分,而非后续需要补充的功能。
在推广该技术栈之前,应先冻结版本,为关键流程保存完整的操作记录,并明确回滚步骤。共享环境需要设置速率限制、进行租户身份验证,同时要指定专人负责密钥轮换工作。与其展示花哨的一次性演示,不如追求扎实可靠的性能。
关于 91ba71830d6a 的批量说明:请勿将提供商密钥放入代码仓库,应为每个会话设置令牌使用上限,并将操作记录与评估用配置文件存放在同一位置,以便后续更换模型时仍能保持数据可比性。
在处理强化措施的第0阶段时,首先写下相关契约:所需的输入参数、成功信号以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改始终符合要求。 相比冗长的脚本,更应采用小型且可测试的单元。当某个步骤失败时,故障应能指向单一的责任模块,而非复杂的流程链。
强化措施细节0/811:需测量该步骤的运行时间、错误类型以及代币消耗情况,然后依据固定的评估标准而非主观判断来决定是否保留该修改。
将强化措施的第1阶段视为可量化的目标面来处理效果最佳。在扩大范围之前,先记录一份理想的操作流程、一个失败案例以及回滚说明。 在功能结果旁同时记录时间消耗及代币或查询成本。提前明确成本情况,可避免在从演示环境过渡到共享环境时出现意外支出。
强化措施细节1/811:测量该任务的执行时间、错误类型以及令牌消耗情况,然后依据固定的问题集而非个别案例来决定是否保留该变更。
在强化措施的第二阶段,应在修改代码之前明确输入参数、该步骤的负责人以及完成标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。需同时记录正常流程和故障恢复流程。重试机制、人工审核环节以及错误处理方式都是产品本身的组成部分,而非后续的优化工作。
强化措施细节2/811:测量该任务的执行时间、错误类型以及令牌消耗情况,然后依据固定的问题集而非个别案例来决定是否保留该变更。