MCP·Engineering
← All case studies

github-mcp — a read+write MCP over a real external API

// public repo · read + write · 74 passing tests · write tools off by default
Python 74 passing tests read + write write-gated by default typed rate-limit handling

The problem

Most MCP requests are the same shape: "expose our tool's API to Claude so the team can read and change records from a chat." The read half is easy. The parts that decide whether it's safe to put in front of a real API — where the credential lives, what happens when the upstream rate-limits you mid-call, and whether a write action can fire by accident — are the parts a hiring team can't see from a pitch. They have to trust that you handle them.

What I built

github-mcp is a read+write MCP server over the GitHub REST API — the exact pattern of the most common client request, built in the open so the discipline is verifiable instead of asserted. 14 tools across two groups:

  • Read (9 tools, always on) — repos, issues, pull requests, file contents, commits, search, users. Works unauthenticated at GitHub's 60 req/hr tier, so it demos with zero secrets. github_mcp/groups/read.py
  • Write (5 tools, OFF by default) — create/comment/label/close issues, PR review comments — refused with a structured error unless GITHUB_MCP_ENABLE_WRITE=1 is set per deployment. github_mcp/groups/write.py
  • Auth from the environment — a fine-grained PAT read from GITHUB_TOKEN, never hardcoded, never logged. github_mcp/config.py
  • Typed upstream errors — GitHub's rate limit (primary and secondary/abuse) and 4xx/5xx surface as clean typed errors carrying the reset time, never a raw crash. github_mcp/client.py

Evidence you can check yourself

Public on purpose — every claim maps to a file you can open before you reply:

  • Repo: github.com/jaimenbell/github-mcp
  • Tests: python -m pytest74 passed, 1 skipped, 0 failed (the skip is a live-API smoke, gated behind an env flag)
  • Safety: try any write tool without GITHUB_MCP_ENABLE_WRITE=1 and it returns a structured policy refusal — write access is opt-in per deployment, not a default.

Stated plainly in the README: this is a reference portfolio implementation, not the official GitHub MCP server. It exists to show how I build a read+write API server — the auth boundary, the write gate, the typed error handling — on a public API you already know, so the pattern transfers directly to your internal tool.

What it shows about how I work

This is the artifact behind the pitch. When a post says "build a custom MCP server that exposes read/write actions over our tool's API, keep secrets in env, handle errors cleanly," this is that, already built and green — with write actions defaulting off, because the safe default on a real API is the one you have to deliberately turn on.

Book a scoping call  Next case study →