ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

从零开发一个MCP:用 Python + fastmcp 搭出可复用的 config.yaml 骨架

从零开发一个MCP:用 Python + fastmcp 搭出可复用的 config.yaml 骨架 1. 从零开发一个 MCP为什么我建议你先搭 config.yaml 骨架MCPModel Context Protocol是 Anthropic 提出的一套工具调用协议说白了就是让大模型用统一格式去点菜——声明要调用哪个工具、传什么参数、拿回什么结果。它用 JSON Schema 描述工具接口支持动态发现、参数校验和结果类型化所以模型不用为每个外部系统单独写适配代码。适合谁适合手里有一堆脚本、API、数据库查询逻辑想让 AI 助手直接调用的开发者。Python 生态里目前上手最快的是 fastmcp几行代码就能把一个普通函数注册成 MCP 工具。但很多人第一次写 MCP 会踩同一个坑工具函数写完了启动脚本也跑起来了结果接入客户端时发现工具没加载、参数对不上、路径写错。问题往往不在业务逻辑而在项目骨架没搭好——工具注册散落在代码里配置项硬编码在启动脚本里改一个端口要翻三个文件。我试过把工具注册和运行参数全部收进一个 config.yaml代码只负责读配置、注册、启动后面加工具就是往 yaml 里加一段改端口就是改一行。这篇就按这个思路用 Python fastmcp 搭一个可复用的最小骨架包含 config.yaml、启动代码、本地验证和常见报错排查。2. TaoToken 前置MCP 服务要接的模型入口怎么准备MCP 服务本身只是工具提供方它需要一个支持 MCP 的客户端比如 hermes、Claude Code 这类来调用。而客户端背后要连大模型模型入口这块可以用 TaoToken 统一管理。它的 API 地址是 https://taotoken.net/api兼容常见的 OpenAI 风格调用方式MCP 客户端配置模型时把 base_url 指过去就行。你需要先去控制台拿一个 API Key然后按客户端要求填到配置里。这一步不复杂但顺序别搞反先把 MCP 服务跑起来、本地验证工具能被调用再去配客户端。否则工具没通、模型也没通报错会混在一起排查起来很痛苦。具体入口我列一下按需取用拿 Key、管理额度https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmcp_python_fastmcp控制台总览https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmcp_python_fastmcp接入文档看 base_url 和参数格式https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmcp_python_fastmcp想先在网页里试模型对话https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmcp_python_fastmcp长期跑编码类 Agent看 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmcp_python_fastmcp注意MCP 服务监听的是本地端口客户端连的是模型 API两者是两条独立的链路。先把本地这条跑通再去配模型那条。3. 可复制配置config.yaml 骨架与 fastmcp 启动代码3.1 项目结构先定目录后面所有路径都基于它mcp-demo/ ├── config.yaml ├── server.py ├── tools/ │ ├── __init__.py │ └── domain_ip.py └── requirements.txtrequirements.txt 内容fastmcp2.0.0 requests2.31.0 pyyaml6.0安装pip install -r requirements.txt3.2 config.yaml把工具注册和运行参数都收进来这个文件是整个骨架的核心。它分两块server 段管运行参数host、port、transport、base_pathtools 段管要注册哪些工具模块。加工具只改这里不动 server.py。server: name: domain-ip-lookup transport: streamable-http host: 0.0.0.0 port: 9069 base_path: /mcp tools: - module: tools.domain_ip enabled: true # 以后加工具就在这里追加一行 # - module: tools.weather # enabled: true字段说明用表格对照一下字段作用常用值server.nameMCP 服务实例名客户端加载列表里显示自定义字符串server.transport传输方式streamable-http / stdioserver.host监听地址0.0.0.0 或 127.0.0.1server.port监听端口9069 等未占用端口server.base_pathHTTP 访问路径/mcptools[].module工具模块的导入路径tools.xxxtools[].enabled是否启用该模块true / false3.3 工具模块tools/domain_ip.py工具函数本身保持纯粹只关心业务不关心怎么启动。用mcp.tool装饰器注册description 写清楚模型靠它判断什么时候调用。import socket import requests from fastmcp import FastMCP def register(mcp: FastMCP) - None: mcp.tool( description查询指定域名的IP地址以及IP的物理归属地包含国家、省份、城市、运营商信息 ) async def lookup_domain_ip_location(domain: str) - str: Args: domain: 要查询的域名例如 baidu.com无需带 http 前缀 try: ip socket.gethostbyname(domain) resp requests.get( fhttp://ip-api.com/json/{ip}, params{lang: zh-CN}, timeout5, ) resp.raise_for_status() data resp.json() if data[status] ! success: return f查询失败{data.get(message, 未知错误)} return ( f域名{domain}\n f解析IP{ip}\n f国家{data.get(country, 未知)}\n f省份{data.get(regionName, 未知)}\n f城市{data.get(city, 未知)}\n f运营商{data.get(isp, 未知)} ) except socket.gaierror: return f域名解析失败无法解析 {domain}请检查域名是否正确 except requests.RequestException as e: return f归属地接口请求异常{str(e)} except Exception as e: return f查询异常{str(e)}tools/init.py 留空即可。3.4 server.py读配置、动态注册、启动这段代码只做三件事读 yaml、按 tools 列表动态导入并调用各模块的 register、按 server 段启动。加工具不用改它。import importlib import yaml from fastmcp import FastMCP def load_config(path: str config.yaml) - dict: with open(path, r, encodingutf-8) as f: return yaml.safe_load(f) def build_server(cfg: dict) - FastMCP: server_cfg cfg[server] mcp FastMCP(server_cfg[name]) for item in cfg.get(tools, []): if not item.get(enabled, True): continue module importlib.import_module(item[module]) if not hasattr(module, register): raise AttributeError(f模块 {item[module]} 缺少 register(mcp) 函数) module.register(mcp) return mcp if __name__ __main__: cfg load_config() mcp build_server(cfg) s cfg[server] mcp.run( transports[transport], hosts[host], ports[port], base_paths.get(base_path, /mcp), )启动python server.py看到服务监听在 0.0.0.0:9069 就说明起来了。这套骨架的好处是工具模块之间互不干扰config.yaml 就是唯一的注册表团队协作时谁加工具谁改自己那行。4. 验证请求确认工具真的被加载了服务跑起来不等于工具注册成功。fastmcp 的 streamable-http 传输下可以用 MCP 的 initialize 和 tools/list 请求来验证。先发一个初始化请求curl -s -X POST http://127.0.0.1:9069/mcp \ -H Content-Type: application/json \ -H Accept: application/json, text/event-stream \ -d { jsonrpc: 2.0, id: 1, method: initialize, params: { protocolVersion: 2024-11-05, capabilities: {}, clientInfo: {name: curl-test, version: 1.0} } }返回里会带 session 相关信息。接着列工具curl -s -X POST http://127.0.0.1:9069/mcp \ -H Content-Type: application/json \ -H Accept: application/json, text/event-stream \ -d { jsonrpc: 2.0, id: 2, method: tools/list, params: {} }如果返回的 JSON 里 tools 数组包含lookup_domain_ip_location说明工具注册成功。再调一次工具本身curl -s -X POST http://127.0.0.1:9069/mcp \ -H Content-Type: application/json \ -H Accept: application/json, text/event-stream \ -d { jsonrpc: 2.0, id: 3, method: tools/call, params: { name: lookup_domain_ip_location, arguments: {domain: baidu.com} } }正常会返回域名、解析 IP、国家、省份、城市、运营商。到这一步MCP 服务这条链路就通了。之后在客户端比如 hermes的 config.yaml 里加mcp_servers: domain-ip-lookup-server: url: http://127.0.0.1:9069/mcp启动客户端会话在 MCP Servers 加载列表里能看到domain-ip-lookup-server就可以在聊天框里让它调用这个工具干活了。5. 本篇常见错排查config.yaml 与 fastmcp 的坑5.1 工具没出现在 tools/list 里最常见的原因是 config.yaml 里 module 路径写错或者模块没有register函数。server.py 里我加了 hasattr 检查如果模块缺 register 会直接抛 AttributeError启动时就报出来比运行到一半才发现好。另外注意 enabled 字段写成false或漏写导致被跳过。5.2 端口被占用或客户端连不上OSError: [Errno 98] Address already in use说明 9069 被占了改 config.yaml 里的 port 即可不用动代码。客户端连不上时先确认 host本地客户端用 127.0.0.1跨机器访问才用 0.0.0.0 并检查防火墙。base_path 要和客户端 url 里的路径一致默认 /mcp。5.3 工具调用返回查询异常ip-api.com 有频率限制短时间大量请求会被限流。生产环境建议换带 Key 的归属地接口或者加本地缓存。另外socket.gethostbyname只支持 IPv4遇到纯 IPv6 域名会解析失败需要的话换成socket.getaddrinfo。5.4 异步函数里用同步 requests 阻塞fastmcp 的工具函数可以是 async但里面调requests.get是同步阻塞的高并发下会拖慢事件循环。量小无所谓量大建议换httpx.AsyncClient。这是骨架阶段容易忽略、上线后才暴露的问题。5.5 yaml 缩进错误导致读不到配置yaml 对缩进敏感tools 列表项前面的短横线和缩进层级要对齐。读配置失败时先单独跑一段python -c import yaml; print(yaml.safe_load(open(config.yaml)))确认能解析。6. 把骨架用起来下一步怎么扩展这套骨架跑通后加新工具就是三步在 tools/ 下新建模块、写 register 函数、在 config.yaml 的 tools 列表加一行。server.py 完全不用动。如果你打算长期跑编码类 Agent、频繁调用 MCP 工具可以看下 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmcp_python_fastmcp模型入口和 Key 管理在控制台https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmcp_python_fastmcp接入参数和 base_url 格式以文档为准https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmcp_python_fastmcp想先在网页里验证模型能不能正常对话再去接 MCPhttps://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmcp_python_fastmcp最后提醒一句config.yaml 里的 base_path 和客户端 url 路径必须一致这个坑我见过太多次工具明明注册成功客户端就是加载不出来查半天发现是路径差了一个斜杠。
返回列表