learn-claude-code mcp-builder Skill 实战:从零构建 MCP 服务器并接入 Agent Harness
learn-claude-code mcp-builder Skill 实战从零构建 MCP 服务器并接入 Agent Harness【免费下载链接】learn-claude-codeBash is all you need - A nano claude code–like 「agent harness」, built from 0 to 1项目地址: https://gitcode.com/GitHub_Trending/an/learn-claude-code本篇基于 skills/mcp-builder/SKILL.md 展开讲解如何在 learn-claude-code 这个从 0 到 1 构建 Claude Code 式 agent harness的仓库中理解并使用 mcp-builder 技能来构建 MCPModel Context Protocol服务器从项目初始化、Python/TypeScript 双语言服务器模板到外部 API 集成、数据库访问、Resource 暴露、测试验证与工程最佳实践并结合仓库 s14 章节源码说明自建 MCP 服务器是如何被 harness 侧发现、命名、鉴权和调用的。读完你能独立完成一个可注册、可测试、可被 agent 动态发现的 MCP 服务器并理解其在 agent loop 中的完整调用链路。1. 技能文件本身frontmatter 决定了何时被加载mcp-builder 是仓库skills/目录下的一个技能文件与 skills/code-review/SKILL.md、skills/agent-builder/SKILL.md、skills/pdf/SKILL.md 并列。它的结构遵循仓库统一的 SKILL.md 约定一段 YAML frontmatter 一份完整的操作指南正文。--- name: mcp-builder description: Build MCP (Model Context Protocol) servers that give Claude new capabilities. Use when user wants to create an MCP server, add tools to Claude, or integrate external services. ---这两个字段不是装饰而是 harness 消费技能的接口name是技能在目录中的唯一标识description会进入技能目录catalog模型正是依据它来决定何时需要这个技能。mcp-builder 的 description 明确写了触发条件——Use when user wants to create an MCP server, add tools to Claude, or integrate external services即用户想创建 MCP 服务器、给 Claude 加工具、或集成外部服务时命中。这一点在仓库测试中有直接印证tests/test_skill_loading.py 验证了技能加载器只把namedescription放进目录例如断言目录输出为- code-review: Review code for bugs, regressions, and missing tests.而完整正文含全部代码模板只在load_skill被调用时才注入上下文。也就是说mcp-builder 这份 SKILL.md 的正文——下面要逐一讲解的所有模板和命令——属于按需加载的知识包这正是 learn-claude-code s07 技能加载章节s07_skill_loading/code.py 中SkillLoader.catalog()与SkillLoader.load()所演示的机制。写作/维护这类技能文件的推论description 写得越精准技能越容易被正确触发正文则应当自包含、可复制运行因为加载它的是一次性的上下文而不是长期驻留的文档。2. MCP 是什么服务器暴露三类能力文档开宗明义MCP 让 Claude 通过标准化协议与外部服务交互。一个 MCP 服务器可以暴露三类能力能力含义类比Tools模型可以调用的函数类似 API 端点Resources模型可以读取的数据类似文件或数据库记录Prompts预置的提示词模板可复用的 prompt 片段三者的核心区别在方向Tool 是执行动作有副作用可能Resource 是只读数据服务器主动声明 URIPrompt 是模板人/模型选择的起点。后文的代码模板分别覆盖了 ToolsPython/TypeScript 示例与 Resourcesserver.resource装饰器Prompts 在文档中作为概念列出。3. 快速开始Python MCP 服务器3.1 项目初始化文档给出的标准初始化流程# Create project mkdir my-mcp-server cd my-mcp-server python3 -m venv venv source venv/bin/activate # Install MCP SDK pip install mcp要点官方 SDK 包名就是mcp文档模板假设 Python 3python3 -m venv建虚拟环境后激活再装 SDK。3.2 基础服务器模板完整可运行以下是文档中的完整模板可直接复制为my_server.py#!/usr/bin/env python3 my_server.py - A simple MCP server from mcp.server import Server from mcp.server.stdio import stdio_server from mcp.types import Tool, TextContent # Create server instance server Server(my-server) # Define a tool server.tool() async def hello(name: str) - str: Say hello to someone. Args: name: The name to greet return fHello, {name}! server.tool() async def add_numbers(a: int, b: int) - str: Add two numbers together. Args: a: First number b: Second number return str(a b) # Run server async def main(): async with stdio_server() as (read, write): await server.run(read, write) if __name__ __main__: import asyncio asyncio.run(main())几个值得注意的实现细节传输层是 stdiostdio_server()通过标准输入/输出收发 JSON-RPC 消息服务器不监听端口、不需要网络配置——这是 MCP 服务器最常见的部署形态也决定了它在mcp.json里以可执行命令的形式注册见 3.3 节。装饰器注册工具server.tool()把普通async函数变成工具。函数的 docstring 会成为模型的调用依据——这就是文档Best Practices第一条描述要清晰的落点。类型标注即参数 schemaname: str、a: int, b: int这些标注由 SDK 推导为工具的输入 schema模型据此生成参数。返回字符串两个工具都返回str这是最简输出形态SDK 也支持TextContent等结构化内容模板顶部即导入了TextContent。3.3 注册到 Claude服务器写好后将其加入~/.claude/mcp.json{ mcpServers: { my-server: { command: python3, args: [/path/to/my_server.py] } } }配置语义很直白客户端Claude需要时直接以python3 /path/to/my_server.py启动该进程通过 stdio 通信。args指向脚本绝对路径若脚本依赖虚拟环境command应指向该环境内的解释器。4. TypeScript MCP 服务器显式处理器写法同一能力也可以用 TypeScript 实现文档给出了完整的对照模板。4.1 初始化mkdir my-mcp-server cd my-mcp-server npm init -y npm install modelcontextprotocol/sdk4.2 服务器模板完整// src/index.ts import { Server } from modelcontextprotocol/sdk/server/index.js; import { StdioServerTransport } from modelcontextprotocol/sdk/server/stdio.js; const server new Server({ name: my-server, version: 1.0.0, }); // Define tools server.setRequestHandler(tools/list, async () ({ tools: [ { name: hello, description: Say hello to someone, inputSchema: { type: object, properties: { name: { type: string, description: Name to greet }, }, required: [name], }, }, ], })); server.setRequestHandler(tools/call, async (request) { if (request.params.name hello) { const name request.params.arguments.name; return { content: [{ type: text, text: Hello, ${name}! }] }; } throw new Error(Unknown tool); }); // Start server const transport new StdioServerTransport(); server.connect(transport);与 Python 版对照可以看到 MCP 协议的两个核心请求在语言层面的体现tools/list返回工具清单每个工具含name、description和 JSON Schema 形式的inputSchema。这里 schema 是手写的而 Python 装饰器版由 SDK 从类型标注推导——两种写法最终交付给客户端的是同一份结构。tools/call按request.params.name分发到具体实现返回{ content: [{ type: text, text: ... }] }。未知工具直接抛错。StdioServerTransportserver.connect(transport)与 Python 的stdio_server()对应同样是 stdio 传输。5. 进阶模式文档的Advanced Patterns给出三类真实场景的写法。5.1 外部 API 集成把 HTTP API 包成工具以天气查询为例完整代码import httpx from mcp.server import Server server Server(weather-server) server.tool() async def get_weather(city: str) - str: Get current weather for a city. async with httpx.AsyncClient() as client: resp await client.get( fhttps://api.weatherapi.com/v1/current.json, params{key: YOUR_API_KEY, q: city} ) data resp.json() return f{city}: {data[current][temp_c]}C, {data[current][condition][text]}要点工具函数是async的内部用httpx.AsyncClient做非阻塞请求避免 I/O 阻塞事件循环呼应最佳实践第 4 条YOUR_API_KEY是占位符实际部署时应从环境变量注入而非硬编码。5.2 数据库访问只读查询import sqlite3 from mcp.server import Server server Server(db-server) server.tool() async def query_db(sql: str) - str: Execute a read-only SQL query. if not sql.strip().upper().startswith(SELECT): return Error: Only SELECT queries allowed conn sqlite3.connect(data.db) cursor conn.execute(sql) rows cursor.fetchall() conn.close() return str(rows)这里有一个值得肯定的防御式设计服务器端自行做输入校验——拒绝一切非SELECT开头的语句把执行任意 SQL收窄为只读查询。注意这只是第一道防线真正的授权边界应在宿主侧见第 8 节。5.3 Resources只读数据server.resource(config://settings) async def get_settings() - str: Application settings. return open(settings.json).read() server.resource(file://{path}) async def read_file(path: str) - str: Read a file from the workspace. return open(path).read()Resource 通过 URI 模板暴露config://settings是固定 URIfile://{path}带路径参数path会被绑定为函数形参。Resource 与 Tool 的分工Resource 是客户端声明并读取的数据源Tool 是模型主动决定调用的动作。6. 测试与调试文档给出两条验证路径完整命令# Test with MCP Inspector npx anthropics/mcp-inspector python3 my_server.py # Or send test messages directly echo {jsonrpc:2.0,id:1,method:tools/list} | python3 my_server.pyMCP Inspector官方调试工具把服务器当子进程拉起后提供交互式界面可枚举工具、试调用、查看原始消息裸 JSON-RPC 管道测试直接通过 stdin 发一条tools/list请求。这同时也展示了 MCP 的传输本质——服务器就是从 stdin 读 JSON-RPC、往 stdout 写 JSON-RPC的进程。第二条命令还隐含了一个 stdio 服务器的通用约束stdout 必须只承载协议消息调试日志应写到 stderr否则会污染协议流从文档模板的写法看服务器代码不应随意print。7. 最佳实践清单文档结尾的六条 Best Practices逐条对应前面模板中的具体设计工具描述要清晰Clear tool descriptions——模型靠 description 决定何时调用工具Python 模板的 docstring、TypeScript 模板的description字段都是这个用途输入校验Input validation——始终校验并清洗输入如 5.2 节只允许SELECT错误处理Error handling——返回有意义的错误信息如Error: Only SELECT queries allowed而不是抛栈或静默失败默认 asyncAsync by default——I/O 操作用 async/await如 5.1 节的httpx.AsyncClient安全Security——不暴露未鉴权的敏感操作幂等性Idempotency——工具应当可以安全重试。8. 从 harness 侧看 MCP 消费s14 章节的印证以上都是建服务器的一侧。learn-claude-code 的 s14_mcp_plugin/README.md 与 s14_mcp_plugin/code.py 则从消费服务器的一侧闭环了这条链路——自建服务器的工具最终如何进入 agent 的工具池。以下结论均出自该章节源码1发现即连接工具动态进池。harness 暴露一个connect_mcp(name)基础工具模型调用它连接服务器见 s14_mcp_plugin/code.py#L279-L292。连接成功后MCPClient保存tools/list的发现和tools/call的处理器s14_mcp_plugin/code.py#L160-L187随后每一轮 agent loop 都调用assemble_tool_pool()重建工具池把新连接的服务器工具加入模型输入s14_mcp_plugin/code.py#L313-L356。这就是为什么 mcp-builder 建出来的hello/get_weather/query_db不需要改动 harness 代码就能被模型看见。2命名规范化mcp__{server}__{tool}。多个服务器都可能有search工具harness 用统一前缀消歧docs服务器的search变成mcp__docs__search。normalize_mcp_name()会把工具名中字母表外的字符替换为下划线并检查 64 字符上限与归一化后的重名冲突s14_mcp_plugin/code.py#L203-L208。对你自建服务器的直接含义工具名用简短的snake_case最稳妥避免归一化后撞名。3授权来自宿主策略不来自服务器自述。s14 明确服务器可以给出readOnlyHint/destructiveHint注解但那只是提示不是授权。harness 侧维护一张宿主策略表MCP_HOST_POLICY { (docs, search): allow, (docs, get_version): allow, (deploy, status): allow, (deploy, trigger): confirm, }permission_hook()对mcp__前缀的工具按此表裁决未配置的外部工具默认要求用户确认s14_mcp_plugin/code.py#L402-L408。这与文档 Best Practices 第 5 条敏感操作必须鉴权形成呼应服务器内做输入校验宿主做最终授权两层缺一不可。4错误留在工具边界。模型少传必填参数时MCPClient.call_tool()捕获异常并返回形如MCP error: TypeError: lambda() missing 1 required argument: query的错误tool_results14_mcp_plugin/code.py#L180-L187agent loop 不中断模型下一轮可以自行纠正参数。这正是 Best Practices 第 3 条返回有意义的错误信息的价值所在——错误字符串是给模型看的提示而不是给终端看的日志。注s14 章节使用进程内的 mock 服务器来演示tools/list/tools/call边界并未实现真实的 MCP 传输层见 s14_mcp_plugin/README.md 的说明但命名、策略、错误处理的机制与你用 mcp-builder 构建真实服务器后接入 harness 时面对的接口完全一致。9. 小结把 skills/mcp-builder/SKILL.md 当作一份可执行的知识包它的价值链条是完整的frontmatternamedescription决定技能何时被加载正文按需注入上下文Python/TypeScript 双模板覆盖 stdio 传输、工具注册装饰器式与setRequestHandler式、mcp.json注册三个关键步骤均可直接复制运行进阶模式给出 API 集成、只读数据库访问、Resources 三类真实场景的完整代码测试手段MCP Inspector 裸 JSON-RPC 管道 六条最佳实践构成工程化收尾s14 章节源码从消费侧印证了这套服务器接入 harness 后的完整链路connect_mcp发现 →mcp__{server}__{tool}命名进池 → 宿主策略鉴权 → 错误回到工具边界。按这份技能操作你可以为自己的外部服务构建一个命名规范、描述清晰、错误可控的 MCP 服务器并理解它在 agent harness 中被发现与调用的完整机制。【免费下载链接】learn-claude-codeBash is all you need - A nano claude code–like 「agent harness」, built from 0 to 1项目地址: https://gitcode.com/GitHub_Trending/an/learn-claude-code创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考