Skip to main content
Glama
kazaminn

github-mcp-proxy

by kazaminn
README.md
# github-mcp-proxy

A self-hosted **GitHub MCP server** running on Cloudflare Workers, designed for personal / small-team use with **per-repository guardrails** for AI agents.

Any [MCP](https://modelcontextprotocol.io/)-capable agent (Claude, Codex, ChatGPT, Cursor, Cline, …) can be plugged in — with tight control over *which* repos it can touch and *how*.

> [!WARNING]
> If you need security or stability, use the [official GitHub MCP server](https://github.com/github/github-mcp-server).
> Fork freely; security issues are your responsibility once forked.

## Why

Generic GitHub tokens give an agent full account access. This server narrows that down:

- **Per-repo permission levels** — `read` / `issues` / `full`
- **Branch protection** — new branches must match a prefix (default `claude/`); `main`/`master` never writable
- **Commit-message policy** — Conventional Commits, validated server-side
- **Label whitelist** — `allowedLabels` per repo; the server never auto-creates labels on GitHub
- **Forbidden paths** — `.github/workflows/**` always blocked
- **Audit trail** — every write auto-appends `Signed-off-by` / `Assisted-by` trailers or a footer identifying the agent and model

## Architecture

```
 MCP agent ──(Streamable HTTP + OAuth)──▶ Cloudflare Worker
                                              │
                                              ├── workers-oauth-provider  (OAuth 2.1 server to MCP clients)
                                              ├── GitHub OAuth            (OAuth client to github.com)
                                              ├── KV                      (session tokens)
                                              ├── Durable Object          (MCP agent state)
                                              └── Octokit                 (GitHub REST)
```

Config (`ghmcp.config.ts`) is bundled with the Worker at deploy time — no runtime KV reads, typechecked via `satisfies`.

## Quick start

```bash
git clone <your-fork>
cd kz-github-mcp
npm install
cp ghmcp.config.sample.ts ghmcp.config.ts   # edit: repos you want to expose
cp wrangler.sample.jsonc wrangler.jsonc      # edit: KV id + worker name
cp .env.example .env                         # edit: for local dev only
```

Create a [GitHub OAuth App](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app) (Homepage = `https://<worker>.<subdomain>.workers.dev`, Callback = `/callback`), then:

```bash
wrangler kv namespace create "OAUTH_KV"      # add the id to wrangler.jsonc
wrangler secret put GITHUB_CLIENT_ID
wrangler secret put GITHUB_CLIENT_SECRET
wrangler secret put COOKIE_ENCRYPTION_KEY    # openssl rand -hex 32
npm run deploy
```

Local development: same flow with a second OAuth App pointing at `http://localhost:8788`, credentials in `.env`, then `npm run dev`.

### Continuous deployment (optional)

`.github/workflows/deploy.yml` deploys to Cloudflare on every push to `main`. Set two repo secrets in GitHub Settings → Secrets and variables → Actions:

| Secret | Where to get it |
|---|---|
| `CLOUDFLARE_API_TOKEN` | [Cloudflare dashboard → My Profile → API Tokens](https://dash.cloudflare.com/profile/api-tokens) → create a token with the "Edit Cloudflare Workers" template |
| `CLOUDFLARE_ACCOUNT_ID` | Cloudflare dashboard → right sidebar of any Workers page |

The GitHub OAuth credentials (`GITHUB_CLIENT_ID`, `GITHUB_CLIENT_SECRET`, `COOKIE_ENCRYPTION_KEY`) live as **Wrangler** secrets on the Worker itself, not in GitHub Actions — you only need to set them once with `wrangler secret put`.

See [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) for how it works, [`docs/CONFIG.md`](docs/CONFIG.md) for every config field.

## Connect an AI agent

**Claude (claude.ai)** — Settings → Connectors → Add custom connector → paste `https://<worker>.<subdomain>.workers.dev/sse`.

**Claude Code / Codex / Cursor / Cline** — add to the client's MCP config:

```json
{
  "mcpServers": {
    "github": {
      "command": "npx",
      "args": ["mcp-remote", "https://<worker>.<subdomain>.workers.dev/sse"]
    }
  }
}
```

**Any MCP host** — point it at the same `/sse` endpoint. OAuth flow runs on first connect.

## Tools

Prefixed with `ghmcp_`. Each declares a minimum permission level; the server denies calls below that.

| Category | Tools | Min level |
|---|---|---|
| User | `get_me` | read |
| Repo | `list_repos`, `get_file_content`, `search_code` | read |
| Issues | `list_issues`, `get_issue`, `search_issues`, `list_issue_comments` | read |
| Issues | `create_issue`, `update_issue`, `add_issue_comment` | issues |
| PRs | `list_pull_requests`, `get_pull_request` | read |
| PRs | `create_pull_request` | issues |
| Files | `create_or_update_file`, `create_branch` | full |
| Actions | `list_workflow_runs`, `get_workflow_run_logs` | read |

Any tool can be disabled via `disabledTools` in the config.

## Validation

```bash
npm run validate-config
```

Uses the `gh` CLI to confirm that every repo in `ghmcp.config.ts` exists and that every label in `allowedLabels` is present on GitHub. Runs automatically as a `prebuild` hook, so a typo fails `npm run build`.

## License

Originally forked from [`cloudflare/ai/demos/remote-mcp-github-oauth`](https://github.com/cloudflare/ai/tree/main/demos/remote-mcp-github-oauth). MIT-licensed.

This tool is MIT-licensed. — see [LICENSE](LICENSE).