Skip to content

fix(github): send User-Agent on all GitHub API calls#399

Closed
viktormarinho wants to merge 10 commits intomainfrom
viktormarinho/github-cf-worker-check
Closed

fix(github): send User-Agent on all GitHub API calls#399
viktormarinho wants to merge 10 commits intomainfrom
viktormarinho/github-cf-worker-check

Conversation

@viktormarinho
Copy link
Copy Markdown
Contributor

@viktormarinho viktormarinho commented Apr 23, 2026

Summary

Worker crashed with CF error 1101 on the first /mcp request. Wrangler tail showed:

Failed to list GitHub App installations: 403 — Request forbidden by administrative rules. Please make sure your request has a User-Agent header

Cloudflare Workers' `fetch` doesn't set a default `User-Agent`; Node/k8s did, which hid the issue until now. Added `User-Agent: deco-cms-github-mcp` to every outbound GitHub API call (JWT-authed installations list, user installations list, OAuth token exchange/refresh, and the MCP proxy transport).

Test plan

  • Merge and wait for the Deploy GitHub MCP workflow.
  • `curl -X POST https://github-mcp.decocms.com/mcp -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'` returns a tools list (not CF 1101).

🤖 Generated with Claude Code


Summary by cubic

Migrated the GitHub MCP to Cloudflare Workers and fixed GitHub API 403s by sending a User-Agent on every request. Adds durable KV storage, lazy env loading, and a dedicated deploy workflow.

  • Bug Fixes

    • Send User-Agent: deco-cms-github-mcp on all GitHub API calls (installations list, OAuth exchange/refresh, MCP transport).
    • Prevent dropped webhooks by delivering to Mesh directly and using ctx.waitUntil.
    • Clean up stale KV reverse-index keys when an installation’s owner changes.
  • Migration

    • Replatform to Cloudflare Workers via wrangler with custom domain github-mcp.decocms.com.
    • Store installation and trigger state in Workers KV (INSTALLATIONS), replacing in-memory/StudioKV.
    • Lazy-load secrets and defer upstream tool discovery to first request; cache per isolate.
    • Add deploy-github.yml; update github/package.json scripts; add @cloudflare/workers-types, wrangler, and wrangler.toml.

Written for commit aad7096. Summary will update on new commits.

viktormarinho and others added 10 commits April 23, 2026 09:37
Moves the github MCP off kubernetes-bun and onto CF Workers via `wrangler
deploy`. Removes it from deploy.json / the shared `deco deploy` pipeline
and adds a dedicated `deploy-github.yml` workflow.

Key changes to make it isolate-safe:
- Installation map + trigger state migrated from in-memory / Mesh StudioKV
  to a Workers KV binding (`INSTALLATIONS`) with `installation:` and
  `triggers:` prefixes
- All module-level `process.env` reads moved into lazy closures (Workers
  doesn't populate `process.env` at module init)
- Upstream MCP tool discovery deferred to first request (previously a
  top-level `await`) and runtime construction cached per isolate
- Webhook + OAuth closures now pull secrets per-request

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Emits [Webhook] log lines on ingress, on signature failure, on
skip (no installation / no mapping), and on successful notify —
keyed by x-github-delivery so a single event can be traced end
to end in Cloudflare observability.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…cf-worker-check

# Conflicts:
#	github/server/main.ts
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
triggers.notify() is fire-and-forget — on Workers the internal fetch
to Mesh got cancelled as soon as we returned the HTTP response,
silently dropping events.

Replaced with a direct Mesh callback delivery that reads trigger
credentials from KV and returns an awaitable Promise, which we hand
to ctx.waitUntil so Workers keeps the isolate alive until the POST
completes. Threads ExecutionContext through handle() → webhook
handler. Also logs every success / failure per delivery id.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…owner

If two users share a GitHub App installation, the second OAuth would
overwrite installation:<id> but leave the first user's
connection:<oldConn>:<id> entry behind. A later
removeByConnection(oldConn) then deleted the live forward mapping
for the new owner.

set() now reads the current owner first and cleans up its reverse-
index entry in the same batch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…cf-worker-check

# Conflicts:
#	github/server/main.ts
Cloudflare Workers' fetch doesn't set a default User-Agent, and
GitHub's API rejects UA-less requests with 403 "Request forbidden
by administrative rules" — the worker was crashing at upstream tool
discovery (getAppInstallationToken) on the very first request.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@viktormarinho
Copy link
Copy Markdown
Contributor Author

Superseded — opening a fresh PR rebased off main.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant