MCP GitHub CrunchTools
This MCP GitHub server lets you read and write GitHub repository data for issues, pull requests, Actions, releases, repository files, and search.
List, get, create, update, comment on, close, and reopen issues.
List/get pull requests, get diffs and CI checks, list all PR conversation/diff comments and reviews, create/update PRs, reply to review comments, and merge only when required checks are green.
List/inspect Actions workflow runs and jobs, fetch failed-job log summaries, trigger workflows, and rerun full runs or failed jobs.
Create releases, including tags.
Read decoded repository file contents and list git trees.
Search code and issues/PRs across GitHub.
Supports github.com and GitHub Enterprise Server with token-based auth, including optional per-request HTTP tokens.
Provides tools for interacting with GitHub repositories, including issues, pull requests, file content, and code search.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@MCP GitHub CrunchTools List open issues for crunchtools/mcp-github"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
MCP GitHub CrunchTools
A secure MCP (Model Context Protocol) server for GitHub issues, pull requests, Actions runs, releases, repository files, and search. Works with github.com and GitHub Enterprise Server.
Overview
This MCP server is designed to be:
Secure by default - STRIDE threat model (see SECURITY.md), Pydantic input validation, and the API token held as a
SecretStrto prevent accidental loggingNo third-party services - Runs locally via stdio, your API token never leaves your machine
Multi-instance - Works with github.com or GitHub Enterprise Server via configurable API URL
Cross-platform - Works on Linux, macOS, and Windows
Automatically updated - GitHub Actions monitor for CVEs and update dependencies
Containerized - Available at
quay.io/crunchtools/mcp-githubbuilt on Hummingbird Python base image
Related MCP server: GitHub Prod MCP
Naming Convention
Component | Name |
GitHub repo | |
Container |
|
Python package (PyPI) |
|
CLI command |
|
Module import |
|
Why Hummingbird?
The container image is built on the Hummingbird Python base image from Project Hummingbird, which provides:
Minimal CVE exposure - Built with a minimal package set, dramatically reducing the attack surface
Regular updates - Security patches are applied promptly
Optimized for Python - Pre-configured Python environment
Production-ready - Proper signal handling and non-root user defaults
Features
26 tools. Tools marked write change something on GitHub; the others only
read and publish readOnlyHint. The last column is the fine-grained token
permission the tool needs (see Token permissions).
Issues (5 tools)
Tool | What it does | Permission |
| List issues for a repository (pull requests excluded) | Issues: read |
| Get a single issue by number | Issues: read |
| Comments on an issue or a pull request conversation, oldest first | Issues: read, or Pull requests: read for a PR |
| Open an issue (write) | Issues: write |
| Comment on an issue or pull request (write) | Issues: write, or Pull requests: write for a PR |
| Edit, close or reopen an issue (write) | Issues: write |
Pull Requests (9 tools)
Tool | What it does | Permission |
| List pull requests for a repository | Pull requests: read |
| Get a single pull request by number | Pull requests: read |
| Get the unified diff for a pull request | Pull requests: read |
| CI verdict from check-runs and commit statuses; skipped is not failed | Pull requests: read, Commit statuses: read, Actions: read (and see the note under Token permissions) |
| Conversation comments, diff comments and reviews in one list, each tagged with its kind | Pull requests: read |
| Open a pull request, optionally as a draft (write) | Pull requests: write |
| Edit, close or reopen a pull request; does not merge (write) | Pull requests: write |
| Reply in the thread of a diff comment (write) | Pull requests: write |
| Merge, squash or rebase a pull request, only once the base branch's required checks are green on its head (see The merge gate) (write) | Contents: write; Pull requests, Commit statuses and Actions: read |
Actions (6 tools)
Tool | What it does | Permission |
| List workflow runs, filtered by branch or status | Actions: read |
| One run's status, conclusion, head SHA and jobs (no logs) | Actions: read |
| A failure summary (the cause lines) of each failed job in a run; the raw log tail with | Actions: read |
| Start a fresh run via | Actions: write |
| Re-run every job of a run (write) | Actions: write |
| Re-run only the failed jobs of a run (write) | Actions: write |
Releases (1 tool)
Tool | What it does | Permission |
| Create a release, and its tag if the tag does not exist (write) | Contents: write |
Files (2 tools)
Tool | What it does | Permission |
| Read decoded file content from a repository | Contents: read |
| List the git tree (files and directories) | Contents: read |
Search (2 tools)
Tool | What it does | Permission |
| Search code across GitHub | Contents: read on a private repository to be searched |
| Search issues and pull requests across GitHub | Issues: read, Pull requests: read on a private repository to be searched |
Limits on what a tool returns
list_pull_request_comments_toolreads each of its three kinds oldest first, up to 300 items per kind.truncatedandtruncated_kindssay when newer items were left out. Called on a plain issue it says so; uselist_issue_comments_toolthere.list_issue_comments_toolreturns one page (up to 100 comments); followpagination.next_pagefor more.get_workflow_run_toollists up to 300 jobs;jobs_truncatedsays when the run has more.get_failed_job_logs_toolreturns afailure_summaryper job by default — the tail lines that name the cause (error annotations, test failures, tracebacks, non-zero exits) — scanning at most 1000 lines per job (200 by default), for at most 5 failed jobs, and at most 256 KiB of log text in all, split evenly between the jobs. Passinclude_raw_log=trueto also get the full cleaned tail aslog/log_lines; a scanning gateway may withhold that raw tail where it delivers the summary.tail_linesin the result is the value actually used,failed_jobs_omittedcounts the jobs left out, andtruncatedis set on a job whose log or summary was cut and on the result when anything was. Only the first 300 jobs of a run are examined;jobs_truncatedsays when the run has more, and the counts then cover only those.
Installation
With uvx (Recommended)
uvx mcp-github-crunchtoolsWith pip
pip install mcp-github-crunchtoolsWith Container
podman run -e GITHUB_TOKEN=your_token \
quay.io/crunchtools/mcp-githubConfiguration
Environment Variables
Variable | Required | Default | Description |
| Yes, unless | — | GitHub Personal Access Token |
| No | off |
|
| No |
| API base URL (set for GHES) |
| No | — | Default owner when a tool omits |
| No | — | Custom CA bundle path, for self-hosted GHES with an internal CA |
| No |
| Set |
Per-request token
GitHub's own hosted MCP server takes the caller's token on every HTTP
request as Authorization: Bearer <token> and acts with it. With
GITHUB_ACCEPT_REQUEST_TOKEN=true this server does the same, so a client
configured for GitHub's server can point here instead with the same header.
Over the
streamable-httporssetransport, a request carryingAuthorization: Bearer <token>is sent to GitHub with that token, for that request only. Concurrent requests with different tokens each use their own; the token is never stored on the shared client.A request without the header falls back to
GITHUB_TOKEN. With the option on,GITHUB_TOKENis optional: an instance without it serves only requests that bring a token, and refuses the others before calling GitHub ("No GitHub token for this request").The header must be exactly
Bearer <token>(scheme in any case, one space, a token of printable ASCII with no spaces). Anything else is refused, never forwarded, even whenGITHUB_TOKENis set.The request token is scrubbed from error messages like
GITHUB_TOKENis.With the option off (the default) an incoming Authorization header is ignored, exactly as before. stdio has no request headers and always uses
GITHUB_TOKEN.
The option makes the server act as whoever sends a token. Expose the HTTP port only to callers you would hand that token to anyway, behind TLS.
Creating a GitHub Personal Access Token
Navigate to token settings
Create a token
Name:
mcp-github-crunchtoolsExpiration: Set an appropriate date (90 days recommended)
Scopes: Grant only what the tools you use need; see Token permissions.
Copy and Store Token
Copy the token immediately (shown only once)
Store securely in a password manager
Token permissions
For a fine-grained personal access token, the repository permissions are:
Permission | Read is enough for | Write is needed for |
Metadata | every tool (GitHub grants it with any other permission) | — |
Contents |
|
|
Issues |
|
|
Pull requests |
|
|
Actions |
|
|
Commit statuses |
| — |
get_pull_request_checks_tool and merge_pull_request_tool read check-runs,
and GitHub's permission table for fine-grained tokens has no permission for
that endpoint: a fine-grained token gets 403 on a private repository. Both
tools then read the head commit's GitHub Actions jobs instead (Actions: read).
Every Actions job is a check run with the same id and name, so the verdict
and the merge gate are the same for checks GitHub Actions reports. Checks from
any other app are not visible that way, so the merge gate refuses a required
check that names another app, or that accepts any app and has no commit
status, as missing. checks_source in each result says which was read, and
the checks verdict then carries a note saying other apps' checks are not
visible. Only the newest run of each workflow and event is read, and a commit
with runs of more than 30 workflows is not judged.
The search tools have no permission of their own: they return what the token can already read.
Three things worth knowing before granting write:
Contents: write is the merge and release permission. GitHub has no narrower one: the same grant lets the token push commits and tags. A release published with
create_release_toolalso fires the repository'son: releaseworkflows.A release whose target commit changes a workflow file under
.github/workflows/, relative to the default branch, additionally needs Workflows: write; without it GitHub answers 404. Expect the same of a merge that brings workflow changes in.The merge gate checks required status checks itself, below. Other branch rules (required reviews, for instance) are left to GitHub, which answers 405 when it refuses; a token that may bypass the rules gets past them.
The merge gate
An organization owner, or anyone on a ruleset's bypass list, can merge past
failing required checks: GitHub merges a red pull request for such a token.
So merge_pull_request_tool does not leave the checks to GitHub. Before it
merges it:
reads the pull request's base branch and head SHA, and refuses if
shawas given and the head has moved;collects the base branch's required status checks from its active rulesets and its classic branch protection, read from the branch itself so no Administration permission is needed. An error reading either, or a protected branch whose protection is not shown, refuses the merge rather than count as none;
refuses if no check is required, unless
allow_no_required_checks=True;requires, for every required check on the head commit, a completed check run of that name concluding
success,skippedorneutral(the most recent run of the name decides), or a commit status ofsuccess. A rule that names an app accepts only that app's check runs. A token that cannot read check-runs is judged on the Actions jobs (see above). A missing, pending or failed check refuses the merge, and the error lists each one and why;reads the pull request again and refuses if its head or base changed meanwhile (GitHub's merge call pins the head but not the base), then merges with the head SHA pinned, so a later push makes GitHub refuse (409) rather than merge code nobody checked.
With a classic token, repo covers all of the above (public_repo for public
repositories only), plus workflow for the workflow-file case.
Add to Claude Code
claude mcp add mcp-github-crunchtools \
--env GITHUB_TOKEN=your_token_here \
-- uvx mcp-github-crunchtoolsFor GitHub Enterprise Server:
claude mcp add mcp-github-crunchtools \
--env GITHUB_TOKEN=your_token_here \
--env GITHUB_API_URL=https://ghe.example.com/api/v3 \
-- uvx mcp-github-crunchtoolsFor the container version:
claude mcp add mcp-github-crunchtools \
--env GITHUB_TOKEN=your_token_here \
-- podman run -i --rm -e GITHUB_TOKEN quay.io/crunchtools/mcp-githubUsage Examples
List Issues
User: List open issues for crunchtools/mcp-github
Assistant: [calls list_issues_tool with owner="crunchtools", repo="mcp-github"]Review a Pull Request
User: Show me the diff for PR #5 in crunchtools/mcp-github
Assistant: [calls get_pull_request_diff_tool with pull_number=5]Check CI Status
User: Did the checks pass on pull request 5?
Assistant: [calls get_pull_request_checks_tool with pull_number=5]Find Out Why CI Failed
User: Why did run 123456 fail?
Assistant: [calls get_failed_job_logs_tool with run_id=123456]Answer a Review and Merge
User: Reply to the review findings on PR 5, then squash-merge it
Assistant: [calls list_pull_request_comments_tool with pull_number=5]
Assistant: [calls reply_to_review_comment_tool with comment_id=..., body="fixed in abc1234"]
Assistant: [calls get_pull_request_checks_tool with pull_number=5]
Assistant: [calls merge_pull_request_tool with merge_method="squash", sha=<head_sha from the checks>]
(the tool checks the required checks again itself before merging)Read a File
User: Show me src/server.py from crunchtools/mcp-github
Assistant: [calls get_file_content_tool with path="src/server.py"]Search
User: Find code using FastMCP in crunchtools repos
Assistant: [calls search_code_tool with query="FastMCP org:crunchtools"]Security
This server was designed with security as a primary concern. See SECURITY.md for details.
Key Security Features
Token Protection
Stored as SecretStr (never accidentally logged)
Environment variable only (never in files or args)
Optionally, a per-request token from the HTTP
Authorizationheader, used for that request only and never stored on the shared clientBoth sanitized from all error messages
Input Validation
Pydantic models for write inputs
Allowlist character validation for owner/repo names
Path traversal prevention for file reads
API Hardening
Bearer-token auth and pinned GitHub API version
HTTPS enforcement (except localhost)
TLS certificate validation
Request timeouts (30s)
Response size limits (10MB)
Job-log downloads follow GitHub's redirect without the token, over HTTPS only, and keep just the tail (20MB download limit)
Merges only when the required checks are green on the head commit, even for a token GitHub would let bypass them
Automated CVE Scanning
GitHub Actions scan dependencies
Container security scanning with Trivy
Development
Setup
git clone https://github.com/crunchtools/mcp-github.git
cd mcp-github
uv sync --all-extrasRun Tests
uv run pytestLint and Type Check
uv run ruff check src tests
uv run mypy srcBuild Container
podman build -t mcp-github .License
AGPL-3.0-or-later
Contributing
Contributions welcome! Please read SECURITY.md before submitting security-related changes.
Links
Available Tools
26 toolscreate_issue_comment_toolCreate Issue Comment ToolC
Create a comment on a GitHub issue or pull request.
Inputs: body (required), repo (required), issue_number (required), owner. Returns: object
create_issue_toolCreate Issue ToolC
Create a new issue in a GitHub repository.
Inputs: repo (required), title (required), body, owner, +1 more. Returns: object
create_pull_request_toolCreate Pull Request ToolC
Open a GitHub pull request.
Inputs: base (required), head (required), repo (required), title (required), +3 more. Returns: object
create_release_toolCreate Release ToolA
Create a GitHub release, creating its tag if the tag does not exist.
Publishing a release (draft=false) fires the repository's
on: release workflows, such as package and image publishing.
Inputs: repo (required), tag_name (required), body, name, +5 more. Returns: object
get_failed_job_logs_toolGet Failed Job Logs ToolARead-only
Get the failure summary of each failed job in a GitHub Actions run.
Returns, for every job that failed or timed out, a failure_summary: the
log-tail lines that name the cause (error annotations, test failures,
tracebacks, non-zero exits), with ANSI escapes and timestamps removed, plus
the names of the steps that failed. Set include_raw_log to also get the
full cleaned tail as log — it can be large and, for a repository whose
CI output quotes attack corpora, a scanning gateway may flag and withhold
it, where the small, signal-only summary is far more likely to pass.
Logs are fetched for at most 5 failed jobs and all log text together is
capped at 256 KiB; truncated and failed_jobs_omitted say when
something was cut. jobs_truncated means the run has more than the 300
jobs examined, so the failed-job counts cover only those.
Inputs: repo (required), run_id (required), owner, tail_lines, +1 more. Returns: object
get_file_content_toolGet File Content ToolARead-only
Get the decoded text content of a file in a GitHub repository.
Inputs: path (required), repo (required), ref, owner. Returns: object
get_issue_toolGet Issue ToolBRead-only
Get a single GitHub issue by number.
Inputs: repo (required), issue_number (required), owner. Returns: object
get_pull_request_checks_toolGet Pull Request Checks ToolARead-only
Get a CI verdict for a PR that distinguishes skipped from failed.
Classifies every check-run and commit-status context into passed,
failing, pending, or skipped. SKIPPED checks are NOT failures. Use the
returned ready_to_merge boolean as the signal for whether the PR is
clear to merge (True only when nothing is failing or pending and the PR
is not known-unmergeable). A token that cannot read check-runs (any
fine-grained token) gets the Actions jobs instead; checks_source says
which, and only Actions-reported checks are then visible.
Inputs: repo (required), pull_number (required), owner. Returns: object
get_pull_request_diff_toolGet Pull Request Diff ToolBRead-only
Get the unified diff for a GitHub pull request.
Inputs: repo (required), pull_number (required), owner. Returns: object
get_pull_request_toolGet Pull Request ToolBRead-only
Get a single GitHub pull request by number.
Inputs: repo (required), pull_number (required), owner. Returns: object
get_workflow_run_toolGet Workflow Run ToolARead-only
Get one GitHub Actions workflow run and its jobs (no logs).
Inputs: repo (required), run_id (required), owner. Returns: object
list_issue_comments_toolList Issue Comments ToolARead-only
List the comments on a GitHub issue or pull request conversation, oldest first.
Works for plain issues, which list_pull_request_comments_tool does not. On a pull request it returns only the conversation comments; diff comments and reviews come from list_pull_request_comments_tool.
Inputs: repo (required), issue_number (required), page, owner, +1 more. Returns: object
list_issues_toolList Issues ToolBRead-only
List issues for a GitHub repository (pull requests excluded).
Inputs: repo (required), page, owner, state, +2 more. Returns: object
list_pull_request_comments_toolList Pull Request Comments ToolARead-only
List everything said on a GitHub pull request, in one list.
Combines the PR's conversation comments (kind "issue_comment"), comments on the diff (kind "review_comment", with path, line, review_id and in_reply_to_id) and submitted reviews (kind "review", with state), oldest first. Every item carries kind, id, author, created_at, body and html_url.
Each kind is capped at 300 items; truncated and truncated_kinds
say when newer items were left out.
Inputs: repo (required), pull_number (required), owner. Returns: object
list_pull_requests_toolList Pull Requests ToolCRead-only
List pull requests for a GitHub repository.
Inputs: repo (required), page, owner, state, +1 more. Returns: object
list_repo_tree_toolList Repo Tree ToolCRead-only
List the git tree (files and directories) of a GitHub repository.
Inputs: repo (required), owner, tree_sha, recursive. Returns: object
list_workflow_runs_toolList Workflow Runs ToolCRead-only
List GitHub Actions workflow runs for a repository.
Inputs: repo (required), page, owner, branch, +2 more. Returns: object
merge_pull_request_toolMerge Pull Request ToolA
Merge a GitHub pull request, only once its required checks are green.
The tool enforces the base branch's required status checks itself before
merging, because an organization owner's token (or one on a ruleset's
bypass list) lets GitHub merge past failing required checks. It refuses,
naming each offending check, when a required check run or status on the
PR head is missing, pending or failed; when sha is not the current
head; when branch protection cannot be read; and when the base branch
requires no checks at all, unless allow_no_required_checks is True.
Passing check runs conclude success, skipped or neutral. The merge is
pinned to the head SHA that was checked. A token that cannot read
check-runs (any fine-grained token) is judged on the Actions jobs, and a
required check not reported by GitHub Actions is refused as missing.
Inputs: repo (required), pull_number (required), sha, owner, +2 more. Returns: object
reply_to_review_comment_toolReply To Review Comment ToolA
Reply in the thread of a pull request review comment.
comment_id is the id of a "review_comment" item from
list_pull_request_comments_tool whose in_reply_to_id is empty; GitHub
rejects a reply to a reply, so answer the comment that started the thread.
Inputs: body (required), repo (required), comment_id (required), pull_number (required), +1 more. Returns: object
rerun_failed_jobs_toolRerun Failed Jobs ToolB
Re-run only the failed jobs in a GitHub Actions workflow run.
Inputs: repo (required), run_id (required), owner. Returns: object
rerun_workflow_run_toolRerun Workflow Run ToolB
Re-run all jobs in a GitHub Actions workflow run.
Inputs: repo (required), run_id (required), owner. Returns: object
search_code_toolSearch Code ToolARead-only
Search for code across GitHub.
Uses GitHub code search syntax (e.g., "addClass repo:jquery/jquery").
Inputs: query (required), page, per_page. Returns: object
search_issues_toolSearch Issues ToolBRead-only
Search for issues and pull requests across GitHub.
Uses GitHub issue search syntax (e.g., "is:open is:pr author:octocat").
Inputs: query (required), page, per_page. Returns: object
trigger_workflow_toolTrigger Workflow ToolA
Trigger a fresh GitHub Actions run via the workflow_dispatch event.
Use this to force a new build. Unlike rerun_workflow_run_tool (which
re-runs an existing run and is rejected by GitHub for runs older than 30
days), this starts a brand-new run regardless of when the workflow last
ran. The target workflow must declare an on: workflow_dispatch trigger.
Inputs: repo (required), workflow_id (required), ref, owner, +1 more. Returns: object
update_issue_toolUpdate Issue ToolA
Update a GitHub issue, including closing or reopening it.
Set state="closed" to close an issue. Set state="open" with state_reason="reopened" to reopen.
Inputs: repo (required), issue_number (required), body, owner, +4 more. Returns: object
update_pull_request_toolUpdate Pull Request ToolA
Update a GitHub pull request, including closing or reopening it.
This does NOT merge. Set state="closed" to close a PR without merging.
Inputs: repo (required), pull_number (required), body, owner, +2 more. Returns: object
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 26 tools
Most tools have clearly distinct resource/action targets, and the descriptions specifically clarify tricky adjacent cases such as issue comments versus pull request comments and workflow rerun variants. The main mild overlap is around comment handling across issues and pull requests, but it is not severe enough to cause frequent misselection.
Tool names consistently use snake_case verb_noun phrasing with a uniform _tool suffix, such as list_issues_tool, create_pull_request_tool, and get_failed_job_logs_tool. The pattern is predictable throughout the entire set.
At 26 tools this server is heavy for a single MCP surface and sits at the upper boundary of what is comfortably scannable. The breadth of GitHub operations explains many of the tools, but the set could still benefit from grouping or trimming.
Core issue, pull request, Actions, repository search, and file-reading workflows are covered. However, notable gaps remain around PR review creation/approval, release listing or updates, and branch/commit operations, so agents may hit dead ends in common GitHub workflows.
Maintenance
Related MCP Connectors
Create, deploy, and operate MCP servers directly from your GitHub repositories.
A MCP server built for developers enabling Git based project management with project and personal…
An MCP server that gives your AI access to the source code and docs of all public github repos
GitHub MCP — wraps the GitHub public REST API (no auth required for public endpoints)
Related MCP Servers
- -licenseNot gradedqualityBmaintenanceMCP Server for the GitHub API, enabling file operations, repository management, search functionality, and more.127,455 npm91,952MIT
- AlicenseCqualityDmaintenanceA production-ready MCP server for GitHub operations, providing tools for repository management, issues, pull requests, and more via both MCP stdio and REST API.27MIT
- FlicenseNot gradedqualityDmaintenanceMCP server for GitHub operations, providing tools for repository management, issues, pull requests, and code search.-
- AlicenseNot gradedqualityNot gradedmaintenanceMCP server for the GitHub API, enabling file operations, repository management, search functionality, issues, pull requests, and more.317 npmMIT