ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

mcpo 的简单使用:用 uvx/conda/pip 三种方式跑通 MCP 服务

mcpo 的简单使用:用 uvx/conda/pip 三种方式跑通 MCP 服务 1. mcpo 是什么把 MCP 服务变成 HTTP 接口的本地网关如果你最近在折腾 MCPModel Context Protocol大概率会遇到一个尴尬Claude Desktop、Cline、Cursor 这些客户端各自用 stdio 方式拉起 MCP 服务进程一多就乱想用 curl 或 Python 脚本直接调一下某个工具还得自己写一套 stdio 通信。mcpo 就是来解决这个问题的——它把本地基于 stdio 的 MCP 服务包装成一个标准的 HTTP 服务自动生成 OpenAPI 文档你打开浏览器就能看到/docs用 curl 或 requests 就能直接调。一句话概括mcpo 是 MCP 到 HTTP 的桥。它本身不提供任何工具能力工具能力还是来自你挂上去的 MCP server比如 fetch、time、filesystem。mcpo 负责启动这些 server、转发请求、把 MCP 的 JSON-RPC 协议翻译成 REST 风格的接口。它适合谁三类人最需要第一类是想快速验证 MCP 工具能力的人。你不想装 Claude Desktop也不想配 Cline只想确认某个 MCP server 到底能不能跑、返回什么结构mcpo 起一个端口浏览器打开/docs就能试。第二类是想把 MCP 能力接进自己后端的人。你的服务是 Python/Go/Java 写的不想引入 MCP SDK只想发个 HTTP POSTmcpo 就是最省事的中间层。第三类是本地要同时跑多个 MCP server 的人。mcpo 支持一个 JSON 配置文件挂载多个 server统一端口、统一鉴权比一个个手动拉起干净得多。安装方式上社区里流传最广的是三种uvx直接跑、conda建环境、pip装进现有环境。这三种不是互斥的而是对应不同的使用习惯和环境约束。下面我会把三种路径都走一遍给出可复制的命令、启动参数、端口配置并用一次 curl 请求验证服务是否真的通了。你按自己的环境选一种就行不用全装。需要提前说明的是mcpo 依赖 Python 3.10我实测用 3.11 最稳。另外它底层要调用uv/uvx来拉起 MCP server所以即使你用 conda 或 pip 装 mcpo机器上最好也有 uv否则部分 server 的启动命令会失败。这一点后面排障章节会细说。2. 三种安装路径对比uvx、conda、pip 到底怎么选在动手之前先把三条路径的差异讲清楚避免你装到一半发现方向不对。uvx 路径uvx 是 uv 提供的工具运行器类似npx。它的特点是不污染全局环境每次运行在临时环境里解析依赖。命令形如uvx mcpo --port 8000 -- uvx mcp-server-fetch。优点是零安装、版本隔离、升级方便缺点是每次启动有解析开销且首次运行要下载包网络不好时会卡。适合「我就想快速试一下」的场景。conda 路径conda create -n mcpo python3.11建一个独立环境再pip install mcpo。优点是环境干净、Python 版本可控、适合长期使用缺点是多一层环境管理激活环境这一步容易忘。适合「我要长期跑、还要装别的 Python 包」的场景。pip 路径直接pip install mcpo装进当前环境。优点是命令最短缺点是如果当前环境已经有别的包可能产生依赖冲突尤其是pydantic、httpx这类被广泛依赖的库。适合「我有专门的虚拟环境或者就是一次性容器」的场景。维度uvxcondapip是否需预装需 uv需 conda需 Python环境隔离临时隔离独立环境依赖当前环境启动速度首次慢后续快快快版本管理自动手动手动适合场景快速验证长期使用容器/专用环境冲突风险极低低中我个人的建议是先用 uvx 跑通确认能用之后再决定要不要落到 conda 或 pip。因为 uvx 不需要你提前决定环境策略试错成本最低。等你确定要长期挂某个 MCP server再迁到 conda 环境里把版本钉死。还有一个容易被忽略的点mcpo 启动 MCP server 时server 本身的安装方式可以和 mcpo 不同。比如你用 conda 装了 mcpo但配置里写command: uvx去拉起 fetch server这是完全合法的。mcpo 只负责转发不关心 server 怎么来的。所以三种方式本质上是「mcpo 自己的安装方式」而不是「MCP server 的安装方式」别混淆。下面进入实操。我会先给 uvx 的最短路径再给 conda 和 pip 的完整路径最后用同一个 curl 验证三者结果一致。3. 可复制配置uvx / conda / pip 三套启动命令与 mcpo.json这一节是全文的核心所有命令都可以直接复制。我按三种路径分别给出并在最后给出多 server 的mcpo.json配置。3.1 uvx 路径最短启动命令确保机器上有 uv。如果没有先装 uv这一步和 mcpo 无关是 uv 自己的安装curl -LsSf https://astral.sh/uv/install.sh | sh装完后新开一个终端让 PATH 生效然后直接跑uvx mcpo --port 8000 -- uvx mcp-server-fetch这行命令拆开看uvx mcpo表示用 uvx 临时环境运行 mcpo--port 8000是 mcpo 对外暴露的 HTTP 端口--之后的内容是「要挂载的 MCP server 启动命令」这里是uvx mcp-server-fetch。也就是说mcpo 会以子进程方式拉起uvx mcp-server-fetch然后通过 stdio 和它通信。启动成功后终端会打印类似Uvicorn running on http://0.0.0.0:8000的日志。此时浏览器打开http://localhost:8000/docs能看到自动生成的 OpenAPI 文档里面会有/fetch这个 POST 接口。3.2 conda 路径独立环境长期使用conda create -n mcpo python3.11 -y conda activate mcpo pip install mcpo pip install uv注意这里额外装了uv因为很多 MCP server 的推荐启动方式就是uvx机器上有 uv 会省事。装完后启动mcpo --port 8000 -- uvx mcp-server-fetch和 uvx 路径的区别是这里mcpo是 conda 环境里的可执行文件不经过 uvx 的临时环境。启动日志和接口完全一致。3.3 pip 路径装进现有环境python -m venv .venv source .venv/bin/activate # Windows 用 .venv\Scripts\activate pip install mcpo uv mcpo --port 8000 -- uvx mcp-server-fetch我强烈建议 pip 路径一定配一个 venv不要直接装进系统 Python。原因前面说过mcpo 依赖链里有pydantic和httpx和很多项目冲突。3.4 多 server 配置mcpo.json单 server 用--传命令就够了但要同时挂多个 server就得用配置文件。新建mcpo.json{ mcpServers: { fetch: { command: uvx, args: [mcp-server-fetch] }, time: { command: uvx, args: [mcp-server-time, --local-timezoneAsia/Shanghai] } } }然后启动uvx mcpo --config mcpo.json --port 8000 --api-key your-secret-key这里多了--api-key加上之后所有请求都要带Authorization: Bearer your-secret-key否则返回 401。生产环境或者端口对外时务必加。启动后/docs里会同时出现/fetch和/time两组接口。注意--api-key的值不要用示例里的your-secret-key换成你自己的随机串。这个 key 会明文出现在启动命令里注意别提交到 git。配置文件里的command和args字段和 Claude Desktop 的claude_desktop_config.json格式完全一致。这意味着你可以直接把已有的 MCP 配置粘过来改个文件名就能用迁移成本几乎为零。4. 验证请求用 curl 和 Python 确认服务真的通了启动只是第一步能不能返回正确结果才是关键。这一节用 curl 和 Python 两种方式验证并给出预期输出。4.1 curl 验证 fetch 接口先确认端口在监听curl -s http://localhost:8000/docs -o /dev/null -w %{http_code}\n返回200说明 HTTP 服务起来了。接着调/fetchcurl -X POST http://localhost:8000/fetch \ -H Content-Type: application/json \ -d { url: https://docs.cline.bot, max_length: 2000, start_index: 0, raw: false }如果加了--api-key需要补上请求头curl -X POST http://localhost:8000/fetch \ -H Content-Type: application/json \ -H Authorization: Bearer your-secret-key \ -d {url: https://docs.cline.bot, max_length: 2000, start_index: 0, raw: false}预期返回是一个 JSON里面包含抓取到的正文内容markdown 格式因为raw为 false。如果返回{detail: Not Found}说明路径不对检查是不是/fetch而不是/fetch/如果返回 401检查 api-key。4.2 Python 脚本验证curl 适合快速试但实际接入时用 Python 更顺手。下面这个脚本可以直接跑import requests def fetch_webpage(url, max_length10000, start_index0, rawFalse, api_keyNone): headers {Content-Type: application/json} if api_key: headers[Authorization] fBearer {api_key} try: response requests.post( http://localhost:8000/fetch, headersheaders, json{ url: url, max_length: max_length, start_index: start_index, raw: raw, }, timeout30, ) response.raise_for_status() return response.json() except Exception as e: return {error: str(e)} if __name__ __main__: result fetch_webpage(https://docs.cline.bot, max_length2000) print(result)跑通后你会看到返回的 JSON 里有一段 markdown 文本。到这里三种安装路径的验证结果应该完全一致——因为 mcpo 的行为不依赖它自己怎么装的只依赖挂载的 server。4.3 验证 time 接口多 server 场景如果你用了mcpo.json再验证一下 timecurl -X POST http://localhost:8000/time/get_current_time \ -H Content-Type: application/json \ -H Authorization: Bearer your-secret-key \ -d {timezone: Asia/Shanghai}注意 time server 的接口路径是/time/get_current_time因为 mcpo 会用 server 名做前缀避免多个 server 的接口冲突。这一点在/docs里能看得很清楚。提示不同 MCP server 暴露的工具名不同接口路径 /{server名}/{工具名}。具体有哪些工具看/docs最准不要凭记忆猜。5. 常见报错排查401、local proxy failed、reading choices、OAuth这一节是我踩过的坑合集按报错原文对照排查。报错一401 Unauthorized原因启动时加了--api-key但请求没带Authorization头或者 key 不匹配。排查先确认启动命令里的 key再确认请求头格式是Bearer key中间有一个空格。用 curl 时注意引号-H Authorization: Bearer your-secret-key整段要在一个引号里。报错二local proxy failed或connection refused原因mcpo 没能成功拉起 MCP server 子进程。常见于command写错比如写了uvx但机器上没装 uv或者 server 包名拼错。排查把--后面的命令单独在终端跑一遍比如直接执行uvx mcp-server-fetch看能不能起来。如果单独跑也失败问题在 server 不在 mcpo。报错三Error reading choices或 JSON 解析失败原因MCP server 启动后往 stdout 打印了非 JSON-RPC 的内容比如日志、警告污染了 stdio 通道。mcpo 期望 stdout 是纯 JSON-RPC。排查检查 server 是否有--verbose之类的参数被误开或者 server 版本有 bug。换一个 server 版本试试或者改用官方推荐的启动参数。报错四OAuth 相关报错原因部分远程 MCP server 需要 OAuth 鉴权而 mcpo 当前主要面向本地 stdio server。如果你挂的是需要 OAuth 的远程 server会卡在鉴权环节。排查确认你挂的 server 是不是本地 stdio 类型。mcpo 的定位是本地 stdio 转 HTTP远程 OAuth server 不在它的核心场景里。报错五端口被占用Address already in use原因8000 端口已被别的进程占用。排查lsof -i :8000找到进程或者直接换端口--port 8010。换端口后记得同步改 curl 和脚本里的地址。报错六ModuleNotFoundError: No module named mcpo原因conda 或 pip 路径下环境没激活或者装到了别的 Python。排查which mcpo看指向哪个环境pip show mcpo确认装在哪。conda 用户特别注意conda activate mcpo这一步别漏。注意排障时优先看 mcpo 启动日志它会打印子进程的 stderr。很多问题在日志里一眼就能看出来比盲猜快得多。6. 从验证到长期使用把 mcpo 接进你的工作流跑通之后mcpo 的价值才真正体现出来。它把 MCP 从「客户端专属能力」变成了「通用 HTTP 能力」这意味着任何能发 HTTP 请求的东西都能用上 MCP 工具。如果你只是偶尔验证模型能力可以直接用模型对话页面把 mcpo 暴露的接口当成一个普通 API 来调省去本地起服务的步骤。如果你要长期跑编码类 Agent或者需要稳定的 MCP 网关建议走 Coding Plan把 mcpo 作为本地网关固定下来配合mcpo.json管理多个 server。实际接入时我建议把 mcpo 的启动命令写进 systemd 或 supervisor而不是手动在终端跑。因为手动跑的进程一关终端就没了。一个简单的 systemd unit 大概长这样[Unit] Descriptionmcpo gateway Afternetwork.target [Service] ExecStart/home/user/.local/bin/uvx mcpo --config /home/user/mcpo.json --port 8000 --api-key your-secret-key Restartalways Useruser [Install] WantedBymulti-user.target这样开机自启、崩溃自动重启比手动维护省心。注意ExecStart里的路径要用绝对路径systemd 不读你的 shell PATH。另外mcpo.json里的 server 可以随时增删改完重启 mcpo 即可。我习惯把常用的 fetch、time、filesystem 都挂上统一走 8000 端口前端和后端都只认这一个地址管理成本很低。最后说一个实用技巧mcpo 的/docs是自动生成的但如果你想要更结构化的接口清单可以直接请求/openapi.json拿到完整的 OpenAPI 描述喂给代码生成器或者 Postman 都行。这个在对接多个 server 时特别有用不用一个个手写请求体。到这里uvx、conda、pip 三条路径你都走过了curl 和 Python 验证也通了报错对照表也备好了。剩下的就是按你的环境选一条把 mcpo 挂起来然后像访问普通接口一样使用 MCP 服务。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进