Skip to main content
Glama
ArsNovaSingers

ars-nova-github-max

README.md
# ars-nova-github-max

Gloves-off GitHub MCP server for Claude custom connectors. Full REST + GraphQL
passthrough, so it can do anything GitHub's API can do — including the three
operations the stock `github-mcp-server` has never had:

- `delete_branch`
- `create_release`
- `upload_release_asset`

Built 2026-08-13 to replace the `github` connector entry in Claude Desktop.

## Why it exists

The stock GitHub MCP server has no branch-deletion tool in any toolset, and its
release tools are read-only (`list_releases`, `get_latest_release`,
`get_release_by_tag`). That gap was hit twice during plugin release work.

Rather than hand-write the three missing tools, this server exposes `gh_api` —
an any-method, any-path REST passthrough. It therefore covers every endpoint
GitHub has now *and* everything they ship later, with no code change here. The
named tools are thin conveniences over that same primitive.

Deliberately **not** a local stdio server: the Cloud Run version keeps working
when the laptop is off, and — the deciding factor — scheduled tasks run in
fresh cloud sessions with no desktop attached, so a local connector is
invisible to them.

## Design notes

Follows the `ars-nova-gcloud-mcp` pattern (see
`claude/infra/ANS_gcloud_MCP_Build.md`): stateless Streamable HTTP transport,
one `Server` + transport per request, `?key=` token auth, `MCP_DISABLED` kill
switch, single-line JSON audit records on stdout.

One difference worth knowing. The gcloud MCP's headline safety property is "no
shell, ever" — it spawns binaries with an argv array so metacharacters stay
inert. This server never spawns a process at all; every operation is an HTTPS
request built from structured fields. There is no command-injection surface.

What replaces it as the thing to get right is authorization scope, because the
token is a full org read/write PAT. Two guards cover that:

**Org allowlist.** Requests are refused unless they target an allowlisted
owner (`GITHUB_ALLOWED_OWNERS`, default `ArsNovaSingers`). Endpoints scoped to
the token itself — `/user`, `/rate_limit`, `/search/*` — carry no third-party
owner and pass through.

**Confirm gate.** Four irreversible operations require an exact typed phrase,
which the error message states:

| Operation | Required phrase |
|---|---|
| `DELETE /repos/{owner}/{repo}` | `DELETE REPO {owner}/{repo}` |
| `DELETE /orgs/{org}/members/{user}` | `REMOVE MEMBER {user}` |
| `POST /repos/{owner}/{repo}/transfer` | `TRANSFER {owner}/{repo}` |
| `DELETE /orgs/{org}` | `DELETE ORG {org}` |

Branch deletion is deliberately **not** gated — tidying branches is the whole
point, and a deleted branch is recoverable by re-creating a ref at its old SHA.
Repository deletion is not recoverable, which is why it is.

## Environment

| Var | Required | Purpose |
|---|---|---|
| `MCP_AUTH_TOKEN` | yes | Connector token; sent by Claude as `?key=`. Server refuses to start without it. |
| `GITHUB_TOKEN` | yes | Fine-grained PAT. Server refuses to start without it. |
| `GITHUB_ALLOWED_OWNERS` | no | Comma-separated. Default `ArsNovaSingers`. |
| `MCP_DISABLED` | no | `1` disables every tool while leaving the service up and diagnosable. |

## Kill switch

```
gcloud run services update ars-nova-github-max \
  --region us-central1 --project ars-nova-org-mcp \
  --update-env-vars MCP_DISABLED=1
```

## Tools

| Tool | Purpose |
|---|---|
| `gh_api` | Any REST endpoint, any method. The gloves-off primitive. |
| `gh_graphql` | Query or mutation. For Projects v2, cross-repo reads, discussions. |
| `delete_branch` | The operation that motivated this build. |
| `create_release` | Required by PROJECT_RULES §5 — nothing ships without a release. |
| `upload_release_asset` | Attach the plugin `.zip` to that release. Base64 in. |
| `whoami` | Confirm identity and allowlist. Run this first. |

## Known limitation

The org allowlist cannot be structurally enforced on arbitrary GraphQL, since
the owner lives inside a free-form query document. `gh_graphql` logs every call
and flags mutations in the audit trail. Prefer `gh_api` for writes where a REST
equivalent exists.