github-readonly-mcp

github-readonly-mcp

A read-only MCP server for GitHub that exposes 7 tools (list repos, commits, branches, pull requests, issues, file contents, search) via a Cloudflare Worker, using a single password for authorization and a fine-grained GitHub token for access control.

Category
访问服务器

README

github-readonly-mcp

A small, self-hosted MCP connector that gives Claude read-only access to your GitHub repos — including private ones. Which repos are reachable is determined entirely by the scope of the GitHub token you give it (see below), not by a list in the code. It exists because:

  • Cowork's cloud sandbox blocks direct calls to api.github.com (you'll get 403 GitHub access to this repository is not enabled for this session).
  • GitHub's own official remote MCP server (api.githubcopilot.com/mcp) needs a GitHub App OAuth flow that Claude's custom-connector UI doesn't support (it only takes an OAuth Client ID/Secret, not GitHub's flow).
  • Broader third-party connectors grant read and write access, which is more than this needs.

This connector is deliberately narrow: it runs on Cloudflare Workers (free tier is plenty), gates access behind a single shared password + OAuth 2.1, and exposes exactly 7 read-only tools. The reachable repos are whatever the GitHub token can read — scope the token, not the code. There is no write path in the code at all — no create/update/delete/merge function exists anywhere, so it can't modify GitHub no matter what a model asks it to do.

Tools it exposes: list_repos, list_commits, list_branches, list_pull_requests, list_issues, get_file_contents, search_code.

Everything below has already been tested locally end-to-end (dynamic client registration → password-gated authorize → token exchange → authenticated MCP session → tools/list → tools/call, including a real 401 from GitHub with a placeholder token and a correctly-rejected malformed repo name). What's left is deploying it for real and connecting it to Claude.

1. Prerequisites

  • Node.js 18+ and npm (already used to build this).
  • A free Cloudflare account: https://dash.cloudflare.com/sign-up
  • A GitHub fine-grained personal access token, read-only. This token is the connector's only access boundary — it can read exactly the repos the token can, so scope it deliberately: https://github.com/settings/personal-access-tokens/new
    • Repository access: "Only select repositories" → pick exactly the repos you want reachable (or "All repositories" if you truly want full read access).
    • Permissions: Contents (Read-only), Metadata (Read-only), Pull requests (Read-only), Issues (Read-only). Don't grant anything else.

2. Install dependencies

cd github-readonly-mcp
npm install

3. Log in to Cloudflare

npx wrangler login

This opens a browser to authorize the Wrangler CLI against your Cloudflare account.

4. Create the KV namespace

@cloudflare/workers-oauth-provider needs a KV namespace to store OAuth grants and token hashes (no secrets are stored in plaintext — just hashes).

npx wrangler kv namespace create OAUTH_KV

This prints something like:

[[kv_namespaces]]
binding = "OAUTH_KV"
id = "abcd1234...."

Copy that id value into wrangler.jsonc, replacing the placeholder:

"kv_namespaces": [
  { "binding": "OAUTH_KV", "id": "abcd1234...." }  // <- your real id here
],

5. Set the two secrets

These are never written to wrangler.jsonc or committed anywhere — they live only in Cloudflare's encrypted secret store.

npx wrangler secret put GITHUB_PAT
# paste your fine-grained GitHub PAT when prompted

npx wrangler secret put CONNECTOR_PASSWORD
# choose a real password — this is the one gate between the internet and
# your GitHub read tools, so make it a proper passphrase, not "test123"

6. (No allow-list to configure)

There is no repo allow-list in this connector: it can read any repo the GITHUB_PAT grants. The token you scoped in step 1 is the boundary — if you want to narrow what's reachable, narrow the token's repository access, then rotate it (step 5 / "Updating later"). The connector still validates that each repo argument is a well-formed "owner/name" before calling GitHub, so malformed values can't be used to reach unintended API paths.

7. Deploy

npm run deploy

Wrangler will print a URL like https://github-readonly-mcp.<your-subdomain>.workers.dev. That's your connector's base URL — save it, you'll need it in the next step.

Sanity-check it:

curl https://github-readonly-mcp.<your-subdomain>.workers.dev/health
# -> "github-readonly-mcp is running. Connect it to Claude as a custom connector."

8. Add it to Claude as a custom connector

  1. In Claude, go to Settings → Connectors → Add custom connector.
  2. Name it something like "GitHub (read-only)".
  3. Remote MCP server URL: https://github-readonly-mcp.<your-subdomain>.workers.dev/mcp
  4. Save. Claude will kick off the OAuth flow: it hits /register automatically, then opens the /authorize page in step 9.

9. Authorize it

You'll land on a small dark-themed page titled "Authorize Claude." Enter the CONNECTOR_PASSWORD you set in step 5 and submit. That's the only manual gate — after this, Claude holds a scoped OAuth token and can call the 7 tools directly.

10. Verify it works

Ask Claude something like "using the GitHub read-only connector, list recent commits on jpalvarezb/bonafe" and confirm it returns real commit data (not a 401). You can also ask it to list_repos to see everything the token can reach.

11. Tell me once it's connected

Once steps 1–10 are done, let me know — I'll delete and recreate the "Daily Schedule Review (cloud)" scheduled task so its connector list picks up this new one, then we'll run one more supervised test exercising list_commits / list_pull_requests / list_issues against your real repos with real credentials, to confirm the whole chain works end-to-end.

Updating later

Changed a tool, re-scoped the repos, or rotated the PAT? (Re-scoping the reachable repos now means issuing a new fine-grained token and rotating it — there's no list in the code to edit.)

npm run deploy          # code/config changes
npx wrangler secret put GITHUB_PAT   # to rotate the token

No need to touch the OAuth setup again — Claude's existing connection keeps working across redeploys as long as the KV namespace and secrets stay in place.

Security notes

  • Read-only by construction, not by policy. There is no function in src/github.ts (or anywhere else) that issues a POST/PATCH/PUT/DELETE to GitHub. A prompt injection or a misbehaving model can't make this connector write to GitHub — the code to do so doesn't exist.
  • The GitHub token is the access boundary. There is no repo allow-list; the connector can read exactly what the GITHUB_PAT can. Use a fine-grained, read-only token scoped to just the repos you want reachable. The connector does validate that each repo argument is a well-formed "owner/name" before calling GitHub, which prevents malformed values from being smuggled into unintended API paths — but that is an injection guard, not access control. Anyone who has the connector password can read every repo the token can, so keep both the token narrow and the password strong.
  • Secrets never touch source control. GITHUB_PAT and CONNECTOR_PASSWORD are Cloudflare secrets (wrangler secret put), not vars in wrangler.jsonc, and .dev.vars is gitignored.
  • One shared password, by design. The /authorize gate uses a single password rather than a full user-account system because this connector has exactly one legitimate caller — you, connecting your own Claude to your own GitHub data. If you ever want to share it with others, that authorize.ts gate is the one place to revisit.

Project layout

src/
  index.ts       OAuthProvider wiring: routes /authorize, /token, /register, /mcp
  authorize.ts   Password-gated consent page (the only manual step)
  mcp-agent.ts   MCP server: registers the 7 read-only tools
  github.ts      GitHub REST wrapper — GET-only, "owner/name" format-validated
  env.d.ts       Adds GITHUB_PAT/CONNECTOR_PASSWORD to wrangler's generated Env type
wrangler.jsonc   Worker config: DO binding, KV binding

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选