mcp-github-crunchtools 1.1.0__tar.gz → 1.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mcp_github_crunchtools-1.2.0/.github/workflows/ashigaru.yml +98 -0
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/.github/workflows/ci.yml +1 -1
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/.github/workflows/constitution.yml +1 -1
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/.github/workflows/dependabot-automerge.yml +1 -1
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/.github/workflows/gatehouse-retriage.yml +1 -1
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/.github/workflows/gatehouse.yml +2 -2
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/.pre-commit-config.yaml +1 -1
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/.specify/memory/constitution.md +18 -7
- mcp_github_crunchtools-1.2.0/.specify/specs/001-pr-merge-logs-release/spec.md +141 -0
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/CHANGELOG.md +32 -0
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/CLAUDE.md +21 -7
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/Containerfile +3 -3
- mcp_github_crunchtools-1.2.0/PKG-INFO +374 -0
- mcp_github_crunchtools-1.2.0/README.md +348 -0
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/SECURITY.md +105 -15
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/pyproject.toml +2 -2
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/server.json +3 -3
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/src/mcp_github_crunchtools/__init__.py +1 -1
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/src/mcp_github_crunchtools/client.py +160 -28
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/src/mcp_github_crunchtools/models.py +80 -14
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/src/mcp_github_crunchtools/server.py +241 -2
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/src/mcp_github_crunchtools/tools/__init__.py +14 -0
- mcp_github_crunchtools-1.2.0/src/mcp_github_crunchtools/tools/actions.py +389 -0
- mcp_github_crunchtools-1.2.0/src/mcp_github_crunchtools/tools/pull_requests.py +526 -0
- mcp_github_crunchtools-1.2.0/src/mcp_github_crunchtools/tools/releases.py +82 -0
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/tests/conftest.py +30 -3
- mcp_github_crunchtools-1.2.0/tests/test_tools.py +2010 -0
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/tests/test_validation.py +89 -0
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/uv.lock +1 -1
- mcp_github_crunchtools-1.1.0/PKG-INFO +0 -263
- mcp_github_crunchtools-1.1.0/README.md +0 -237
- mcp_github_crunchtools-1.1.0/src/mcp_github_crunchtools/tools/actions.py +0 -187
- mcp_github_crunchtools-1.1.0/src/mcp_github_crunchtools/tools/pull_requests.py +0 -287
- mcp_github_crunchtools-1.1.0/tests/test_tools.py +0 -941
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/.gatehouse-ignore +0 -0
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/.github/dependabot.yml +0 -0
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/.github/workflows/container.yml +0 -0
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/.github/workflows/publish.yml +0 -0
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/.github/workflows/security.yml +0 -0
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/.gitignore +0 -0
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/.specify/specs/000-baseline/spec.md +0 -0
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/.specify/templates/plan-template.md +0 -0
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/.specify/templates/spec-template.md +0 -0
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/LICENSE +0 -0
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/gourmand-exceptions.toml +0 -0
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/gourmand.toml +0 -0
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/src/mcp_github_crunchtools/__main__.py +0 -0
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/src/mcp_github_crunchtools/config.py +0 -0
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/src/mcp_github_crunchtools/errors.py +0 -0
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/src/mcp_github_crunchtools/tools/files.py +0 -0
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/src/mcp_github_crunchtools/tools/issues.py +0 -0
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/src/mcp_github_crunchtools/tools/search.py +0 -0
- {mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/tests/__init__.py +0 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Ashigaru — issues in, fixed bugs out. ONE file enrolls a repo.
|
|
2
|
+
#
|
|
3
|
+
# Drop this in at .github/workflows/ashigaru.yml. The organization supplies the
|
|
4
|
+
# secrets (CLAUDE_CODE_OAUTH_TOKEN, ASHIGARU_APP_ID, ASHIGARU_APP_KEY,
|
|
5
|
+
# OPENROUTER_API_KEY) and the switch (variable ASHIGARU_ENABLED = true).
|
|
6
|
+
#
|
|
7
|
+
# What happens:
|
|
8
|
+
# • a new issue is triaged: bug, documentation and performance issues are
|
|
9
|
+
# labeled ready-to-code and fixed; features are labeled triaged and left
|
|
10
|
+
# for you; unclear ones get needs-info
|
|
11
|
+
# • an issue from someone without write access is triaged too, but is never
|
|
12
|
+
# fixed until a maintainer adds ready-to-code
|
|
13
|
+
# • the fix arrives as a pull request from ashigaru/issue-N with auto-merge on
|
|
14
|
+
# • Gatehouse reviews it like any other; Ashigaru answers each finding
|
|
15
|
+
# (`fixed in <sha>` or `not a bug: <reason>`) for up to five rounds, then
|
|
16
|
+
# labels the pull request needs-human and stops
|
|
17
|
+
# • GitHub merges when the required checks pass. No job here can merge.
|
|
18
|
+
#
|
|
19
|
+
# To steer it by hand: add `ready-to-code` to have an issue fixed,
|
|
20
|
+
# `ready-for-triage` to have it looked at again.
|
|
21
|
+
#
|
|
22
|
+
# Where the checks are. This file only routes events. The workflows it calls
|
|
23
|
+
# do the checking, at the pinned release, before any agent runs:
|
|
24
|
+
# • the switch: every called workflow stops at its first job unless the
|
|
25
|
+
# organization variable ASHIGARU_ENABLED is true
|
|
26
|
+
# • who may reach an agent: scripts/guard.py asks the API for the write
|
|
27
|
+
# access of the issue's author and of whoever triggered the event
|
|
28
|
+
# • which pull requests are touched: fix.yml acts only on ashigaru/ branches
|
|
29
|
+
# in this repository, opened by the Ashigaru app
|
|
30
|
+
# • what the secrets can do: the job that runs the model gets a read-only
|
|
31
|
+
# token; the app key is used only by jobs that run no model
|
|
32
|
+
#
|
|
33
|
+
# Copy unchanged apart from the pins, which Dependabot keeps current.
|
|
34
|
+
|
|
35
|
+
name: Ashigaru
|
|
36
|
+
|
|
37
|
+
on:
|
|
38
|
+
issues:
|
|
39
|
+
types: [opened, labeled]
|
|
40
|
+
workflow_run:
|
|
41
|
+
workflows: [Gatehouse]
|
|
42
|
+
types: [completed]
|
|
43
|
+
|
|
44
|
+
permissions: {}
|
|
45
|
+
|
|
46
|
+
# One run at a time per issue or per pull request branch; a second event waits.
|
|
47
|
+
concurrency:
|
|
48
|
+
group: ashigaru-${{ github.event.issue.number || github.event.workflow_run.head_branch }}
|
|
49
|
+
cancel-in-progress: false
|
|
50
|
+
|
|
51
|
+
jobs:
|
|
52
|
+
triage:
|
|
53
|
+
name: Triage
|
|
54
|
+
if: >-
|
|
55
|
+
github.event_name == 'issues' &&
|
|
56
|
+
(github.event.action == 'opened' || github.event.label.name == 'ready-for-triage')
|
|
57
|
+
permissions:
|
|
58
|
+
contents: read
|
|
59
|
+
issues: write
|
|
60
|
+
uses: crunchtools/ashigaru/.github/workflows/triage.yml@v2
|
|
61
|
+
secrets:
|
|
62
|
+
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
63
|
+
|
|
64
|
+
code:
|
|
65
|
+
name: Code
|
|
66
|
+
needs: triage
|
|
67
|
+
# After a triage that said ready-to-code, or when a maintainer adds the label.
|
|
68
|
+
if: >-
|
|
69
|
+
always() && github.event_name == 'issues' &&
|
|
70
|
+
(needs.triage.outputs.auto_code == 'true' ||
|
|
71
|
+
(github.event.action == 'labeled' && github.event.label.name == 'ready-to-code'))
|
|
72
|
+
permissions:
|
|
73
|
+
contents: read
|
|
74
|
+
issues: read
|
|
75
|
+
uses: crunchtools/ashigaru/.github/workflows/code.yml@v2
|
|
76
|
+
secrets:
|
|
77
|
+
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
78
|
+
ASHIGARU_APP_ID: ${{ secrets.ASHIGARU_APP_ID }}
|
|
79
|
+
ASHIGARU_APP_KEY: ${{ secrets.ASHIGARU_APP_KEY }}
|
|
80
|
+
OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
|
|
81
|
+
|
|
82
|
+
fix:
|
|
83
|
+
name: Fix
|
|
84
|
+
if: >-
|
|
85
|
+
github.event_name == 'workflow_run' &&
|
|
86
|
+
github.event.workflow_run.event == 'pull_request_target' &&
|
|
87
|
+
github.event.workflow_run.head_repository.full_name == github.repository &&
|
|
88
|
+
startsWith(github.event.workflow_run.head_branch, 'ashigaru/')
|
|
89
|
+
permissions:
|
|
90
|
+
contents: read
|
|
91
|
+
pull-requests: read
|
|
92
|
+
checks: read
|
|
93
|
+
actions: read
|
|
94
|
+
uses: crunchtools/ashigaru/.github/workflows/fix.yml@v2
|
|
95
|
+
secrets:
|
|
96
|
+
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
97
|
+
ASHIGARU_APP_ID: ${{ secrets.ASHIGARU_APP_ID }}
|
|
98
|
+
ASHIGARU_APP_KEY: ${{ secrets.ASHIGARU_APP_KEY }}
|
|
@@ -20,4 +20,4 @@ jobs:
|
|
|
20
20
|
if: github.event.workflow_run.event == 'pull_request_review_comment'
|
|
21
21
|
permissions:
|
|
22
22
|
actions: write # re-run the triage job; nothing else
|
|
23
|
-
uses: crunchtools/gatehouse/.github/workflows/retriage.yml@v0
|
|
23
|
+
uses: crunchtools/gatehouse/.github/workflows/retriage.yml@v0
|
{mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/.github/workflows/gatehouse.yml
RENAMED
|
@@ -90,7 +90,7 @@ jobs:
|
|
|
90
90
|
permissions:
|
|
91
91
|
contents: read
|
|
92
92
|
pull-requests: write
|
|
93
|
-
uses: crunchtools/gatehouse/.github/workflows/review.yml@v0
|
|
93
|
+
uses: crunchtools/gatehouse/.github/workflows/review.yml@v0
|
|
94
94
|
# Advisory by default — findings post as comments, the check always passes.
|
|
95
95
|
# Uncomment to let critical/high findings fail the check (still not required):
|
|
96
96
|
# with:
|
|
@@ -107,4 +107,4 @@ jobs:
|
|
|
107
107
|
if: always()
|
|
108
108
|
permissions:
|
|
109
109
|
pull-requests: read
|
|
110
|
-
uses: crunchtools/gatehouse/.github/workflows/triage.yml@v0
|
|
110
|
+
uses: crunchtools/gatehouse/.github/workflows/triage.yml@v0
|
{mcp_github_crunchtools-1.1.0 → mcp_github_crunchtools-1.2.0}/.specify/memory/constitution.md
RENAMED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# mcp-github-crunchtools Constitution
|
|
2
2
|
|
|
3
|
-
> **Version:** 1.
|
|
3
|
+
> **Version:** 1.2.0
|
|
4
4
|
> **Ratified:** 2026-06-14
|
|
5
|
-
> **Amended:** 2026-10-
|
|
5
|
+
> **Amended:** 2026-10-12
|
|
6
6
|
> **Status:** Active
|
|
7
|
-
> **Inherits:** [crunchtools/constitution](https://github.com/crunchtools/constitution) v1.
|
|
7
|
+
> **Inherits:** [crunchtools/constitution](https://github.com/crunchtools/constitution) v1.24.0
|
|
8
8
|
> **Profile:** MCP Server
|
|
9
9
|
|
|
10
10
|
This file holds what is specific to mcp-github. The fleet rules and the MCP
|
|
@@ -19,13 +19,22 @@ restated here.
|
|
|
19
19
|
the environment only. `GitHubApiError` scrubs it from messages, and
|
|
20
20
|
`Config.__repr__()`/`__str__()` never expose it.
|
|
21
21
|
- **Input limits:** Pydantic models with `extra="forbid"`; issue and PR
|
|
22
|
-
states are allowlisted (`open`, `closed`, `all`)
|
|
22
|
+
states are allowlisted (`open`, `closed`, `all`), as are merge methods
|
|
23
|
+
(`merge`, `squash`, `rebase`); owner and repo names are
|
|
23
24
|
checked for injection characters; git refs follow check-ref-format
|
|
24
|
-
rules (no `..`, `//` or leading/trailing `.` or `/`)
|
|
25
|
+
rules (no `..`, `//` or leading/trailing `.` or `/`); an expected head SHA
|
|
26
|
+
is a full 40 or 64 character hex SHA. `NotFoundError`
|
|
25
27
|
truncates long identifiers.
|
|
26
28
|
- **API:** `Authorization: Bearer` header, never the URL; the
|
|
27
29
|
`X-GitHub-Api-Version` header is pinned to `2022-11-28`; path parameters
|
|
28
30
|
are URL-encoded; requests time out after 30s; responses are capped at 10MB.
|
|
31
|
+
- **Redirects:** the API client follows none. A job log, which GitHub serves
|
|
32
|
+
as a redirect to a pre-signed storage URL, is fetched by a separate client
|
|
33
|
+
that has no token, over HTTPS to a named host only, streamed, with only its
|
|
34
|
+
tail kept. The signed URL is never logged or put in an error message.
|
|
35
|
+
- **Result caps:** tools that gather several pages say when they cut:
|
|
36
|
+
300 items per kind for pull request comments, 300 jobs per run, and for
|
|
37
|
+
failed-job logs 1000 lines per job, 5 jobs and 256 KiB per result.
|
|
29
38
|
- **Surface:** pure API wrappers. No filesystem access, shell execution or
|
|
30
39
|
code evaluation.
|
|
31
40
|
|
|
@@ -42,8 +51,9 @@ The server works with github.com and GitHub Enterprise Server:
|
|
|
42
51
|
|
|
43
52
|
## Tool Groups
|
|
44
53
|
|
|
45
|
-
Issues, pull requests, Actions, files and search
|
|
46
|
-
tested for 401, 404, 429 and 204
|
|
54
|
+
Issues, pull requests, Actions, releases, files and search: 25 tools, of which
|
|
55
|
+
14 only read. Client error handling is tested for 401, 404, 429 and 204
|
|
56
|
+
responses in `TestClientErrorHandling`.
|
|
47
57
|
|
|
48
58
|
## Instance
|
|
49
59
|
|
|
@@ -62,3 +72,4 @@ tested for 401, 404, 429 and 204 responses in `TestClientErrorHandling`.
|
|
|
62
72
|
| 1.0.0 | 2026-06-14 | Initial constitution (ported from mcp-gitlab-crunchtools) |
|
|
63
73
|
| 1.0.1 | 2026-09-25 | Inherit constitution v1.17.0 (Gatehouse gates) |
|
|
64
74
|
| 1.1.0 | 2026-10-02 | Manifest under constitution v1.18.0: profile restatement removed, mcp-github specifics kept; `..` rule moved from file paths to git refs to match the code |
|
|
75
|
+
| 1.2.0 | 2026-10-12 | Seven tools for pull requests, Actions runs and logs, and releases (spec 001): redirect handling, result caps, merge-method and SHA validation, releases tool group |
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# Specification: Pull Request, Run Log and Release Tools
|
|
2
|
+
|
|
3
|
+
> **Spec ID:** 001-pr-merge-logs-release
|
|
4
|
+
> **Status:** Implemented
|
|
5
|
+
> **Version:** 1.2.0
|
|
6
|
+
> **Author:** crunchtools.com
|
|
7
|
+
> **Date:** 2026-10-12
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
An agent that takes a change from a branch to a release needs to open the pull
|
|
12
|
+
request, read what reviewers and CI said about it, answer them, merge, and
|
|
13
|
+
publish. Until now it did the missing steps with the `gh` CLI, which hands it
|
|
14
|
+
other people's text with no gateway in between. These seven tools close that
|
|
15
|
+
gap so the whole loop runs through the MCP server.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## New Tools
|
|
20
|
+
|
|
21
|
+
| Tool | Method | Endpoint | Description |
|
|
22
|
+
|------|--------|----------|-------------|
|
|
23
|
+
| `create_pull_request_tool` | POST | `/repos/{owner}/{repo}/pulls` | Open a pull request (write) |
|
|
24
|
+
| `merge_pull_request_tool` | PUT | `/repos/{owner}/{repo}/pulls/{pull_number}/merge` | Merge, squash or rebase; optional expected head SHA (write) |
|
|
25
|
+
| `list_pull_request_comments_tool` | GET | `.../issues/{n}/comments` + `.../pulls/{n}/comments` + `.../pulls/{n}/reviews` | One list of everything said on a PR |
|
|
26
|
+
| `reply_to_review_comment_tool` | POST | `/repos/{owner}/{repo}/pulls/{pull_number}/comments/{comment_id}/replies` | Reply in a review thread (write) |
|
|
27
|
+
| `get_workflow_run_tool` | GET | `/repos/{owner}/{repo}/actions/runs/{run_id}` + `.../jobs` | Run status and jobs, no logs |
|
|
28
|
+
| `get_failed_job_logs_tool` | GET | `.../actions/runs/{run_id}/jobs` + `.../actions/jobs/{job_id}/logs` | Tail of each failed job's log |
|
|
29
|
+
| `create_release_tool` | POST | `/repos/{owner}/{repo}/releases` | Create a release and its tag (write) |
|
|
30
|
+
|
|
31
|
+
The three GET tools publish `readOnlyHint`. Not in scope: creating or forking
|
|
32
|
+
repositories, deleting packages, further search tools.
|
|
33
|
+
|
|
34
|
+
### Result shapes and caps
|
|
35
|
+
|
|
36
|
+
| Tool | Cap | How the result reports it |
|
|
37
|
+
|------|-----|---------------------------|
|
|
38
|
+
| `list_pull_request_comments_tool` | 300 items per kind, read oldest first (at most 9 requests) | `truncated`, `truncated_kinds` |
|
|
39
|
+
| `get_workflow_run_tool` | 300 jobs | `jobs_truncated` |
|
|
40
|
+
| `get_failed_job_logs_tool` | `tail_lines` at most 1000 (default 200); 5 failed jobs; 256 KiB of log text per result, split evenly between jobs; 20 MB per download | `tail_lines` (value used), `failed_jobs_omitted`, per-job and overall `truncated` |
|
|
41
|
+
|
|
42
|
+
A job is failed when its conclusion is `failure` or `timed_out`. Cancelled
|
|
43
|
+
jobs are left out: in a failed run they are mostly siblings stopped by
|
|
44
|
+
fail-fast, and their logs say nothing about the cause.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Security Considerations
|
|
49
|
+
|
|
50
|
+
### Layer 1 — Token Protection
|
|
51
|
+
|
|
52
|
+
- GitHub serves a job log as a 302 to a pre-signed URL on a storage host. The
|
|
53
|
+
API client is created with `follow_redirects=False`; the redirect target is
|
|
54
|
+
fetched by a second client that has no default headers, so the token is not
|
|
55
|
+
sent there.
|
|
56
|
+
- The signed URL is a short-lived credential. It is not logged, not put in an
|
|
57
|
+
error message, and not kept in an exception chain.
|
|
58
|
+
|
|
59
|
+
### Layer 2 — Input Validation
|
|
60
|
+
|
|
61
|
+
- `MERGE_METHODS` allowlist; `validate_commit_sha` for the expected head;
|
|
62
|
+
`validate_head` for `branch` or `owner:branch`; `validate_ref` for base,
|
|
63
|
+
tag and target.
|
|
64
|
+
- `CreateReleaseInput` for the release title and notes. Pull request title
|
|
65
|
+
and body reuse `CreateIssueInput`, and a review reply reuses
|
|
66
|
+
`CreateIssueCommentInput`: the limits are the same.
|
|
67
|
+
- Blank optional values (`sha`, `target_commitish`, `name`) mean "not given".
|
|
68
|
+
|
|
69
|
+
### Layer 3 — API Hardening
|
|
70
|
+
|
|
71
|
+
- The redirect target must be HTTPS, name a host rather than an IP address,
|
|
72
|
+
and carry no embedded credentials. It is not redirected further and must
|
|
73
|
+
answer 200. The host is not held to a list of names: GitHub documents no
|
|
74
|
+
stable set of log-storage hosts, and GHES storage is the operator's.
|
|
75
|
+
- The download is streamed with a rolling 256 KiB tail and abandoned past
|
|
76
|
+
20 MB. Paging loops are bounded by a page count, not by what the server
|
|
77
|
+
says.
|
|
78
|
+
|
|
79
|
+
### Layer 4 — Dangerous Operation Prevention
|
|
80
|
+
|
|
81
|
+
- No filesystem, shell or code evaluation. Logs are held in memory only.
|
|
82
|
+
- Comments, reviews and logs are text written by others. The server returns
|
|
83
|
+
them as data and bounds their size; it does not interpret them.
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Module Changes
|
|
88
|
+
|
|
89
|
+
### New Files
|
|
90
|
+
|
|
91
|
+
| File | Purpose |
|
|
92
|
+
|------|---------|
|
|
93
|
+
| `tools/releases.py` | `create_release` |
|
|
94
|
+
|
|
95
|
+
### Modified Files
|
|
96
|
+
|
|
97
|
+
| File | Changes |
|
|
98
|
+
|------|---------|
|
|
99
|
+
| `client.py` | `_send` split out of `_request`; `get_redirected_tail`; `follow_redirects=False` |
|
|
100
|
+
| `models.py` | `MERGE_METHODS`, `validate_head`, `validate_commit_sha`, `CreateReleaseInput` |
|
|
101
|
+
| `tools/pull_requests.py` | `create_pull_request`, `merge_pull_request`, `list_pull_request_comments`, `reply_to_review_comment` |
|
|
102
|
+
| `tools/actions.py` | `get_workflow_run`, `get_failed_job_logs` |
|
|
103
|
+
| `tools/__init__.py` | Exports |
|
|
104
|
+
| `server.py` | Seven `@mcp.tool` wrappers |
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Testing Requirements
|
|
109
|
+
|
|
110
|
+
### Mocked API Tests
|
|
111
|
+
|
|
112
|
+
- [x] Happy path, validation refusal and GitHub error path for each tool
|
|
113
|
+
- [x] Caps: comments per kind, jobs per run, `tail_lines`, bytes, failed jobs
|
|
114
|
+
- [x] The storage request carries no `Authorization` header (real httpx
|
|
115
|
+
client over a mock transport, so the assertion is on the request as sent)
|
|
116
|
+
- [x] Redirects that are not HTTPS, carry credentials or address an IP are
|
|
117
|
+
refused; storage errors and transport failures do not expose the
|
|
118
|
+
signed URL
|
|
119
|
+
|
|
120
|
+
### Input Validation Tests
|
|
121
|
+
|
|
122
|
+
- [x] `validate_head`, `validate_commit_sha`, `CreateReleaseInput`
|
|
123
|
+
|
|
124
|
+
### Tool Count Update
|
|
125
|
+
|
|
126
|
+
- [x] `test_tool_count` is 25; `READ_ONLY` and `WRITES` updated
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Dependencies
|
|
131
|
+
|
|
132
|
+
- Depends on: 000-baseline
|
|
133
|
+
- Blocks: none
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Changelog
|
|
138
|
+
|
|
139
|
+
| Version | Date | Changes |
|
|
140
|
+
|---------|------|---------|
|
|
141
|
+
| 1.2.0 | 2026-10-12 | Implemented |
|
|
@@ -6,6 +6,38 @@ All notable changes to this project are documented here. The format follows
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [1.2.0] - 2026-10-12
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- `create_pull_request_tool`: open a pull request, optionally as a draft; the
|
|
14
|
+
head may be `owner:branch` for a fork.
|
|
15
|
+
- `merge_pull_request_tool`: merge, squash or rebase a pull request. `sha` is
|
|
16
|
+
the expected head commit; GitHub refuses the merge if the branch has moved.
|
|
17
|
+
- `list_pull_request_comments_tool`: conversation comments, diff comments and
|
|
18
|
+
reviews in one chronological list, each tagged with its kind, author and id.
|
|
19
|
+
Each kind is capped at 300 items and the result says when one was cut.
|
|
20
|
+
- `reply_to_review_comment_tool`: reply in the thread of a diff comment.
|
|
21
|
+
- `get_workflow_run_tool`: one run's status, conclusion, head SHA and branch,
|
|
22
|
+
event and jobs, without logs.
|
|
23
|
+
- `get_failed_job_logs_tool`: the last lines of the log of each failed job in
|
|
24
|
+
a run, with ANSI escapes and timestamps removed. Bounded to 1000 lines per
|
|
25
|
+
job, 5 jobs and 256 KiB per result; the result says when something was cut.
|
|
26
|
+
- `create_release_tool`: create a release and, if needed, its tag.
|
|
27
|
+
- README and SECURITY.md list the fine-grained token permission each tool
|
|
28
|
+
needs.
|
|
29
|
+
|
|
30
|
+
### Security
|
|
31
|
+
|
|
32
|
+
- The API client is created with `follow_redirects=False`. GitHub redirects a
|
|
33
|
+
job log to a pre-signed storage URL; that URL is fetched by a separate
|
|
34
|
+
client that has no token, over HTTPS only, and is never logged or put in an
|
|
35
|
+
error message.
|
|
36
|
+
|
|
37
|
+
### Changed
|
|
38
|
+
|
|
39
|
+
- README lists all 25 tools; it had stopped at the first eleven.
|
|
40
|
+
|
|
9
41
|
## [1.1.0] - 2026-10-10
|
|
10
42
|
|
|
11
43
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Claude Code Instructions
|
|
2
2
|
|
|
3
|
-
Secure MCP server for the GitHub REST API with
|
|
3
|
+
Secure MCP server for the GitHub REST API with 25 tools across issues, pull requests, Actions, releases, files, and search. Works with github.com and GitHub Enterprise Server.
|
|
4
4
|
|
|
5
5
|
## Quick Start
|
|
6
6
|
|
|
@@ -32,8 +32,12 @@ claude mcp add mcp-github-crunchtools \
|
|
|
32
32
|
|
|
33
33
|
1. Go to https://github.com/settings/tokens
|
|
34
34
|
2. Create a fine-grained or classic PAT named `mcp-github-crunchtools`
|
|
35
|
-
3. Grant read access to repository contents, issues,
|
|
36
|
-
|
|
35
|
+
3. Grant read access to repository contents, issues, pull requests, and
|
|
36
|
+
Actions. Add write only for the write tools you use: Issues and Pull
|
|
37
|
+
requests for commenting, opening and updating; Contents for
|
|
38
|
+
`merge_pull_request_tool` and `create_release_tool`; Actions for
|
|
39
|
+
`trigger_workflow_tool` and `rerun_*`. The per-tool table is in README.md
|
|
40
|
+
under "Token permissions".
|
|
37
41
|
4. Copy the token immediately (shown only once)
|
|
38
42
|
|
|
39
43
|
## Environment Variables
|
|
@@ -46,28 +50,38 @@ claude mcp add mcp-github-crunchtools \
|
|
|
46
50
|
| `SSL_CERT_FILE` | No | — | Custom CA bundle for self-hosted instances |
|
|
47
51
|
| `GITHUB_SSL_VERIFY` | No | `true` | Set `false` to disable SSL verification |
|
|
48
52
|
|
|
49
|
-
## Available Tools (
|
|
53
|
+
## Available Tools (25)
|
|
50
54
|
|
|
51
55
|
| Category | Tools |
|
|
52
56
|
|----------|-------|
|
|
53
57
|
| Issues | `list_issues_tool`, `get_issue_tool`, `create_issue_tool`, `create_issue_comment_tool`, `update_issue_tool` |
|
|
54
|
-
| Pull Requests | `list_pull_requests_tool`, `get_pull_request_tool`, `get_pull_request_diff_tool`, `get_pull_request_checks_tool`, `update_pull_request_tool` |
|
|
58
|
+
| Pull Requests | `list_pull_requests_tool`, `get_pull_request_tool`, `get_pull_request_diff_tool`, `get_pull_request_checks_tool`, `list_pull_request_comments_tool`, `create_pull_request_tool`, `update_pull_request_tool`, `reply_to_review_comment_tool`, `merge_pull_request_tool` |
|
|
55
59
|
| Files | `get_file_content_tool`, `list_repo_tree_tool` |
|
|
56
60
|
| Search | `search_code_tool`, `search_issues_tool` |
|
|
57
|
-
| Actions | `list_workflow_runs_tool`, `trigger_workflow_tool`, `rerun_workflow_run_tool`, `rerun_failed_jobs_tool` |
|
|
61
|
+
| Actions | `list_workflow_runs_tool`, `get_workflow_run_tool`, `get_failed_job_logs_tool`, `trigger_workflow_tool`, `rerun_workflow_run_tool`, `rerun_failed_jobs_tool` |
|
|
62
|
+
| Releases | `create_release_tool` |
|
|
58
63
|
|
|
59
|
-
Write tools (`
|
|
64
|
+
Write tools (`create_*`, `update_*`, `merge_pull_request_tool`,
|
|
65
|
+
`reply_to_review_comment_tool`, `trigger_workflow_tool`, `rerun_*`)
|
|
60
66
|
are scoped per-profile by the gateway. `trigger_workflow_tool` dispatches a
|
|
61
67
|
fresh run (via `workflow_dispatch`) and is the way to *force* a build —
|
|
62
68
|
`rerun_*` only re-run an existing run and GitHub rejects runs older than 30
|
|
63
69
|
days.
|
|
64
70
|
|
|
71
|
+
`get_failed_job_logs_tool` is the one tool that talks to a host other than the
|
|
72
|
+
API: GitHub redirects a job log to a storage URL. `GitHubClient.get_redirected_tail`
|
|
73
|
+
follows it with a separate client that has no token. Keep it that way.
|
|
74
|
+
|
|
65
75
|
## Example Usage
|
|
66
76
|
|
|
67
77
|
```
|
|
68
78
|
List open issues for crunchtools/mcp-github
|
|
69
79
|
Get the diff for PR #5 in crunchtools/mcp-github
|
|
70
80
|
Show CI checks for pull request 5
|
|
81
|
+
Show the failed job logs for run 123456
|
|
82
|
+
List the review comments on PR 5 and reply to the first one
|
|
83
|
+
Squash-merge PR 5 in crunchtools/mcp-github
|
|
84
|
+
Create release v1.2.0 for crunchtools/mcp-github
|
|
71
85
|
Read src/server.py from crunchtools/mcp-github
|
|
72
86
|
Search code for "FastMCP" in repo:crunchtools/mcp-github
|
|
73
87
|
Search issues for "is:open label:bug org:crunchtools"
|
|
@@ -27,15 +27,15 @@ FROM quay.io/hummingbird/python:latest
|
|
|
27
27
|
|
|
28
28
|
# Labels for container metadata
|
|
29
29
|
LABEL name="mcp-github-crunchtools" \
|
|
30
|
-
version="1.
|
|
31
|
-
summary="Secure MCP server for GitHub issues, pull requests, files, and search" \
|
|
30
|
+
version="1.2.0" \
|
|
31
|
+
summary="Secure MCP server for GitHub issues, pull requests, Actions, releases, files, and search" \
|
|
32
32
|
description="A security-focused MCP server for GitHub built on Red Hat UBI" \
|
|
33
33
|
maintainer="crunchtools.com" \
|
|
34
34
|
url="https://github.com/crunchtools/mcp-github" \
|
|
35
35
|
io.k8s.display-name="MCP GitHub CrunchTools" \
|
|
36
36
|
io.openshift.tags="mcp,github,devops" \
|
|
37
37
|
org.opencontainers.image.source="https://github.com/crunchtools/mcp-github" \
|
|
38
|
-
org.opencontainers.image.description="Secure MCP server for GitHub issues, pull requests, files, and search" \
|
|
38
|
+
org.opencontainers.image.description="Secure MCP server for GitHub issues, pull requests, Actions, releases, files, and search" \
|
|
39
39
|
org.opencontainers.image.licenses="AGPL-3.0-or-later"
|
|
40
40
|
|
|
41
41
|
WORKDIR /app
|