这篇适合谁、读完能做到什么
如果你想写一个只在自己电脑上运行、供 Claude 等客户端调用的 MCP 服务器,这篇适合你。读完你能用 stdio 传输写出一个带工具的 Python 服务器,用 MCP Inspector 测试它,把它接进 Claude Desktop,并且知道出问题时该查哪几处。[1]
stdio 是什么,为什么本地服务器首选它
stdio 传输让 MCP 服务器通过标准输入(stdin)和标准输出(stdout)与客户端通信。这是目前 MCP 规范里最常用的传输方式,这也是本地服务器推荐的做法。[1]
它的工作方式很简单:客户端把服务器当作子进程启动,服务器从 stdin 读取 JSON-RPC 消息,再把响应写到 stdout。每条消息是一条 JSON-RPC 请求、通知或响应,用换行符分隔,消息内部不能含有换行。[1]
和旧的 SSE 方式相比,stdio 服务器不需要搭建 Web 服务器,也不需要 HTTP 端点,实现起来更简单,调试也更直接。SSE 传输在 MCP 规范 2025-06-18 版中已被废弃,由 Streamable HTTP 取代。如果你要做的是远程服务器,应该看 Streamable HTTP;做本地工具,stdio 就够了。[1][3]
几条必须遵守的规则
stdio 对输出非常敏感,下面三条必须记住:[1]
- 服务器不能往
stdout写任何不是合法 MCP 消息的内容。stdout只留给 MCP 消息。 - 服务器可以把 UTF-8 字符串写到
stderr用于日志。 - 客户端也不能往服务器的
stdin写入不合法的 MCP 消息。
很多新手的 bug 都出在这里:在代码里加一句 print,输出到 stdout,客户端就会解析失败。日志应该写到 stderr。[1]
准备
- Python 3.8 或更高版本。
- MCP Python SDK,用
pip install mcp安装。 - 对异步编程(async)有基本了解,能看懂
async with和await。[1]
这篇的练习是一个 Python 服务器,名字叫 example-stdio-server。它先只有一个问候工具,再加上加法和乘法,最后多一个返回服务器信息的工具。[1]
第一步:写一个最小的 Python 服务器
下面是完整的最小示例,它展示了 stdio 服务器的基本结构:导入类、创建服务器、用装饰器定义工具,再通过 stdio_server 运行。[1]
import asyncio
import logging
from mcp.server import Server
from mcp.server.stdio import stdio_server
# Create server instance
server = Server("example-server")
@server.tool()
def add(a: int, b: int) -> int:
"""Add two numbers"""
return a + b
async def main():
async with stdio_server(server) as (read_stream, write_stream):
await server.run(
read_stream,
write_stream,
server.create_initialization_options()
)
if __name__ == "__main__":
asyncio.run(main())这段代码的关键是 stdio_server 上下文管理器。它负责把 stdin 和 stdout 包装成读写流,然后交给 server.run。服务器自己不需要关心网络或端口。[1]
工具用 @server.tool() 装饰器定义。函数的参数和返回值的类型注解会成为工具的输入和输出描述,函数上方的文档字符串说明工具的用途。[1]
第二步:加入更多工具
在服务器里再加两个工具,一个做乘法,一个返回服务器信息:[1]
@server.tool()
def calculate_sum(a: int, b: int) -> int:
"""Calculate the sum of two numbers"""
return a + b
@server.tool()
def calculate_product(a: int, b: int) -> int:
"""Calculate the product of two numbers"""
return a * b
@server.tool()
def get_server_info() -> dict:
"""Get information about this MCP server"""
return {
"server_name": "example-stdio-server",
"version": "1.0.0",
"transport": "stdio",
"capabilities": ["tools"]
}get_server_info 返回的字典里,服务器名是 example-stdio-server,版本是 1.0.0,传输方式是 stdio,能力是 tools。这个工具适合用来确认你连接的确实是你写的那个服务器。[1]
代码末尾的 if __name__ == "__main__": 让脚本只在直接运行时才启动服务器,asyncio.run(main()) 负责驱动异步的 main 函数。这两处是 Python 版的固定写法,照着保留即可。[1]
运行服务器
把代码保存为 server.py,然后在命令行运行:[1]
python server.py服务器启动后会停在那里,等待 stdin 的输入。这是正常的,它在等客户端发来的消息。你不需要自己去输入 JSON-RPC 消息,交给 Inspector 或 Claude 去做就行。[1]
命令里的 python server.py 也是 Inspector 要启动的服务器命令。npx @modelcontextprotocol/inspector 是 Inspector 本身,后面跟的内容告诉它怎样启动你的服务器,所以写错服务器的启动命令,Inspector 也连不上。[1]
用 MCP Inspector 测试
MCP Inspector 是一个调试和测试工具。它可以查看服务器的能力、用不同参数测试工具、监控 JSON-RPC 消息,还能帮你定位连接问题。[1][2]
启动 Inspector 并指向你的服务器,用这条命令:[1][2]
npx @modelcontextprotocol/inspector python server.py打开 Inspector 之后,建议按这个顺序测试:先看服务器能力,再用 get_greeting 试几个不同的名字,用 calculate_sum 试几组数字,最后调用 get_server_info 核对元数据。Inspector 还会显示客户端和服务器之间交换的 JSON-RPC 消息,遇到问题时可以对照检查格式。[1]
接入 Claude Desktop
Claude Desktop 的配置文件位置是:Windows 上为 %APPDATA%\Claude\claude_desktop_config.json,Mac 上为 ~/Library/Application Support/Claude/claude_desktop_config.json。在文件里用 mcpServers 注册服务器,启动命令是 python,参数是服务器文件的路径:[1]
{
"mcpServers": {
"example-stdio-server": {
"command": "python",
"args": ["path/to/your/server.py"]
}
}
}注意示例里的 path/to/your/server.py 要换成你电脑上 server.py 的真实路径。保存之后重启 Claude,让新配置生效。[1]
然后在对话里试几句话,比如「Can you greet me using the greeting tool?」「Calculate the sum of 15 and 27」或者「What's the server info?」。如果 Claude 能调用你的工具并返回结果,说明整条链路已经通了。[1]
调试技巧
- 日志写到
stderr,不要写到stdout。 - 确保每条 JSON-RPC 消息都用换行分隔。
- 先用简单的工具测试,确认通了再加复杂功能。
- 用 Inspector 核对消息格式。[1]
想在 VS Code 里断点调试,可以在 .vscode/launch.json 里加一个 Python 调试配置:"type": "python","program": "server.py","console": "integratedTerminal"。设好断点后启动调试器,再用 Inspector 发请求。[1]
常见问题
服务器起不来时,先确认依赖装好了(pip install mcp),检查 Python 语法和缩进,再看控制台报错。[1]
工具没有出现在列表里,通常是 @server.tool() 装饰器漏写了,或者工具函数定义在 main() 之后。[1]
连接出问题时,检查服务器是否正确使用了 stdio 传输,确认没有其他进程干扰,并核对 Inspector 的命令格式。[1]
TypeScript 版怎么写
TypeScript 版用的是底层的 Server 类,并要自己注册 ListToolsRequestSchema 和 CallToolRequestSchema 两个请求处理函数,分别负责列出工具和调用工具。下面是它的导入和启动部分:[1]
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
const server = new Server(
{
name: "example-server",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
async function runServer() {
const transport = new StdioServerTransport();
await server.connect(transport);
}
runServer().catch(console.error);调用工具时,处理函数返回的是 type 为 text 的内容数组。如果请求的工具名不认识,就抛出错误。[1]
Python 版只需要用装饰器定义工具,TypeScript 版则要自己写列出工具和调用工具的两个处理函数,所以示例明显更长。两种写法的目标是一样的:让客户端能看到工具清单,并能调用其中的工具。[1]
下一步
下一步可以学习 Streamable HTTP,它是另一种支持的传输方式,适合远程服务器。安全和部署方面,也可以继续看 MCP 的安全最佳实践和部署策略。[1]
本文改编自 Microsoft 开源课程《MCP for Beginners》第 3 课中关于 stdio 服务器的一节[1],原文以 MIT 许可证发布。
来源
- 1MCP Server with stdio TransportMicrosoft mcp-for-beginners (MIT)
- 2MCP InspectorModel Context Protocol
- 3MCP Specification 2026-07-28Model Context Protocol
引用本页: MCP stdio 服务器怎么写. TellWorld, https://www.tellworld.net/zh/practice/practice-mcp-stdio-server. 更新于 2026-11-14