Skip to content

feat(github): migrate to cloudflare workers#396

Merged
viktormarinho merged 9 commits intomainfrom
viktormarinho/github-cf-worker-check
Apr 23, 2026
Merged

feat(github): migrate to cloudflare workers#396
viktormarinho merged 9 commits intomainfrom
viktormarinho/github-cf-worker-check

Conversation

@viktormarinho
Copy link
Copy Markdown
Contributor

@viktormarinho viktormarinho commented Apr 23, 2026

Summary

Moves the github MCP off kubernetes-bun and onto Cloudflare Workers via wrangler deploy. Removes it from deploy.json / the shared deco deploy pipeline and adds a dedicated deploy-github.yml workflow that mirrors the slide-maker / site-diagnostics pattern.

What changed to make it isolate-safe

  • Installation map + trigger state migrated from in-memory (Map) / Mesh StudioKV to a single Workers KV binding (INSTALLATIONS), multiplexed by installation: and triggers: prefixes.
  • All module-level process.env reads moved into lazy closures — Workers doesn't populate process.env at module-init time under nodejs_compat.
  • Upstream MCP tool discovery, previously a top-level await, is now deferred to the first request and the resulting withRuntime is cached per isolate (reset on failure so retries work).
  • Dedicated worker entrypoint via export default { fetch }; webhook + OAuth closures read secrets per-request.

Test plan

  • cd github && bun run check — typecheck passes at pre-existing baseline (1 unrelated runtime type error in mcp-proxy.ts).
  • bunx wrangler deploy --dry-run --outdir=dist — bundles successfully (~372 KB gzip) and shows the INSTALLATIONS KV binding.
  • Add CLOUDFLARE_API_TOKEN + CLOUDFLARE_ACCOUNT_ID repo secrets, set app secrets via wrangler secret bulk .secrets.json, then merge to main and confirm the Deploy GitHub MCP workflow succeeds.
  • Hit the deployed worker's /mcp endpoint via an MCP client and confirm tool discovery works and at least one tool call succeeds.
  • Trigger a test webhook delivery and confirm /webhooks/github verifies the signature and reaches triggers.notify.

🤖 Generated with Claude Code


Summary by cubic

Migrates the GitHub MCP to Cloudflare Workers with KV-backed state, isolate-safe lazy runtime, and reliable webhook delivery via ctx.waitUntil. Adds a dedicated deploy workflow and serves at https://github-mcp.decocms.com.

  • Refactors

    • Persist installation and trigger state in Workers KV INSTALLATIONS with installation:* and triggers:* prefixes; maintain reverse index and evict stale entries on reassignment (in-memory fallback for local dev).
    • Read env vars lazily; defer upstream MCP tool discovery and runtime creation to first request; cache per isolate and reset on failure.
    • New Worker entrypoint via export default { fetch }; proxy MCP resources before runtime; read secrets per request.
    • Webhooks: verify signatures, log by x-github-delivery, and deliver to Mesh via KV-backed callback with ctx.waitUntil to avoid dropped events.
    • Tooling: add wrangler.toml, switch scripts to bunx wrangler, add @cloudflare/workers-types and wrangler, route custom domain, update github/app.json URL, add deploy-github.yml.
  • Migration

    • Create a Workers KV namespace and bind it as INSTALLATIONS in wrangler.toml.
    • Add repo secrets: CLOUDFLARE_API_TOKEN, CLOUDFLARE_ACCOUNT_ID; set Worker secrets via wrangler secret bulk .secrets.json (GITHUB_APP_ID, GITHUB_PRIVATE_KEY, GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET, GITHUB_WEBHOOK_SECRET).
    • Deploy with the new workflow or run bunx wrangler deploy.

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

viktormarinho and others added 9 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
@viktormarinho viktormarinho merged commit e52ce73 into main Apr 23, 2026
2 checks passed
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