前言最近在搞 AI Agent 项目的时候发现一个很头疼的问题大模型能力再强它也没法直接操作数据库、调用 API、读写文件。你得自己写一堆胶水代码把工具和模型串起来而且换个模型又得重写一遍。Anthropic 去年搞了个 MCPModel Context Protocol说白了就是给 AI Agent 定了一套标准的工具调用协议。你实现一次 MCP Server所有支持 MCP 的客户端Claude Desktop、Cursor、Continue 等都能直接用。今天就从零开始手把手搭一个能用的 MCP Server。什么是 MCP先简单说下 MCP 的核心思路。它借鉴了 LSPLanguage Server Protocol的设计哲学协议标准化实现一次到处可用。MCP 的架构分三层Host宿主比如 Claude Desktop、Cursor 这些客户端Client客户端Host 内部的 MCP 客户端负责和 Server 通信Server服务器你写的工具服务提供具体能力通信方式有两种stdio本地进程和 HTTPSSE远程服务。今天先搞最常用的 stdio 方式。环境准备# Python 3.10python--version# 安装 MCP SDKpipinstallmcp# 验证安装python-cimport mcp; print(mcp.__version__)MCP SDK 的版本迭代很快建议用最新的。如果你用的是 uv 包管理器uv init my-mcp-servercdmy-mcp-server uvaddmcp写第一个 MCP Server直接上代码。我们做一个简单的文件工具服务器提供读取文件和列出目录两个能力。# server.pyimportosfrommcp.serverimportServerfrommcp.server.stdioimportstdio_serverfrommcp.typesimportTool,TextContent# 创建 Server 实例serverServer(file-tools)# 定义工具列表server.list_tools()asyncdeflist_tools():return[Tool(nameread_file,description读取指定路径的文件内容,inputSchema{type:object,properties:{path:{type:string,description:文件的绝对路径}},required:[path]}),Tool(namelist_directory,description列出指定目录下的文件和子目录,inputSchema{type:object,properties:{path:{type:string,description:目录的绝对路径}},required:[path]})]# 处理工具调用server.call_tool()asyncdefcall_tool(name:str,arguments:dict):ifnameread_file:file_patharguments[path]ifnotos.path.exists(file_path):return[TextContent(typetext,textf错误文件不存在{file_path})]withopen(file_path,r,encodingutf-8)asf:contentf.read()return[TextContent(typetext,textcontent)]elifnamelist_directory:dir_patharguments[path]ifnotos.path.isdir(dir_path):return[TextContent(typetext,textf错误目录不存在{dir_path})]entriesos.listdir(dir_path)result\n.join(entries)return[TextContent(typetext,textresult)]else:return[TextContent(typetext,textf未知工具{name})]# 启动服务器asyncdefmain():asyncwithstdio_server()as(read_stream,write_stream):awaitserver.run(read_stream,write_stream,server.create_initialization_options())if__name____main__:importasyncio asyncio.run(main())看起来代码不多对吧这就是 MCP 的好处——协议帮你处理了通信、序列化、错误处理这些脏活你只需要关注业务逻辑。代码拆解几个关键点解释一下Server 实例Server(file-tools)里的名字会显示在客户端里用户看到的就是这个。server.list_tools()装饰器注册工具列表。每个 Tool 需要 name、description 和 inputSchemaJSON Schema 格式。inputSchema 很重要LLM 靠它来理解怎么调用你的工具。server.call_tool()实际执行逻辑。name 是工具名arguments 是参数。返回值必须是TextContent列表。stdio_server用标准输入输出通信适合本地运行。客户端启动你的进程后通过 stdin/stdout 交换 JSON-RPC 消息。测试一下先手动跑看看有没有语法错误python server.py程序会阻塞等待输入这是正常的——stdio 模式下它在等客户端发消息。CtrlC 退出就行。配置到 Claude Desktop编辑 Claude Desktop 的配置文件{mcpServers:{file-tools:{command:python,args:[/absolute/path/to/server.py]}}}配置文件位置macOS~/Library/Application Support/Claude/claude_desktop_config.jsonWindows%APPDATA%\Claude\claude_desktop_config.json重启 Claude Desktop你会看到工具图标出现了。试着让它读个文件或者列个目录它会自动调用你的 MCP Server。进阶加点实用功能光读文件太基础了。再加一个代码搜索工具支持正则表达式搜索文件内容importreserver.list_tools()asyncdeflist_tools():return[# ... 前面的工具保留Tool(namesearch_in_files,description在指定目录下搜索匹配正则表达式的文件内容,inputSchema{type:object,properties:{directory:{type:string,description:搜索的根目录},pattern:{type:string,description:正则表达式},file_extension:{type:string,description:限定文件扩展名如 .py、.js,default:}},required:[directory,pattern]})]server.call_tool()asyncdefcall_tool(name:str,arguments:dict):# ... 前面的逻辑保留ifnamesearch_in_files:directoryarguments[directory]patternarguments[pattern]extarguments.get(file_extension,)results[]forroot,dirs,filesinos.walk(directory):forfileinfiles:ifextandnotfile.endswith(ext):continuefilepathos.path.join(root,file)try:withopen(filepath,r,encodingutf-8)asf:fori,lineinenumerate(f,1):ifre.search(pattern,line):results.append(f{filepath}:{i}:{line.strip()})except(UnicodeDecodeError,PermissionError):passifnotresults:return[TextContent(typetext,text没有找到匹配的内容)]return[TextContent(typetext,text\n.join(results[:50]))]这个工具在实际开发中很实用——你可以让 AI 帮你在项目里搜代码、找引用、定位 bug。踩坑记录说几个我实际开发中遇到的坑1. JSON Schema 要写对inputSchema 必须是合法的 JSON Schema。我一开始偷懒没写 required 字段结果 LLM 调用工具时经常漏参数。写清楚 required 能显著提高调用准确率。2. 错误处理别偷懒工具执行出错时不要抛异常返回一个 TextContent 告诉客户端错误信息。否则整个 MCP 连接可能断掉。3. 返回内容别太长如果你的工具返回了超长文本比如读了一个几 MB 的文件有些客户端会截断或者报错。建议加上长度限制contentf.read()iflen(content)10000:contentcontent[:10000]\n... (内容过长已截断)4. Windows 路径注意转义Windows 上的反斜杠路径在 JSON 里需要转义。建议在 inputSchema 的 description 里提示用户用正斜杠。总结MCP 这个协议设计得确实优雅。你只需要关心两个函数list_tools告诉客户端我能做什么call_tool实际去做。通信、协议、序列化这些全帮你搞定了。如果你想让 AI Agent 能操作更多东西——数据库、浏览器、文件系统、第三方 API——写个 MCP Server 就行所有支持 MCP 的客户端都能直接用。下一步可以研究的东西Streamable HTTP远程部署 MCP Server支持多客户端Resources除了 ToolsMCP 还支持 Resources提供上下文数据和 Prompts模板提示词安全机制生产环境下的认证和权限控制完整代码已上传 GitHub欢迎 Star。本文首发于 CSDN转载请注明出处。