{"_id":"@kurvcygnus/github-fetcher","_rev":"5-b6a4dbad558636f514913f4b6c7cfb8c","name":"@kurvcygnus/github-fetcher","dist-tags":{"latest":"0.3.0"},"versions":{"0.0.0-stage":{"name":"@kurvcygnus/github-fetcher","version":"0.0.0-stage","_id":"@kurvcygnus/github-fetcher@0.0.0-stage","stub":true,"maintainers":[{"name":"kurvcygnus","email":"kurvcygnus@qq.com"}],"dist":{"shasum":"9870743681e43736ff1f617458fd251dc3a81728","tarball":"https://registry.npmjs.org/@kurvcygnus/github-fetcher/-/github-fetcher-0.0.0-stage.tgz","fileCount":2,"integrity":"sha512-apIc5WjLFaZWRGmwJXevGds+GlB52yBUAEmWiTjgzVDz8pCj2k0w4wx2e5q12yCxxqNnOltJfx6o9pnfch4jZg==","signatures":[{"sig":"MEQCIBbWMdbX4n3t859uoOCyMjguiv89mX3dLSnZC6hoM63EAiAxM2PMgkZOliIqxTqnFGMsDcb8rSEqLxEcHQcyyfXipg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":348},"_npmUser":{"name":"kurvcygnus","email":"kurvcygnus@qq.com"},"description":"Temporary package placeholder for staged publishing","directories":{}},"0.1.0":{"name":"@kurvcygnus/github-fetcher","version":"0.1.0","license":"MIT","_id":"@kurvcygnus/github-fetcher@0.1.0","maintainers":[{"name":"kurvcygnus","email":"kurvcygnus@qq.com"}],"bin":{"ghf":"dist/index.js","github-fetcher":"dist/index.js"},"dist":{"shasum":"d4afe06f85fe908342ab93986f2f7f7953b5d1a9","tarball":"https://registry.npmjs.org/@kurvcygnus/github-fetcher/-/github-fetcher-0.1.0.tgz","fileCount":49,"integrity":"sha512-QNQq4UHrD1Aira+3aaclUejFD8S5Og4mGRp1HIybGADu4GD783lqq0QBBi7iJy8Qt+hran44rJDg2uH9wug9Gw==","signatures":[{"sig":"MEUCIHb/c3EEnY41tajSYeMwHO1SuRT9vDCDBRXid4qi6dKdAiEA1c2o9NyTwqvQIxkzXQsQGmp5SDFVTnnPAn1pHN4ZoI0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIQDpJTDrkAt0i3mHkMVFxeZniCJIHqLJm1GD9e6rR6jAZAIgfomSPYcbMThlw+lO3i3uXsx5wmi7vgZumDj7NG/iFrc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":137221},"type":"module","engines":{"node":">=20.18"},"gitHead":"ccfb0e8172adced3d51d32609b908ad6041300b3","scripts":{"test":"vitest run","build":"tsc -p tsconfig.build.json","smoke":"node scripts/smoke.mjs","typecheck":"tsc -p tsconfig.json --noEmit","test:watch":"vitest","prepublishOnly":"npm run typecheck && npm test && npm run build"},"_npmUser":{"name":"kurvcygnus","email":"kurvcygnus@qq.com"},"_npmVersion":"11.20.0","description":"Read-only GitHub MCP server with token auth, proxy routing, and ETag caching","directories":{},"_nodeVersion":"26.13.0","dependencies":{"zod":"^4.6.7","undici":"^8.12.3","@modelcontextprotocol/sdk":"^1.32.2"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^5.0.5","typescript":"^7.0.2","@types/node":"^26.6.6"},"_npmOperationalInternal":{"tmp":"tmp/github-fetcher_0.1.0_1793431186597_0.4246134471404729","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@kurvcygnus/github-fetcher","version":"0.2.0","keywords":["mcp","mcp-server","model-context-protocol","github","github-mcp","read-only","proxy","rate-limit","etag-cache","china","typescript","cli","ai-agents"],"author":{"url":"https://github.com/KurvCygnus","name":"Kurv Cygnus"},"license":"MIT","_id":"@kurvcygnus/github-fetcher@0.2.0","maintainers":[{"name":"kurvcygnus","email":"kurvcygnus@qq.com"}],"homepage":"https://github.com/KurvCygnus/github-fetcher#readme","bugs":{"url":"https://github.com/KurvCygnus/github-fetcher/issues"},"bin":{"ghf":"dist/index.js","github-fetcher":"dist/index.js"},"dist":{"shasum":"750e1959c793fef8d6f1b84c0bb50c4dde6291f1","tarball":"https://registry.npmjs.org/@kurvcygnus/github-fetcher/-/github-fetcher-0.2.0.tgz","fileCount":49,"integrity":"sha512-K3+rgQAuobEyQ6AN6pXrYN0oXUeiCVS3mUt0dMa64vFK7lBtIcg2cMDt80jBWKTeBBLSHZ+YG0+kWF27t35edg==","signatures":[{"sig":"MEUCIQDnJ3Tp+qZAivjYWXG+JyxkL5r4EtQCj0jQyY4AcQow9gIgRaGcCgG0uSIzu8LSM/1voOtURHijvoM6Bei52eh+KZc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIQDl1YE8yRVzrfwehkqHgtBzG0Du1z5RW9DdWhtwUa8XIAIgOn9tj9F/dq0NixfuYjfzjCFefwTkzjEsd1KE9n9S4Fo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@kurvcygnus%2fgithub-fetcher@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":185167},"type":"module","engines":{"node":">=22.19.0"},"gitHead":"e1066e2b6acccd178d271fc76d30971d45e6a230","mcpName":"io.github.KurvCygnus/github-fetcher","scripts":{"test":"vitest run","build":"tsc -p tsconfig.build.json","smoke":"node scripts/smoke.mjs","typecheck":"tsc -p tsconfig.json --noEmit","test:watch":"vitest","verify:launch":"node scripts/verify-launch.mjs","prepublishOnly":"npm run typecheck && npm test && npm run build && npm run smoke && npm run verify:launch"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"5f6a78cc-ec8a-4fc1-b0ee-e82593a4fdae"}},"repository":{"url":"git+https://github.com/KurvCygnus/github-fetcher.git","type":"git"},"_npmVersion":"11.19.1","description":"Read-only GitHub MCP server for AI agents, built for unreliable networks (especially mainland China): 22 tools, token reuse, proxy fallback, ETag cache, one-command setup.","directories":{},"_nodeVersion":"24.22.1","dependencies":{"zod":"^4.6.7","undici":"^8.12.3","@modelcontextprotocol/sdk":"^1.32.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^5.0.5","typescript":"^7.0.2","@types/node":"^26.6.6"},"_npmOperationalInternal":{"tmp":"tmp/github-fetcher_0.2.0_1793579227972_0.8989275863932754","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@kurvcygnus/github-fetcher","version":"0.2.1","keywords":["mcp","mcp-server","model-context-protocol","github","github-mcp","read-only","proxy","rate-limit","etag-cache","china","typescript","cli","ai-agents"],"author":{"url":"https://github.com/KurvCygnus","name":"Kurv Cygnus"},"license":"MIT","_id":"@kurvcygnus/github-fetcher@0.2.1","maintainers":[{"name":"kurvcygnus","email":"kurvcygnus@qq.com"}],"homepage":"https://github.com/KurvCygnus/github-fetcher#readme","bugs":{"url":"https://github.com/KurvCygnus/github-fetcher/issues"},"bin":{"ghf":"dist/index.js","github-fetcher":"dist/index.js"},"dist":{"shasum":"7a01d00559767974b741696a587ba0b537b48b99","tarball":"https://registry.npmjs.org/@kurvcygnus/github-fetcher/-/github-fetcher-0.2.1.tgz","fileCount":49,"integrity":"sha512-QpMYkIUC69a6LVJ4iqEjQ9r2wYa+fQN/966LQfLdJwRbZwi2ov3B3MGiLewadyN/xItJHhL5AzsE1GAECiY03Q==","signatures":[{"sig":"MEUCIC8ftz71JlJHyTi15ZGXPKOr5PUw3QqLqtxN4XHx/Va1AiEAzwNaP3Q3B7ydl/dLyx9YUyIz3CRaArk+KMhWor8qUUY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEQCIE9qcmNnIlmOZT/9lg/BjGvycKQAqltDS4ZTc5lPEp7tAiAcLHmP73HuoVoLiZX2YcUVgNYP/8H0wOg9IQPeAcPeTw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@kurvcygnus%2fgithub-fetcher@0.2.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":199093},"type":"module","engines":{"node":">=22.19.0"},"gitHead":"e4d1424592357eac4b5c6b0bfaa41da8c0f95fd7","mcpName":"io.github.KurvCygnus/github-fetcher","scripts":{"test":"vitest run","build":"tsc -p tsconfig.build.json","smoke":"node scripts/smoke.mjs","typecheck":"tsc -p tsconfig.json --noEmit","test:watch":"vitest","verify:launch":"node scripts/verify-launch.mjs","prepublishOnly":"npm run typecheck && npm test && npm run build && npm run smoke && npm run verify:launch"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"5f6a78cc-ec8a-4fc1-b0ee-e82593a4fdae"}},"repository":{"url":"git+https://github.com/KurvCygnus/github-fetcher.git","type":"git"},"_npmVersion":"11.19.1","description":"Read-only GitHub MCP server for AI agents, built for unreliable networks (especially mainland China): 22 tools, token reuse, proxy fallback, ETag cache, one-command setup.","directories":{},"_nodeVersion":"24.22.2","dependencies":{"zod":"^4.6.7","undici":"^8.12.3","@modelcontextprotocol/sdk":"^1.32.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^5.0.5","typescript":"^7.0.2","@types/node":"^26.6.6"},"_npmOperationalInternal":{"tmp":"tmp/github-fetcher_0.2.1_1794015623465_0.5532066307309287","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"_id":"@kurvcygnus/github-fetcher@0.3.0","bin":{"ghf":"dist/index.js","github-fetcher":"dist/index.js"},"bugs":{"url":"https://github.com/KurvCygnus/github-fetcher/issues"},"dist":{"shasum":"51ff757df8ca648a2dafb130be96937d73a7465b","tarball":"https://registry.npmjs.org/@kurvcygnus/github-fetcher/-/github-fetcher-0.3.0.tgz","fileCount":58,"integrity":"sha512-Rl66WkjZK+3HLj0Jg/x6o8+owFVV0G4QX9xGwevhlGc9oz4rZxZ2L57Bu1lVZhYNU7kcQ/VC5cWR5Lz8omVAiw==","signatures":[{"sig":"MEYCIQCPFWzbd4jaWja7aJg5pC3E0PvWhvPW3BfURcRn6IYxYAIhAJ8c7Ffqv2OJ/B9t57s3G7manj6ylCYPpf5r3gB5wIrx","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIAYfbKIbnn+BhMqge5Q/69yz6p39IpzMOQROguAw+F47AiBcrsIMebmL2JUQjkaC0Avo35hwAqWjWYj7h7VN1gq/JQ=="}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@kurvcygnus%2fgithub-fetcher@0.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":287009},"name":"@kurvcygnus/github-fetcher","type":"module","author":{"url":"https://github.com/KurvCygnus","name":"Kurv Cygnus"},"engines":{"node":">=22.19.0"},"gitHead":"93711f77ad2a4c15ffe1a070608014c3aaf06302","license":"MIT","mcpName":"io.github.KurvCygnus/github-fetcher","scripts":{"test":"vitest run","build":"tsc -p tsconfig.build.json","smoke":"node scripts/smoke.mjs","typecheck":"tsc -p tsconfig.json --noEmit","test:watch":"vitest","verify:launch":"node scripts/verify-launch.mjs","prepublishOnly":"npm run typecheck && npm test && npm run build && npm run smoke && npm run verify:launch"},"version":"0.3.0","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"5f6a78cc-ec8a-4fc1-b0ee-e82593a4fdae"}},"homepage":"https://github.com/KurvCygnus/github-fetcher#readme","keywords":["mcp","mcp-server","model-context-protocol","github","github-mcp","read-only","proxy","rate-limit","etag-cache","china","typescript","cli","ai-agents"],"repository":{"url":"git+https://github.com/KurvCygnus/github-fetcher.git","type":"git"},"_npmVersion":"11.19.1","description":"Read-only GitHub MCP server for AI agents, built for unreliable networks (especially mainland China): 34 tools, token reuse, proxy fallback, ETag cache, one-command setup.","directories":{},"maintainers":[{"name":"kurvcygnus","email":"kurvcygnus@qq.com"}],"_nodeVersion":"24.22.2","dependencies":{"zod":"^4.6.7","undici":"^8.12.3","@modelcontextprotocol/sdk":"^1.32.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^5.0.5","typescript":"^7.0.2","@types/node":"^26.6.6"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/github-fetcher_0.3.0_1794616887000_0.8340384856209252"}}},"time":{"created":"2026-10-31T07:18:51.185Z","modified":"2026-11-14T00:41:27.432Z","0.0.0-stage":"2026-10-31T07:18:51.185Z","0.1.0":"2026-10-31T07:19:46.704Z","0.2.0":"2026-11-02T00:27:08.084Z","0.2.1":"2026-11-07T01:40:23.594Z","0.3.0":"2026-11-14T00:41:27.115Z"},"bugs":{"url":"https://github.com/KurvCygnus/github-fetcher/issues"},"author":{"url":"https://github.com/KurvCygnus","name":"Kurv Cygnus"},"license":"MIT","homepage":"https://github.com/KurvCygnus/github-fetcher#readme","keywords":["mcp","mcp-server","model-context-protocol","github","github-mcp","read-only","proxy","rate-limit","etag-cache","china","typescript","cli","ai-agents"],"repository":{"url":"git+https://github.com/KurvCygnus/github-fetcher.git","type":"git"},"description":"Read-only GitHub MCP server for AI agents, built for unreliable networks (especially mainland China): 34 tools, token reuse, proxy fallback, ETag cache, one-command setup.","maintainers":[{"name":"kurvcygnus","email":"kurvcygnus@qq.com"}],"readme":"[English](https://github.com/KurvCygnus/github-fetcher/blob/main/README.md) | [简体中文](https://github.com/KurvCygnus/github-fetcher/blob/main/README.zh-CN.md)\n\n# @kurvcygnus/github-fetcher\n\n[![npm version](https://img.shields.io/npm/v/@kurvcygnus/github-fetcher.svg)](https://www.npmjs.com/package/@kurvcygnus/github-fetcher)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)\n![Node](https://img.shields.io/badge/node-%E2%89%A522.19-blue)\n[![M8ven Trust](https://m8ven.ai/badge/mcp/kurvcygnus/github-fetcher)](https://m8ven.ai/mcp/kurvcygnus/github-fetcher?s=readme)\n\nAI 编码 agent 的日常工作几乎全在读 GitHub, 而直连 GitHub 的读取路径既不安全也不省钱, 更谈不上可靠. `@kurvcygnus/github-fetcher` 是一个只读的 GitHub MCP server: 它通过 stdio 暴露 34 个工具, 走 GitHub REST API 读取仓库, 用户, 文件, commit, issue, pull request, release, 仓库健康与活跃度, 仓库与 ref 横向对比, 自身的工具能力索引与自检, Actions 和搜索结果. 它复用你已有的凭据, 能绕开不可靠的网络, 把响应缓存到磁盘, 并且无法修改任何东西.\n\n## 为中国大陆网络而构建\n\nGitHub 匿名 REST 配额是**每个出口 IP 每小时 60 次**, 由机器上的所有工具共享. `raw.githubusercontent.com` 经常被 DNS 污染, 缺少 `User-Agent` 的裸 HTTP 请求会直接收到 403. 这个 server 围绕这些故障设计:\n\n- 复用 git credential store 里已有的 token; `GITHUB_TOKEN`/`GH_TOKEN` 优先.\n- API 先直连, 失败后走代理. 读取文件时先用 contents API 取元数据和内联内容, GitHub 不返回内联正文时再经代理从 `raw.githubusercontent.com` 取.\n- 每条路由对传输失败重试两次, 15 秒以内的限流窗口自动等待, 更长窗口报告精确重置时间.\n- 成功响应带 ETag 缓存到磁盘, 重复读取不消耗配额.\n\n不在中国大陆时默认配置同样可用; 设置 `GITHUB_PROXY_MODE=direct` 可以完全跳过代理尝试.\n\n## 问题所在\n\nAgent 访问 GitHub 的频率极高 (读目录树, 读文件, 查 issue, 查 pull request, 看 CI), 而直连访问会以各种方式失败, 浪费所有人的时间:\n\n- 匿名 REST 访问的配额是每 IP 每小时 60 次请求, 由机器上的所有工具共享.\n- GitHub 会用 403 拒绝没有 `User-Agent` 头的请求, 裸脚本因此报错, 且原因很难察觉.\n- 在 `raw.githubusercontent.com` 被 DNS 污染的网络 (中国大陆很常见) 上, raw 文件抓取直接失败.\n- 为了只读工作给 agent 配一个带写权限的 token 是不必要的风险.\n- 没有共享缓存时, 每个工具都会重复抓取同样的内容.\n\n## 你能得到什么\n\n- 通过 stdio 暴露的 34 个只读 MCP 工具: 仓库, 用户, 目录树, 文件, commit, 分支, issue, pull request, release, 仓库健康与活跃度, 仓库与 ref 横向对比, 用户主页, star 记录和公开事件, 工具能力索引与自检, 搜索, Actions 运行记录, 以及一个通用的只读 GET 兜底工具.\n- 复用 git credential store 里的 token: 服务器读取你的 git credential helper 已保存的凭据 (例如 GitHub CLI 或 Git Credential Manager), 不会在磁盘上新增秘密. `GITHUB_TOKEN` 优先; 完全没有 token 时退回匿名访问 (每小时 60 次请求).\n- 为恶劣网络设计的代理路由: `auto` 模式下 API 请求先直连, `raw.githubusercontent.com` 请求先走代理; 可用 `always` 和 `direct` 覆盖. 代理候选按顺序尝试: `GITHUB_PROXY`, `HTTPS_PROXY`, `HTTP_PROXY`, 然后是 git 的代理配置.\n- 能在重启后存活的 ETag 磁盘缓存: 未超过 TTL 的条目直接本地返回, 过期条目用 `If-None-Match`/`If-Modified-Since` 廉价重验证, 收到 304 就复用已存的响应体, 不再重新下载.\n- 速率限制处理: 短窗口 (15 秒以内) 自动等待一次后重试; 长窗口则报错并给出精确的重置时间.\n- 内置客户端配置: `github-fetcher init` 为八种客户端写入 MCP 条目, 改动前先备份文件; `github-fetcher remove` 可以撤销.\n\n## 与官方 server 的对比\n\n官方 `github/github-mcp-server` 适合需要通用 GitHub MCP, 写工具或远程托管的场景. 这个项目做的是更窄的取舍:\n\n| 能力 | GitHub Fetcher | 官方 github-mcp-server |\n| --- | --- | --- |\n| 始终只读 | 是 | 只读模式需显式开启; 默认提供写工具 |\n| 本地 stdio | 是 | 是 |\n| 远程托管 | 否 | 是 |\n| 认证 | 复用 git credential; `GITHUB_TOKEN`/`GH_TOKEN` | PAT, OAuth 或 GitHub App |\n| 中国大陆 raw/代理回退 | 是 | 没有针对中国大陆的路由 |\n| ETag 磁盘缓存 | 是 | 没有内置 ETag 缓存 |\n\n官方 server 的信息按 2026-11-01 的 README 核对.\n\n## 快速开始\n\n最快的方式是一条命令:\n\n    npx -y @kurvcygnus/github-fetcher@latest init\n\n`init` 会检测支持的客户端, 复用已经指向本包的现有条目, 并在修改每个文件前备份. 下面的手动配置是参考形式.\n\n把对应的条目加进你客户端的 MCP 配置.\n\nOpenCode:\n\n```json\n{\n  \"mcp\": {\n    \"github\": {\n      \"type\": \"local\",\n      \"command\": [\"npx\", \"-y\", \"@kurvcygnus/github-fetcher@latest\"],\n      \"enabled\": true\n    }\n  }\n}\n```\n\nClaude Desktop, 加到 `claude_desktop_config.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"github\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@kurvcygnus/github-fetcher@latest\"],\n      \"env\": { \"GITHUB_TOKEN\": \"ghp_your_token\" }\n    }\n  }\n}\n```\n\nCursor, 加到 `.cursor/mcp.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"github\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@kurvcygnus/github-fetcher@latest\"]\n    }\n  }\n}\n```\n\nVS Code, 加到 `.vscode/mcp.json`:\n\n```json\n{\n  \"servers\": {\n    \"github\": {\n      \"type\": \"stdio\",\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@kurvcygnus/github-fetcher@latest\"]\n    }\n  }\n}\n```\n\n四个客户端运行的是同一条命令; 在客户端支持的位置加一个带 `GITHUB_TOKEN` 的 `env` 块, 或者在启动客户端的 shell 里 export 这个变量.\n\n## CLI\n\n每个工具同时也是同一个二进制的子命令 - 适合 shell 管道, 也适合在没有 agent 参与的情况下查看数据.\n\n全局安装 (`npm i -g @kurvcygnus/github-fetcher`) 后会同时注册短别名 `ghf`, `ghf releases anomalyco/opencode --per-page 5` 同样可用.\n\n    github-fetcher releases anomalyco/opencode --per-page 5 | jq -r \".items[].tag_name\"\n    github-fetcher health anthropics/claude-code --window-days 30 --pretty\n    github-fetcher file KurvCygnus/github-fetcher README.md\n\n- 直接运行 `github-fetcher` (或 `serve`) 启动 MCP stdio server; 带子命令则走 CLI.\n- stdout 输出紧凑 JSON (`--pretty` 输出缩进格式, 适合人类阅读); 错误以 JSON 写到 stderr, 退出码: 2 用法, 3 速率限制, 4 认证, 5 未找到, 6 网络, 7 上游.\n- `--version` (`-v`) 输出包版本; `--help` 列出子命令, `github-fetcher <subcommand> --help` 显示单个工具的参数.\n- `init` 和 `remove` 管理 MCP 客户端配置, 不查询 GitHub; 详见下文.\n\n### `init`\n\n`github-fetcher init` 把 `github` 条目写入你的 MCP 客户端配置. 在终端里它会打开选择菜单 (空格切换, `a` 全选或全不选, 回车确认, Esc 取消; 配置文件已存在的客户端默认选中). 脚本里改用参数:\n\n    github-fetcher init --dry-run\n    github-fetcher init --client opencode,cursor\n    github-fetcher init --client opencode --name github\n\n- `--client id,id` 指定客户端 (逗号分隔); `--all` 覆盖所有适配器; `--dry-run` 只报告 `would-create`, `would-update` 或 `would-skip`, 不改动文件; `--name` 设置条目名 (默认 `github`). 只带 `--dry-run` 而不给 `--client` 或 `--all` 时同样会覆盖所有适配器, 检测不到的客户端报告 `not-installed`, 不会报告 `would-create`.\n- 默认情况下, `init` 会复用 `command` 或 `args` 已指向本包的现有条目, 即使它的键是 `github-fetcher` 这样的旧名称, 也会原地更新该条目而不是新增重复项. `--name` 会关闭解析并强制使用指定的键. 已经属于其他服务器的键绝不会被覆盖: 如果没有本包拥有的旧键可复用, 客户端会报告 `skipped`, 可用 `--name` 选择其他键.\n- 只有在显式选中某个客户端时才会新建缺失的配置文件 (`--client`, 或在菜单里勾选); 未被选中且检测不到的客户端报告 `not-installed`, 不会为它写任何文件. 单独使用 `--all` 覆盖所有已检测到的客户端, 其余报告为 `not-installed`.\n- 改动已有文件前都会在同目录备份为 `<path>.bak.<timestamp>`; 备份写完才会改动目标文件; 同一秒内重跑会改用数字后缀, 不覆盖先前的备份.\n- stdout 输出一个 JSON 数组, 每个客户端一个对象 (`created`, `updated`, `skipped`, `not-installed`, `would-*`, `error`), 并包含解析后的条目 `name`; 解析到的认证方式 (`token` 或 `anonymous`) 写到 stderr. 取消菜单会输出 `cancelled` 并以 0 退出.\n\n支持的客户端: `opencode`, `claude-desktop`, `claude-code`, `cursor`, `vscode`, `gemini`, `windsurf`, `codex`.\n\n### `remove`\n\n`github-fetcher remove` 删除这个条目. 不带参数时只列出配置里确实含有该条目的客户端, 并全部预选; `--client id,id` 和 `--all` 跳过菜单, `--name` 指定要删除哪个条目. 不带参数时 `remove` 需要交互式终端来显示菜单, stdin 不是 TTY 时会以 2 退出; 无人值守时用 `--client` 或 `--all`.\n\n- 只处理已存在的条目: 没有配置文件的客户端报告 `not-installed`, 配置里没有该条目的客户端报告 `skipped`, 这两种情况都不会重写文件. 属于其他服务器的键绝不会被删除; 这种情况报告 `skipped` 并附带说明.\n- 删除前会把当前文件备份, 命名与 `init` 相同 (`<path>.bak.<timestamp>`).\n- 删除最后一个条目后, 如果配置文件会变成空文件, 该文件会被删除; 结果中标记 `deleted: true`, 备份保留下来作为恢复副本. 存在顶层键或同级条目时文件会保留.\n- ZCode 有意不支持: 它不是适配器, `init` 不会提供它, `remove` 也不会读写它的配置.\n\n## 限制\n\n- 从构造上只读: 不支持写操作, GraphQL, clone 或 archive 工具.\n- 34 个工具, 不是完整的 GitHub API 客户端; 专用工具没覆盖的只读 REST 端点用 `github_api_get`.\n- 匿名访问每小时 60 次; 代码搜索需要 token.\n- 不内置代理; 请自备 HTTP(S) 代理.\n- 缓存会把响应体写到磁盘; 共享账号无法接受这一点时不要使用.\n\n## 安全模型\n\n- 从构造上只读: 服务器只发出 GET 请求; 不支持 GraphQL 和写操作, `github_api_get` 接受任意只读 GET 路径, 主机固定为 api.github.com.\n- 只会联系两个主机: API 请求只到 `api.github.com`, raw 字节只来自 `raw.githubusercontent.com`.\n- 重定向被限制在 GitHub origin 之内: 离开原主机的重定向会被拒绝.\n- Token 只出现在 Authorization 头里, 绝不进入日志, 错误信息, 工具输出或缓存; 缓存键使用 token 的 SHA-256 哈希.\n- 缓存文件以 0600 权限写入 (尽力而为), 位置在用户自己的缓存目录下.\n- 身份默认私密: `github_rate_limit` 只报告配额. 设置 `GITHUB_MCP_EXPOSE_IDENTITY` 后才追加 `authenticated_as` 和 `scopes`; 无论开关, token 和它的指纹都不会返回.\n\n## 工作原理\n\n每次工具调用都会变成一次针对 GitHub REST API 的 GET 请求. 工具结果只走单通道: 响应携带 `structuredContent`, `content` 数组恒为空, 只认文本的旧客户端会看到空负载, 而不是重复的文本渲染. 请求先经过缓存层: 未超过端点 TTL 的条目直接本地返回, 过期条目用 ETag 重验证; 只有成功的响应才会进缓存, 所以 not-found 响应永远不会被缓存, 每次都以 `not_found` 错误呈现. 除 `github_rate_limit` 外, 每个读取 API 的工具都接受 `fresh` 选项, 为单次请求绕过缓存; `github_api_get` 的响应与其他工具一样进缓存; `github_capabilities` 不接受输入. 每个走网络的响应都以 `meta: {cache, age_s, quota}` 结尾; 缓存命中时, quota 反映的是产生缓存体的那次请求, `age_s` 表示缓存体已存在多久. 缓存之下, HTTP 层为每个请求选择路由, 重试传输失败, 自动等过短的速率限制窗口, 并在 token 被拒时刷新一次再放弃.\n\n`stdio MCP → tool registry → GithubClient (ETag cache) → HttpClient (routing, proxy, retries) → GitHub REST`\n\n## 配置\n\n| 变量 | 默认值 | 说明 |\n| --- | --- | --- |\n| `GITHUB_TOKEN` | 未设置 | GitHub token, 最先尝试. |\n| `GH_TOKEN` | 未设置 | 备用 token, 在 `GITHUB_TOKEN` 未设置时使用. |\n| `GITHUB_PROXY` | 未设置 | 专用代理 URL (`http://` 或 `https://`), 优先于通用代理变量. |\n| `GITHUB_PROXY_MODE` | `auto` | 代理路由模式: `auto`, `always` 或 `direct`. |\n| `HTTPS_PROXY` | 未设置 | 通用 HTTPS 代理兜底. |\n| `HTTP_PROXY` | 未设置 | 通用 HTTP 代理兜底, 在 `HTTPS_PROXY` 之后尝试. |\n| `GITHUB_CACHE` | `disk` | 缓存后端: `disk`, `memory` 或 `off`. |\n| `GITHUB_CACHE_DIR` | 平台缓存目录 | 覆盖磁盘缓存位置. |\n| `GITHUB_MCP_TIMEOUT_MS` | `30000` | 单次请求超时 (毫秒). |\n| `GITHUB_MCP_MAX_FILE_BYTES` | `262144` | 响应文本的 UTF-8 字节上限, 超出即截断 (单次调用可用 `max_bytes` 进一步收紧). |\n| `GITHUB_MCP_EXPOSE_IDENTITY` | 未设置 | 显式开启: `1`/`true`/`yes`/`on` 让 `github_rate_limit` 和 `github_doctor` 追加 `authenticated_as` 和 `scopes`; 其他取值保持关闭. |\n| `GITHUB_MCP_LOG` | `error` | 会被解析和校验; 目前未使用 - 警告始终写入 stderr, 与级别无关. |\n\n无效值会被忽略, 在 stderr 上给出警告, 然后使用默认值.\n\n## 工具一览\n\nAgent 会通过 `tools/list` 拿到每个工具的完整输入 schema; `github_capabilities` 的每行还带从同一 schema 派生的输入行 (名字, 可选性, 说明), 索引与 schema 不会漂移. 分组与 `github_capabilities` 返回的索引一致.\n\n- Meta: `github_capabilities` (一行摘要带 `Envelope:` 标签, 外加每个工具派生的 `input` 行与输入签名; `group` 可把索引过滤到单个分组), `github_rate_limit` (默认只投影 core 和 search; `all: true` 展开全部资源桶; 通过 `GITHUB_MCP_EXPOSE_IDENTITY` 显式开启身份输出), `github_api_get` (对 api.github.com 的任意只读 GET; `path` 可带查询串, `query` 接受标量值; `pagination` 携带 Link 头的翻页 URL; 不支持 GraphQL), `github_doctor` (一屏自检: token 是否存在, 可选的身份输出, 代理可达性, 缓存状态和配额)\n- Repository: `github_repo` (报告 `visibility`), `github_repos` (一次并发批量查询最多 20 个仓库, 每项独立报告 ok 或 error), `github_compare_refs` (ahead/behind 计数, 总 commit 数, 前 100 个变更文件, patch 截断到 4000 字符), `github_branches`, `github_languages` (按语言统计字节数, 降序排列), `github_contributors` (login, 账号类型和贡献次数), `github_repo_activity` (窗口期内的 pull, issue, commit, 活跃贡献者和 release 计数, 只消耗 core 配额, 附截断标记)\n- User: `github_user` (单个用户主页: name, type, bio, company, location, blog 和粉丝数), `github_user_repos` (用户的仓库列表, 采用统一的投影字段; `type` 默认为 `owner`; 默认丢弃 fork, `include_forks: true` 保留; `min_stars` 设定 star 下限; `sort: stars` 时聚合最多 10 页, 每页 100 条, 并按 star 数降序排列, 达到上限时报告 `gather_truncated`), `github_user_starred` (用户 star 过的仓库, 附 `starred_at` 时间), `github_user_events` (仅公开事件)\n- Files: `github_tree` (默认最多返回 `max_entries` 1000 条, 附 `total_entries`, 类型 `summary` 和 `api_truncated`; 递归列目录超时会降级为顶层条目并标记 `degraded: true`; `summary_only: true` 省略 `entries`, 改为返回 `total_bytes` 和 `top_dirs`), `github_file` (支持从 1 开始的 `start_line`/`end_line` 行区间, 以及单次调用的 `max_bytes` 预算), `github_readme` (经 GitHub 的 readme 端点取仓库 README, 自动解析默认命名变体; 需要特定路径时用 `github_file`)\n- History: `github_commits` (`max_message_chars` 截断每条 commit message)\n- Collaboration: `github_issue`, `github_issues` (接受 `since`, 一个 ISO 8601 时间戳, 按更新时间过滤), `github_pr`, `github_prs`\n- Releases: `github_release` (最新或指定 tag 的 release, 含资产与下载链接; GitHub 提供时资产带 `digest`), `github_releases`\n- Snapshot: `github_repo_health`, `github_compare_repos` (一次调用对最多 20 个仓库做限速的横向对比)\n- Search: `github_search_repos` (每条结果报告 `private`, `visibility`, `topics`, `license`, `created_at` 和 `pushed_at`; `public_only` 默认为 `true` — 传 `false` 才包含私有仓库 — 在本地过滤并报告 `filtered_count`, `total_count` 保持 GitHub 的未过滤总数), `github_search_code` (需要 token), `github_search_issues`, `github_search_users` (按 GitHub 查询语法搜索用户 — `location:`, `language:`, `followers:>=N` 等限定符; 返回精简档案, 详情见 `github_user`)\n- Actions: `github_workflows`, `github_workflow_runs`, `github_workflow_run`\n\n## 故障排查\n\n| 现象 | 含义 | 怎么办 |\n| --- | --- | --- |\n| 403/429, `rate_limited` | 当前凭据的配额用尽 | 调用 `github_rate_limit`, 等待它报告的重置时间 |\n| raw 或 API 主机 DNS/连接失败 | 这个网络到 GitHub 的链路不可靠 | 设置 `GITHUB_PROXY` 或 `HTTPS_PROXY` |\n| `auth_missing` | 该端点需要 token; `github_search_code` 必须有 token | 设置 `GITHUB_TOKEN` 或 `GH_TOKEN` |\n| `not_found` | 仓库, ref, 路径或 release tag 不存在, 或是私有仓库 | 检查 `owner/name` 和 ref |\n| TLS 错误 | TLS 拦截 (企业 MITM) 或代理链路损坏 | 把 `GITHUB_PROXY` 或 `HTTPS_PROXY` 指向可用的代理, 或尝试 `GITHUB_PROXY_MODE=direct` |\n\n## 开发\n\n- `npm test`: 运行测试套件\n- `npm run typecheck`: 运行严格 TypeScript 检查\n- `npm run build`: 编译生产构建\n- `npm run smoke`: 通过 stdio 启动构建产物, 断言 34 个工具全部列出\n- `GITHUB_SMOKE=1 npm test`: 带着 `GITHUB_SMOKE` 运行测试套件; 该变量目前是预留变量, 未被使用\n\n## 许可证\n\nMIT. Copyright (c) 2026 Kurv Cygnus. 详见 [LICENSE](LICENSE).\n","readmeFilename":"README.zh-CN.md"}