Skip to main content
Glama
github-repo-mcp-server

github-repo-mcp-server

by technomad641

github-repo-mcp-server

An MCP (Model Context Protocol) server that lets an AI assistant query GitHub on your behalf — your own repos, issues, PRs, and CI status, plus discovery of trending repos by topic across public GitHub.

See SPEC.md for the full design and development plan.

Why we built this

This started as a learning project to understand MCP by actually building one, not just reading about it - so the goal was a server that (a) does something genuinely useful day to day, and (b) touches all three MCP primitives (tools, resources, prompts) instead of just tools, which is where most "hello world" MCP examples stop.

GitHub was the right domain for that: it has a mature, well-documented, official REST API (no scraping, no unofficial endpoints, no partner approval needed to get started), it's something we already use constantly, and it naturally splits into two different trust boundaries worth modeling - your own repos (should be tightly scoped) vs. public GitHub data at large (fine to query more freely). That split became the project's actual design backbone: see Architecture below.

The specific tool set also traces back to a real question asked earlier in this project: "what's trending in machine learning right now?" Answering that honestly - GitHub's real Trending page has no API, and "trending in the US" isn't answerable at all since repos have no geography field - is what shaped search_trending_repos into an honest proxy instead of a fake exact match. That mix of "build the useful thing" and "be upfront about what's actually possible" carried through the rest of the tools too.

Related MCP server: AI Project Explorer

Architecture

flowchart LR
    subgraph Host["MCP Host (Claude Code / Claude Desktop / Cursor)"]
        AI["AI assistant"]
    end

    AI <-->|"JSON-RPC over stdio"| Server

    subgraph Server["github-repo-mcp-server"]
        direction TB
        Tools["Tools (13)\npersonal-scope + search_trending_repos"]
        Resources["Resources (3)\nrepo://.../readme, issues/{n}, pulls/{n}"]
        Prompts["Prompts (3)\nreview summary, weekly digest, trending"]
        Config["config.ts\nrepo allowlist"]
        Client["client.ts\nOctokit + throttling"]

        Tools --> Config
        Resources --> Config
        Tools --> Client
        Resources --> Client
        Prompts -.->|"instructs AI to call"| Tools
    end

    Client -->|"personal-scope calls\n(allowlist-gated)"| GH["GitHub REST API"]
    Tools -->|"search_trending_repos\n(not allowlist-gated)"| GH

    GH -->|"read-only, fine-grained PAT"| Repos[("Your repos\n+ public GitHub data")]

Two trust boundaries drive the whole design:

  • Personal scope — every tool that touches a specific repo (get_repo, list_issues, get_file_contents, etc.) is checked against config.json's allowlist before it's allowed to run. A token scoped to your repos plus code that enforces the allowlist means the server physically cannot read a repo you didn't list, even if the AI asked it to.

  • Global discovery — search_trending_repos is deliberately exempt from the allowlist, since it searches public GitHub data at large rather than a specific repo. Same GitHub API, different trust model, so it's routed differently in code, not just documented differently.

Prompts sit a layer above both: they don't call GitHub directly, they return instructions telling the AI which tools to call and how to summarize the results - the same primitive as a saved prompt template a human would write, just server-hosted so every client that connects gets it for free.

Status

✅ All planned milestones complete.

  • M0 — Project scaffold: TypeScript + MCP SDK, minimal ping tool over stdio

  • M1 — GitHub client + config (Octokit auth, repo allowlist)

  • M2 — Tier 1 read-only personal-repo tools (12 tools)

  • M3 — search_trending_repos discovery tool

  • M4 — MCP resources (README, issue/PR content)

  • M5 — MCP prompts (reusable templates)

  • M6 — Tests, rate-limit hardening, docs

Tier 2 write tools (create issue, comment, etc.) were scoped out of v1 intentionally — see SPEC.md §11. This is a read-only server.

Requirements

  • Node.js 18+ (developed against Node 20; see .nvmrc)

