YARMCP:把仓库克隆到自己的服务器,让 Claude Code 读到最新源码
项目地址:sunnamed434/yarmcp(YARMCP,Yet Another Repo MCP,MIT 许可,Docker 部署)。
为什么需要它
问 AI「React Router v7 最新怎么处理数据加载」,得到的答案经常停留在六七个月前的 API。模型的训练数据有截止时间,网页搜索虽然能补,但慢,而且把整页内容塞进上下文很吃 token。
另一条路是用 GitHub MCP 直接查仓库,但 GitHub API 对未认证请求有 5000 次/小时的限额,多仓库、高频查询很容易撞上。撞上之后不是降速,而是直接失败。
第三个问题发生在本地:手上有 10 多个仓库,每台机器、每个项目各自 clone 一份,磁盘和时间都花在重复动作上。YARMCP 的做法是把仓库统一克隆到一台服务器,所有客户端共享同一份,每 6 小时自动更新,查询走本地存储,不经过 GitHub API。
它怎么工作
YARMCP 由两个容器组成:
- MCP Server:基于 FastMCP,监听 9742 端口,对外提供 MCP 协议接口,只读访问仓库。
- Updater:按 cron 执行
git克隆/拉取,负责把repos.yaml里的仓库同步到磁盘。
两者共享一个卷 /opt/yarmcp/repos,Updater 写,MCP Server 读。仓库用 shallow clone(--depth=1)减少磁盘占用;是否采用由部署配置决定,代价见后文。
对外暴露 8 个工具:
| 工具 | 作用 |
|---|---|
list_repos | 列出已配置仓库 |
get_repo_info | 仓库元信息 |
read_file | 读文件内容 |
search_code | 基于 ripgrep 搜索,带上下文行和耗时指标 |
list_files | 列目录,预览前 3 项 |
tree | 目录树,Unicode 输出,深度 1–5 |
get_readme | 取 README |
get_yarmcp_usage_guide | 返回使用说明 |
几个细节:请求的文件不存在时,会提示相似文件名;列目录只预览前 3 项,避免一次返回过多内容;路径会做校验,防止目录穿越,同时屏蔽敏感文件。
和同类方案相比,定位差异比较清楚:Context7 零配置但只有 2 个工具、私有仓库仅付费版;GitHub MCP 有 51 个工具但受 5000 次/小时限流;git-mcp 4 个工具、仅公开仓库;git-mcp-server 走 npm、27 个工具、JWT、无限流;本地 clone 则完全手动。YARMCP 选的是 Docker 自托管、8 个工具、支持私有仓库、查询不限流。
Docker Compose 部署
先建目录:
/opt/yarmcp/
├── config/ # repos.yaml,只读挂载
├── repos/ # 仓库存储
├── data/ # OAuth clients,可写
└── secrets/ssh/ # SSH 私钥
前提:Docker 20.10+、Compose 2+、openssl、10GB 以上磁盘、9742 端口可用。
.env.local 里几个关键变量:
BEARER_AUTH_TOKEN:用openssl rand -hex 32生成。MCP_BINDMOUNT_0..2:分别挂载 config(只读)、repos(只读)、data(可写,存 OAuth clients)。UPDATER_BINDMOUNT_0..2:config(只读)、repos(可写)、ssh 密钥目录。GITHUB_TOKEN:私有仓库时可选。UPDATE_SCHEDULE=0 */6 * * *:每 6 小时更新一次。OAUTH_CLIENT_ID/OAUTH_CLIENT_SECRET/OAUTH_JWT_SECRET/OAUTH_TOKEN_EXPIRY=2592000。
config/repos.yaml 示例:
repos:
- name: react
url: https://github.com/facebook/react.git
auto_update: true
- name: typescript
url: https://github.com/microsoft/TypeScript.git
branch: main
auto_update: true
description: TypeScript compiler and language service
字段是 name、url、branch、auto_update、description。branch 写错不会静默失败,会报错并列出可用分支。
私有仓库两种接法。PAT 直接放 GITHUB_TOKEN;或者用 SSH:
ssh-keygen -t ed25519 -f /opt/yarmcp/secrets/ssh/id_ed25519 -N ""
chmod 600/644
然后把 url 写成 git@github.com:user/repo.git。
启动并验证:
docker compose --env-file .env.local up -d
curl http://localhost:9742/health
健康检查返回 {"status":"ok"} 即正常。
两种认证方式,以及 JWT secret 的坑
- Bearer Token:适合 Claude Desktop、Claude Code CLI,也适合直接调 API。请求头带
Authorization: Bearer。 - OAuth 2.1 + PKCE:适合 Claude.ai 网页版、ChatGPT、Cursor,支持动态客户端注册。
关键点:OAUTH_JWT_SECRET 必须固定写死。如果每次启动随机生成,容器一重启所有已签发的 token 全部失效,网页端用户会被登出,需要重新走一遍 OAuth。这不是 bug,是配置问题,部署时把值写进 .env.local 并保存好。
客户端接入
Claude Code CLI 加一条命令:
claude mcp add --transport http yarmcp https://your-yarmcp-url.com --header "Authorization: Bearer your-auth-token-here"
验证:
claude mcp list
需要注意:改完 .claude.json 之后必须新开一个会话,或者重启 Claude Code。MCP 连接是按会话缓存的,旧会话不会重新读取配置,表现为「配置看着没问题,但工具就是不出来」。
让 AI 真的会用
工具接上了,不代表模型会主动调用。需要在项目里补上下文。
两层写法:
- 项目根目录
CLAUDE.md:写一段说明,指出训练数据可能过期,涉及具体仓库实现时先查 YARMCP,再列出 8 个工具名。这一段要短,常驻上下文。 yarmcp/CLAUDE.md:放详细工作流,比如先list_repos确认仓库名,再search_code定位,最后read_file读具体段落。这份不常驻,只在需要时读取,省 token。
主文件轻量、详细文件按需加载,比把所有规则堆在一处更划算。
取舍与坑
shallow clone 的代价。--depth=1 只保留最近一次提交,磁盘占用小、拉取快,但没有完整历史。git log、git blame、切到旧 tag 这些操作会受限。如果主要用途是读当前源码,这没问题;如果要做历史考古,就得在 Updater 侧调整克隆方式,或者单独维护一份完整克隆。
镜像版本要固定。生产环境用 :1.0.0,:edge 只用于测试。镜像同时发布在 Docker Hub 的 sunnamed434/yarmcp-mcp 和 ghcr.io/sunnamed434/yarmcp-mcp,选一个源固定住,避免不同机器拉到不同版本。
公网暴露必须上 HTTPS。Claude.ai 网页版、ChatGPT、Cursor 这类客户端要求 HTTPS,OAuth 流程本身也需要 TLS;只在本地用 Claude Code CLI 连 localhost 则不需要。两种常见做法:Cloudflare Tunnel,免费、自动证书、不用开防火墙端口;或者 Nginx + certbot。用 Cloudflare Tunnel 时,如果 tunnel 和 mcp 不在同一个 Docker network,需要加一个 override 文件把 mcp 服务接入 tunnel 网络,然后让 tunnel 指向 mcp:9742。
私有仓库凭证。PAT 放 GITHUB_TOKEN 最省事;SSH 方式注意私钥权限,chmod 600/644 按原样设置,权限过宽 git 会拒绝使用。
更新。仓库默认每 6 小时自动更新。想立刻拉一次,不必等 cron:
docker compose --env-file .env.local restart updater
重启 Updater 会触发一轮同步。