Configuration

  1. Copy .env.example to .env and set GITHUB_TOKEN to a fine-grained personal access token with read-only Contents, Issues, Pull requests, and Actions permissions, scoped to the repos you want this server to access.

  2. List those repos (as "owner/repo" strings) in config.json — this is the allowlist personal-scope tools are restricted to. search_trending_repos is exempt, since it queries public GitHub data rather than a specific repo.

  3. get_notifications additionally needs the token's Notifications account permission (separate section from repository permissions on the token creation page) — every other tool works without it.

Tools

Personal scope (allowlist-gated): get_repo, list_repos, list_branches, list_commits, get_file_contents, list_issues, get_issue, list_pull_requests, get_pull_request, search_code, get_workflow_runs, get_notifications.

Global discovery (not allowlist-gated, searches public GitHub data): search_trending_repos — finds trending repos for a topic, ranked by star velocity among recently-created repos. A documented proxy for github.com/trending, which has no official API.

Resources

URI template

Content

repo://{owner}/{repo}/readme

The repo's README, as markdown

repo://{owner}/{repo}/issues/{number}

Issue title, body, and comments

repo://{owner}/{repo}/pulls/{number}

PR title, description, diff stats

Prompts

Name

What it does

summarize_prs_for_review

Finds open PRs and summarizes which need attention

weekly_repo_digest

Commits, issues, PRs, and CI status from the last week

whats_trending

Trending public repos for a GitHub topic

Getting started

npm install
npm run dev     # run the server directly via tsx
npm run build   # compile to dist/
npm start       # run the compiled server

The server communicates over stdio, following the MCP protocol — it's meant to be launched by an MCP-compatible host (e.g. Claude Code, Claude Desktop, Cursor), not run standalone in a terminal.

Manual testing with MCP Inspector

MCP Inspector is the official visual testing tool for MCP servers — it gives you a web UI to browse every tool/resource/prompt and call them interactively, instead of hand-typing JSON-RPC.

npm run build   # inspector launches the compiled server, so build first
npx @modelcontextprotocol/inspector@latest node --env-file=.env dist/index.js

This prints a local URL with an auth token pre-filled, e.g.:

MCP Inspector Web is up and running at:
   http://localhost:6274?MCP_INSPECTOR_API_TOKEN=<token>

Open that URL (it also tries to open your browser automatically). From there you can call any tool with real arguments - e.g. get_repo with owner: <your-username>, repo: <a-repo-in-your-allowlist>, or search_trending_repos with topic: machine-learning - and see the live result. The token in the URL authenticates your session; treat it like a credential and don't share the link.

Note: Inspector v2 lists Node 22+ as its required engine. It still runs fine on Node 20 (just an EBADENGINE warning, not a hard failure) - if you hit a real compatibility issue, npm i -g n && n 22 (or your Node version manager's equivalent) resolves it.

Testing

npm test        # typecheck (src + test) then run the full vitest suite
npm run typecheck

The suite (32 tests) runs through the real MCP protocol layer — an McpServer and Client connected over an in-memory transport — with @octokit/rest mocked, so it exercises actual schema validation and request routing without making live API calls. Every tool, resource, and prompt was also manually verified against a real repo during development; see individual commit messages for what was and wasn't exercised against real data.

What we learned

About MCP as a protocol:

  • It's JSON-RPC 2.0 underneath, nothing exotic - once you've seen the initialize handshake and one tools/call round trip, you've seen the shape of the whole protocol. Everything else is more of the same three message types (tools, resources, prompts) repeated.

  • Tools, resources, and prompts are genuinely different primitives, not three names for the same thing. Tools are actions the AI decides to invoke; resources are addressable content the AI (or a human) can read directly without a tool call; prompts are server-authored instructions that tell the AI which tools to call and how - a reusable playbook, not a data fetch. Building all three (not just tools, which is where most examples stop) is what made the distinction click.

  • A server is trust-boundary-agnostic by default - it'll do whatever the code allows. The allowlist pattern here (config.ts gating every personal-scope tool) exists because MCP itself provides no scoping; the server author has to build it.

About testing against real data instead of assuming code is correct: Every tool, resource, and prompt was smoke-tested against a live repo during development, which surfaced real issues a "does it compile" check would have missed entirely:

  • GitHub's code search doesn't index unstarred forks. search_code returned 0 results with incomplete_results: true against a fresh fork - confirmed via a raw curl call that this is GitHub's own indexing limitation, not a bug (forks are only code-search-indexed once they have more stars than their parent repo).

  • get_notifications needs a separate token permission. It uses a GitHub account-level permission (Notifications), not a repository permission like every other tool - discovered via a live 403, now surfaced as a specific error message instead of the generic one.

  • Composed Octokit plugin types aren't portable. Wrapping the client with @octokit/plugin-throttling broke .d.ts emission (TS2883). Fixed by dropping declaration: true from tsconfig.json, since this is a runnable server, not a library other packages import.

  • A separate test tsconfig was needed. The main tsconfig.json excludes test/ from compilation, so tsc --noEmit silently never type-checked the test files - vitest's esbuild transform strips types without checking them. tsconfig.test.json plus wiring npm test to typecheck first caught real unsafe-property-access bugs in the tests that were otherwise invisible.

License

MIT

Available Tools

14 tools
get_file_contentsB

Read a file's contents from a repo at a given ref.

Inputs: path (required), repo (required), owner (required), ref.

get_issueA

Get full detail for one issue, including its comment thread.

Inputs: repo (required), owner (required), issue_number (required).

get_notificationsA

Get your unread GitHub notifications (mentions, review requests), filtered to repos in this server's allowlist. Requires the token's Notifications account permission - not required by other tools.

Inputs: all.

get_pull_requestA

Get detail for one PR: diff stats, mergeable state, and review status.

Inputs: repo (required), owner (required), pull_number (required).

get_repoB

Get metadata for a repo: description, stars, language, default branch, visibility.

Inputs: repo (required), owner (required).

get_workflow_runsA

Get recent GitHub Actions workflow run status (pass/fail) for a repo.

Inputs: repo (required), owner (required), limit, branch.

list_branchesB

List branches in a repo.

Inputs: repo (required), owner (required), limit.

list_commitsA

List recent commits on a branch.

Inputs: repo (required), owner (required), limit, branch.

list_issuesA

List issues in a repo, filterable by state, label, and assignee.

Inputs: repo (required), owner (required), limit, state, +2 more.

list_pull_requestsB

List pull requests in a repo, filterable by state and branch.

Inputs: repo (required), owner (required), base, head, +2 more.

list_reposA

List every repo in this server's allowlist, with basic metadata for each.

Inputs: no named parameters.

pingA

Health check — returns pong plus the current server time.

Inputs: message.

search_codeA

Search code within a single allowlisted repo. Note: GitHub's code search only indexes forks that have more stars than their parent repo, and can lag for low-activity repos - a 0-result response may mean 'not indexed yet' rather than 'no matches'.

Inputs: repo (required), owner (required), query (required), limit.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.8/5.0

    Scored across 14 tools

    Disambiguation5/5

    Each tool has a clearly distinct purpose. Getter vs. list tools are separated by singular/plural semantics, and the two search tools differ by target (code within a repo vs. trending repos across GitHub). No two tools overlap in function.

    Naming Consistency5/5

    All tool names use lowercase with underscores and follow a consistent verb_noun pattern (get_, list_, search_, ping). The pattern is predictable and easy to infer from the resource being accessed.

    Tool Count5/5

    14 tools is well within the ideal range for a GitHub-focused server. Each tool covers a distinct resource or operation, and the count feels appropriate for the scope—not bloated, not too sparse.

    Completeness3/5

    The read-side surface is solid: repos, issues, PRs, branches, commits, files, workflows, notifications, and trending. However, there are no create/update/delete operations for any resource, which is a significant gap if the server is expected to support full issue/PR lifecycle management. Agents can work around this only if their tasks are read-only.

    Maintenance

    ActivityInactive
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers