mcp-github-crunchtools 1.3.2__tar.gz → 1.5.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.3.2 → mcp_github_crunchtools-1.5.0}/CHANGELOG.md +33 -0
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/CLAUDE.md +3 -3
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/Containerfile +1 -1
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/PKG-INFO +27 -13
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/README.md +26 -12
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/pyproject.toml +1 -1
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/server.json +2 -2
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/src/mcp_github_crunchtools/__init__.py +1 -1
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/src/mcp_github_crunchtools/errors.py +1 -0
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/src/mcp_github_crunchtools/server.py +42 -3
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/src/mcp_github_crunchtools/tools/__init__.py +2 -0
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/src/mcp_github_crunchtools/tools/issues.py +57 -3
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/src/mcp_github_crunchtools/tools/pull_requests.py +188 -29
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/tests/test_merge_gate.py +288 -4
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/tests/test_tools.py +102 -3
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/uv.lock +1 -1
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/.gatehouse-ignore +0 -0
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/.github/dependabot.yml +0 -0
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/.github/workflows/ashigaru.yml +0 -0
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/.github/workflows/ci.yml +0 -0
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/.github/workflows/constitution.yml +0 -0
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/.github/workflows/container.yml +0 -0
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/.github/workflows/dependabot-automerge.yml +0 -0
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/.github/workflows/gatehouse-retriage.yml +0 -0
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/.github/workflows/gatehouse.yml +0 -0
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/.github/workflows/publish.yml +0 -0
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/.github/workflows/security.yml +0 -0
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/.gitignore +0 -0
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/.pre-commit-config.yaml +0 -0
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/.specify/memory/constitution.md +0 -0
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/.specify/specs/000-baseline/spec.md +0 -0
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/.specify/specs/001-pr-merge-logs-release/spec.md +0 -0
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/.specify/specs/002-request-token-merge-gate/spec.md +0 -0
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/.specify/templates/plan-template.md +0 -0
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/.specify/templates/spec-template.md +0 -0
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/LICENSE +0 -0
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/SECURITY.md +0 -0
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/gourmand-exceptions.toml +0 -0
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/gourmand.toml +0 -0
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/src/mcp_github_crunchtools/__main__.py +0 -0
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/src/mcp_github_crunchtools/client.py +0 -0
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/src/mcp_github_crunchtools/config.py +0 -0
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/src/mcp_github_crunchtools/models.py +0 -0
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/src/mcp_github_crunchtools/tools/actions.py +0 -0
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/src/mcp_github_crunchtools/tools/files.py +0 -0
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/src/mcp_github_crunchtools/tools/releases.py +0 -0
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/src/mcp_github_crunchtools/tools/search.py +0 -0
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/tests/__init__.py +0 -0
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/tests/conftest.py +0 -0
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/tests/test_request_token.py +0 -0
- {mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/tests/test_validation.py +0 -0
|
@@ -6,6 +6,39 @@ All notable changes to this project are documented here. The format follows
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [1.5.0] - 2026-10-20
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- `merge_pull_request_tool` and `get_pull_request_checks_tool` work with a
|
|
14
|
+
fine-grained token. GitHub cannot grant one the Checks permission, so
|
|
15
|
+
check-runs answers 403; the tools then read the head commit's Actions jobs
|
|
16
|
+
(Actions: read), each of which is a check run with the same id and name.
|
|
17
|
+
Only Actions-reported checks are visible that way, so the merge gate
|
|
18
|
+
refuses a required check that names another app, or that accepts any app
|
|
19
|
+
and has no commit status. Both results carry `checks_source`.
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- `get_pull_request_checks_tool` reads every page of check runs, not just the
|
|
24
|
+
first, up to the merge gate's page limit; past it the verdict says
|
|
25
|
+
`truncated` and is not `ready_to_merge`.
|
|
26
|
+
|
|
27
|
+
## [1.4.0] - 2026-10-17
|
|
28
|
+
|
|
29
|
+
### Added
|
|
30
|
+
|
|
31
|
+
- `list_issue_comments_tool`: the comments on an issue or a pull request
|
|
32
|
+
conversation, oldest first, one page at a time, with id, author,
|
|
33
|
+
author_association, created_at, updated_at, body and html_url (#45).
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
|
|
37
|
+
- `list_pull_request_comments_tool` called on a plain issue returned GitHub's
|
|
38
|
+
403 "Resource not accessible by personal access token", which reads like a
|
|
39
|
+
token scope problem. It now says the number is an issue and points to
|
|
40
|
+
`list_issue_comments_tool` (#45).
|
|
41
|
+
|
|
9
42
|
## [1.3.2] - 2026-10-17
|
|
10
43
|
|
|
11
44
|
### Fixed
|
|
@@ -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 26 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
|
|
|
@@ -51,11 +51,11 @@ claude mcp add mcp-github-crunchtools \
|
|
|
51
51
|
| `SSL_CERT_FILE` | No | — | Custom CA bundle for self-hosted instances |
|
|
52
52
|
| `GITHUB_SSL_VERIFY` | No | `true` | Set `false` to disable SSL verification |
|
|
53
53
|
|
|
54
|
-
## Available Tools (
|
|
54
|
+
## Available Tools (26)
|
|
55
55
|
|
|
56
56
|
| Category | Tools |
|
|
57
57
|
|----------|-------|
|
|
58
|
-
| Issues | `list_issues_tool`, `get_issue_tool`, `create_issue_tool`, `create_issue_comment_tool`, `update_issue_tool` |
|
|
58
|
+
| Issues | `list_issues_tool`, `get_issue_tool`, `list_issue_comments_tool`, `create_issue_tool`, `create_issue_comment_tool`, `update_issue_tool` |
|
|
59
59
|
| 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` |
|
|
60
60
|
| Files | `get_file_content_tool`, `list_repo_tree_tool` |
|
|
61
61
|
| Search | `search_code_tool`, `search_issues_tool` |
|
|
@@ -27,7 +27,7 @@ FROM quay.io/hummingbird/python:latest
|
|
|
27
27
|
|
|
28
28
|
# Labels for container metadata
|
|
29
29
|
LABEL name="mcp-github-crunchtools" \
|
|
30
|
-
version="1.
|
|
30
|
+
version="1.5.0" \
|
|
31
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" \
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: mcp-github-crunchtools
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.5.0
|
|
4
4
|
Summary: Secure MCP server for GitHub issues, pull requests, Actions, releases, files, and search
|
|
5
5
|
Author: crunchtools.com
|
|
6
6
|
License-Expression: AGPL-3.0-or-later
|
|
@@ -60,7 +60,7 @@ The container image is built on the [Hummingbird Python base image](https://quay
|
|
|
60
60
|
|
|
61
61
|
## Features
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
26 tools. Tools marked *write* change something on GitHub; the others only
|
|
64
64
|
read and publish `readOnlyHint`. The last column is the fine-grained token
|
|
65
65
|
permission the tool needs (see [Token permissions](#token-permissions)).
|
|
66
66
|
|
|
@@ -70,6 +70,7 @@ permission the tool needs (see [Token permissions](#token-permissions)).
|
|
|
70
70
|
|------|--------------|------------|
|
|
71
71
|
| `list_issues_tool` | List issues for a repository (pull requests excluded) | Issues: read |
|
|
72
72
|
| `get_issue_tool` | Get a single issue by number | Issues: read |
|
|
73
|
+
| `list_issue_comments_tool` | Comments on an issue or a pull request conversation, oldest first | Issues: read, or Pull requests: read for a PR |
|
|
73
74
|
| `create_issue_tool` | Open an issue (*write*) | Issues: write |
|
|
74
75
|
| `create_issue_comment_tool` | Comment on an issue or pull request (*write*) | Issues: write, or Pull requests: write for a PR |
|
|
75
76
|
| `update_issue_tool` | Edit, close or reopen an issue (*write*) | Issues: write |
|
|
@@ -81,12 +82,12 @@ permission the tool needs (see [Token permissions](#token-permissions)).
|
|
|
81
82
|
| `list_pull_requests_tool` | List pull requests for a repository | Pull requests: read |
|
|
82
83
|
| `get_pull_request_tool` | Get a single pull request by number | Pull requests: read |
|
|
83
84
|
| `get_pull_request_diff_tool` | Get the unified diff for a pull request | Pull requests: read |
|
|
84
|
-
| `get_pull_request_checks_tool` | CI verdict from check-runs and commit statuses; skipped is not failed | Pull requests: read, Commit statuses: read (and see the note under Token permissions) |
|
|
85
|
+
| `get_pull_request_checks_tool` | 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) |
|
|
85
86
|
| `list_pull_request_comments_tool` | Conversation comments, diff comments and reviews in one list, each tagged with its kind | Pull requests: read |
|
|
86
87
|
| `create_pull_request_tool` | Open a pull request, optionally as a draft (*write*) | Pull requests: write |
|
|
87
88
|
| `update_pull_request_tool` | Edit, close or reopen a pull request; does not merge (*write*) | Pull requests: write |
|
|
88
89
|
| `reply_to_review_comment_tool` | Reply in the thread of a diff comment (*write*) | Pull requests: write |
|
|
89
|
-
| `merge_pull_request_tool` | Merge, squash or rebase a pull request, only once the base branch's required checks are green on its head (see [The merge gate](#the-merge-gate)) (*write*) | Contents: write; Pull requests
|
|
90
|
+
| `merge_pull_request_tool` | Merge, squash or rebase a pull request, only once the base branch's required checks are green on its head (see [The merge gate](#the-merge-gate)) (*write*) | Contents: write; Pull requests, Commit statuses and Actions: read |
|
|
90
91
|
|
|
91
92
|
### Actions (6 tools)
|
|
92
93
|
|
|
@@ -123,7 +124,10 @@ permission the tool needs (see [Token permissions](#token-permissions)).
|
|
|
123
124
|
|
|
124
125
|
- `list_pull_request_comments_tool` reads each of its three kinds oldest
|
|
125
126
|
first, up to 300 items per kind. `truncated` and `truncated_kinds` say when
|
|
126
|
-
newer items were left out.
|
|
127
|
+
newer items were left out. Called on a plain issue it says so; use
|
|
128
|
+
`list_issue_comments_tool` there.
|
|
129
|
+
- `list_issue_comments_tool` returns one page (up to 100 comments); follow
|
|
130
|
+
`pagination.next_page` for more.
|
|
127
131
|
- `get_workflow_run_tool` lists up to 300 jobs; `jobs_truncated` says when the
|
|
128
132
|
run has more.
|
|
129
133
|
- `get_failed_job_logs_tool` returns at most 1000 lines per job (200 by
|
|
@@ -218,16 +222,25 @@ For a fine-grained personal access token, the repository permissions are:
|
|
|
218
222
|
|------------|--------------------|---------------------|
|
|
219
223
|
| Metadata | every tool (GitHub grants it with any other permission) | — |
|
|
220
224
|
| Contents | `get_file_content_tool`, `list_repo_tree_tool` | `merge_pull_request_tool`, `create_release_tool` |
|
|
221
|
-
| Issues | `list_issues_tool`, `get_issue_tool` | `create_issue_tool`, `update_issue_tool`, `create_issue_comment_tool` |
|
|
225
|
+
| Issues | `list_issues_tool`, `get_issue_tool`, `list_issue_comments_tool` | `create_issue_tool`, `update_issue_tool`, `create_issue_comment_tool` |
|
|
222
226
|
| Pull requests | `merge_pull_request_tool`, `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`, `create_issue_comment_tool` on a PR |
|
|
223
|
-
| 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` |
|
|
227
|
+
| Actions | `list_workflow_runs_tool`, `get_workflow_run_tool`, `get_failed_job_logs_tool`, `get_pull_request_checks_tool`, `merge_pull_request_tool` | `trigger_workflow_tool`, `rerun_workflow_run_tool`, `rerun_failed_jobs_tool` |
|
|
224
228
|
| Commit statuses | `get_pull_request_checks_tool`, `merge_pull_request_tool` | — |
|
|
225
229
|
|
|
226
|
-
`get_pull_request_checks_tool` and `merge_pull_request_tool` read check-runs,
|
|
227
|
-
for fine-grained tokens
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
230
|
+
`get_pull_request_checks_tool` and `merge_pull_request_tool` read check-runs,
|
|
231
|
+
and GitHub's permission table for fine-grained tokens has no permission for
|
|
232
|
+
that endpoint: a fine-grained token gets 403 on a private repository. Both
|
|
233
|
+
tools then read the head commit's GitHub Actions jobs instead (Actions: read).
|
|
234
|
+
Every Actions job is a check run with the same id and name, so the verdict
|
|
235
|
+
and the merge gate are the same for checks GitHub Actions reports. Checks from
|
|
236
|
+
any other app are not visible that way, so the merge gate refuses a required
|
|
237
|
+
check that names another app, or that accepts any app and has no commit
|
|
238
|
+
status, as missing. `checks_source` in each result says which was read, and
|
|
239
|
+
the checks verdict then carries a `note` saying other apps' checks are not
|
|
240
|
+
visible. Only the newest run of each workflow and event is read, and a commit
|
|
241
|
+
with runs of more than 30 workflows is not judged.
|
|
242
|
+
|
|
243
|
+
The search tools have no permission of their own: they return what the token can already read.
|
|
231
244
|
|
|
232
245
|
Three things worth knowing before granting write:
|
|
233
246
|
|
|
@@ -262,7 +275,8 @@ merges it:
|
|
|
262
275
|
4. requires, for every required check on the head commit, a completed check
|
|
263
276
|
run of that name concluding `success`, `skipped` or `neutral` (the most
|
|
264
277
|
recent run of the name decides), or a commit status of `success`. A rule
|
|
265
|
-
that names an app accepts only that app's check runs. A
|
|
278
|
+
that names an app accepts only that app's check runs. A token that cannot
|
|
279
|
+
read check-runs is judged on the Actions jobs (see above). A missing, pending
|
|
266
280
|
or failed check refuses the merge, and the error lists each one and why;
|
|
267
281
|
5. reads the pull request again and refuses if its head or base changed
|
|
268
282
|
meanwhile (GitHub's merge call pins the head but not the base), then
|
|
@@ -34,7 +34,7 @@ The container image is built on the [Hummingbird Python base image](https://quay
|
|
|
34
34
|
|
|
35
35
|
## Features
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
26 tools. Tools marked *write* change something on GitHub; the others only
|
|
38
38
|
read and publish `readOnlyHint`. The last column is the fine-grained token
|
|
39
39
|
permission the tool needs (see [Token permissions](#token-permissions)).
|
|
40
40
|
|
|
@@ -44,6 +44,7 @@ permission the tool needs (see [Token permissions](#token-permissions)).
|
|
|
44
44
|
|------|--------------|------------|
|
|
45
45
|
| `list_issues_tool` | List issues for a repository (pull requests excluded) | Issues: read |
|
|
46
46
|
| `get_issue_tool` | Get a single issue by number | Issues: read |
|
|
47
|
+
| `list_issue_comments_tool` | Comments on an issue or a pull request conversation, oldest first | Issues: read, or Pull requests: read for a PR |
|
|
47
48
|
| `create_issue_tool` | Open an issue (*write*) | Issues: write |
|
|
48
49
|
| `create_issue_comment_tool` | Comment on an issue or pull request (*write*) | Issues: write, or Pull requests: write for a PR |
|
|
49
50
|
| `update_issue_tool` | Edit, close or reopen an issue (*write*) | Issues: write |
|
|
@@ -55,12 +56,12 @@ permission the tool needs (see [Token permissions](#token-permissions)).
|
|
|
55
56
|
| `list_pull_requests_tool` | List pull requests for a repository | Pull requests: read |
|
|
56
57
|
| `get_pull_request_tool` | Get a single pull request by number | Pull requests: read |
|
|
57
58
|
| `get_pull_request_diff_tool` | Get the unified diff for a pull request | Pull requests: read |
|
|
58
|
-
| `get_pull_request_checks_tool` | CI verdict from check-runs and commit statuses; skipped is not failed | Pull requests: read, Commit statuses: read (and see the note under Token permissions) |
|
|
59
|
+
| `get_pull_request_checks_tool` | 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) |
|
|
59
60
|
| `list_pull_request_comments_tool` | Conversation comments, diff comments and reviews in one list, each tagged with its kind | Pull requests: read |
|
|
60
61
|
| `create_pull_request_tool` | Open a pull request, optionally as a draft (*write*) | Pull requests: write |
|
|
61
62
|
| `update_pull_request_tool` | Edit, close or reopen a pull request; does not merge (*write*) | Pull requests: write |
|
|
62
63
|
| `reply_to_review_comment_tool` | Reply in the thread of a diff comment (*write*) | Pull requests: write |
|
|
63
|
-
| `merge_pull_request_tool` | Merge, squash or rebase a pull request, only once the base branch's required checks are green on its head (see [The merge gate](#the-merge-gate)) (*write*) | Contents: write; Pull requests
|
|
64
|
+
| `merge_pull_request_tool` | Merge, squash or rebase a pull request, only once the base branch's required checks are green on its head (see [The merge gate](#the-merge-gate)) (*write*) | Contents: write; Pull requests, Commit statuses and Actions: read |
|
|
64
65
|
|
|
65
66
|
### Actions (6 tools)
|
|
66
67
|
|
|
@@ -97,7 +98,10 @@ permission the tool needs (see [Token permissions](#token-permissions)).
|
|
|
97
98
|
|
|
98
99
|
- `list_pull_request_comments_tool` reads each of its three kinds oldest
|
|
99
100
|
first, up to 300 items per kind. `truncated` and `truncated_kinds` say when
|
|
100
|
-
newer items were left out.
|
|
101
|
+
newer items were left out. Called on a plain issue it says so; use
|
|
102
|
+
`list_issue_comments_tool` there.
|
|
103
|
+
- `list_issue_comments_tool` returns one page (up to 100 comments); follow
|
|
104
|
+
`pagination.next_page` for more.
|
|
101
105
|
- `get_workflow_run_tool` lists up to 300 jobs; `jobs_truncated` says when the
|
|
102
106
|
run has more.
|
|
103
107
|
- `get_failed_job_logs_tool` returns at most 1000 lines per job (200 by
|
|
@@ -192,16 +196,25 @@ For a fine-grained personal access token, the repository permissions are:
|
|
|
192
196
|
|------------|--------------------|---------------------|
|
|
193
197
|
| Metadata | every tool (GitHub grants it with any other permission) | — |
|
|
194
198
|
| Contents | `get_file_content_tool`, `list_repo_tree_tool` | `merge_pull_request_tool`, `create_release_tool` |
|
|
195
|
-
| Issues | `list_issues_tool`, `get_issue_tool` | `create_issue_tool`, `update_issue_tool`, `create_issue_comment_tool` |
|
|
199
|
+
| Issues | `list_issues_tool`, `get_issue_tool`, `list_issue_comments_tool` | `create_issue_tool`, `update_issue_tool`, `create_issue_comment_tool` |
|
|
196
200
|
| Pull requests | `merge_pull_request_tool`, `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`, `create_issue_comment_tool` on a PR |
|
|
197
|
-
| 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` |
|
|
201
|
+
| Actions | `list_workflow_runs_tool`, `get_workflow_run_tool`, `get_failed_job_logs_tool`, `get_pull_request_checks_tool`, `merge_pull_request_tool` | `trigger_workflow_tool`, `rerun_workflow_run_tool`, `rerun_failed_jobs_tool` |
|
|
198
202
|
| Commit statuses | `get_pull_request_checks_tool`, `merge_pull_request_tool` | — |
|
|
199
203
|
|
|
200
|
-
`get_pull_request_checks_tool` and `merge_pull_request_tool` read check-runs,
|
|
201
|
-
for fine-grained tokens
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
204
|
+
`get_pull_request_checks_tool` and `merge_pull_request_tool` read check-runs,
|
|
205
|
+
and GitHub's permission table for fine-grained tokens has no permission for
|
|
206
|
+
that endpoint: a fine-grained token gets 403 on a private repository. Both
|
|
207
|
+
tools then read the head commit's GitHub Actions jobs instead (Actions: read).
|
|
208
|
+
Every Actions job is a check run with the same id and name, so the verdict
|
|
209
|
+
and the merge gate are the same for checks GitHub Actions reports. Checks from
|
|
210
|
+
any other app are not visible that way, so the merge gate refuses a required
|
|
211
|
+
check that names another app, or that accepts any app and has no commit
|
|
212
|
+
status, as missing. `checks_source` in each result says which was read, and
|
|
213
|
+
the checks verdict then carries a `note` saying other apps' checks are not
|
|
214
|
+
visible. Only the newest run of each workflow and event is read, and a commit
|
|
215
|
+
with runs of more than 30 workflows is not judged.
|
|
216
|
+
|
|
217
|
+
The search tools have no permission of their own: they return what the token can already read.
|
|
205
218
|
|
|
206
219
|
Three things worth knowing before granting write:
|
|
207
220
|
|
|
@@ -236,7 +249,8 @@ merges it:
|
|
|
236
249
|
4. requires, for every required check on the head commit, a completed check
|
|
237
250
|
run of that name concluding `success`, `skipped` or `neutral` (the most
|
|
238
251
|
recent run of the name decides), or a commit status of `success`. A rule
|
|
239
|
-
that names an app accepts only that app's check runs. A
|
|
252
|
+
that names an app accepts only that app's check runs. A token that cannot
|
|
253
|
+
read check-runs is judged on the Actions jobs (see above). A missing, pending
|
|
240
254
|
or failed check refuses the merge, and the error lists each one and why;
|
|
241
255
|
5. reads the pull request again and refuses if its head or base changed
|
|
242
256
|
meanwhile (GitHub's merge call pins the head but not the base), then
|
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
"url": "https://github.com/crunchtools/mcp-github",
|
|
8
8
|
"source": "github"
|
|
9
9
|
},
|
|
10
|
-
"version": "1.
|
|
10
|
+
"version": "1.5.0",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "pypi",
|
|
14
14
|
"identifier": "mcp-github-crunchtools",
|
|
15
|
-
"version": "1.
|
|
15
|
+
"version": "1.5.0",
|
|
16
16
|
"transport": {
|
|
17
17
|
"type": "stdio"
|
|
18
18
|
}
|
{mcp_github_crunchtools-1.3.2 → mcp_github_crunchtools-1.5.0}/src/mcp_github_crunchtools/server.py
RENAMED
|
@@ -21,6 +21,7 @@ from .tools import (
|
|
|
21
21
|
get_pull_request_checks,
|
|
22
22
|
get_pull_request_diff,
|
|
23
23
|
get_workflow_run,
|
|
24
|
+
list_issue_comments,
|
|
24
25
|
list_issues,
|
|
25
26
|
list_pull_request_comments,
|
|
26
27
|
list_pull_requests,
|
|
@@ -45,7 +46,7 @@ READ_ONLY = {"readOnlyHint": True}
|
|
|
45
46
|
|
|
46
47
|
mcp = FastMCP(
|
|
47
48
|
name="mcp-github",
|
|
48
|
-
version="1.
|
|
49
|
+
version="1.5.0",
|
|
49
50
|
instructions=(
|
|
50
51
|
"Secure MCP server for GitHub repositories: issues, pull requests "
|
|
51
52
|
"(open, review discussion, diffs, CI checks, merge), Actions runs and "
|
|
@@ -106,6 +107,40 @@ async def get_issue_tool(
|
|
|
106
107
|
return await get_issue(owner=owner, repo=repo, issue_number=issue_number)
|
|
107
108
|
|
|
108
109
|
|
|
110
|
+
@mcp.tool(annotations=READ_ONLY)
|
|
111
|
+
async def list_issue_comments_tool(
|
|
112
|
+
repo: str,
|
|
113
|
+
issue_number: Annotated[int, Field(ge=1)],
|
|
114
|
+
owner: str | None = None,
|
|
115
|
+
per_page: int = 30,
|
|
116
|
+
page: int = 1,
|
|
117
|
+
) -> dict[str, Any]:
|
|
118
|
+
"""List the comments on a GitHub issue or pull request conversation, oldest first.
|
|
119
|
+
|
|
120
|
+
Works for plain issues, which list_pull_request_comments_tool does not.
|
|
121
|
+
On a pull request it returns only the conversation comments; diff
|
|
122
|
+
comments and reviews come from list_pull_request_comments_tool.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
repo: Repository name
|
|
126
|
+
issue_number: Issue or pull request number
|
|
127
|
+
owner: Repository owner (defaults to GITHUB_DEFAULT_ORG if unset)
|
|
128
|
+
per_page: Results per page, max 100 (default: 30)
|
|
129
|
+
page: Page number (default: 1)
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
{"issue_number", "items", "pagination"?}; each item carries id,
|
|
133
|
+
author, author_association, created_at, updated_at, body, html_url
|
|
134
|
+
"""
|
|
135
|
+
return await list_issue_comments(
|
|
136
|
+
owner=owner,
|
|
137
|
+
repo=repo,
|
|
138
|
+
issue_number=issue_number,
|
|
139
|
+
per_page=per_page,
|
|
140
|
+
page=page,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
|
|
109
144
|
@mcp.tool()
|
|
110
145
|
async def create_issue_tool(
|
|
111
146
|
repo: str,
|
|
@@ -270,7 +305,9 @@ async def get_pull_request_checks_tool(
|
|
|
270
305
|
failing, pending, or skipped. SKIPPED checks are NOT failures. Use the
|
|
271
306
|
returned ``ready_to_merge`` boolean as the signal for whether the PR is
|
|
272
307
|
clear to merge (True only when nothing is failing or pending and the PR
|
|
273
|
-
is not known-unmergeable).
|
|
308
|
+
is not known-unmergeable). A token that cannot read check-runs (any
|
|
309
|
+
fine-grained token) gets the Actions jobs instead; ``checks_source`` says
|
|
310
|
+
which, and only Actions-reported checks are then visible.
|
|
274
311
|
|
|
275
312
|
Args:
|
|
276
313
|
repo: Repository name
|
|
@@ -372,7 +409,9 @@ async def merge_pull_request_tool(
|
|
|
372
409
|
head; when branch protection cannot be read; and when the base branch
|
|
373
410
|
requires no checks at all, unless ``allow_no_required_checks`` is True.
|
|
374
411
|
Passing check runs conclude success, skipped or neutral. The merge is
|
|
375
|
-
pinned to the head SHA that was checked.
|
|
412
|
+
pinned to the head SHA that was checked. A token that cannot read
|
|
413
|
+
check-runs (any fine-grained token) is judged on the Actions jobs, and a
|
|
414
|
+
required check not reported by GitHub Actions is refused as missing.
|
|
376
415
|
|
|
377
416
|
Args:
|
|
378
417
|
repo: Repository name
|
|
@@ -16,6 +16,7 @@ from .issues import (
|
|
|
16
16
|
create_issue,
|
|
17
17
|
create_issue_comment,
|
|
18
18
|
get_issue,
|
|
19
|
+
list_issue_comments,
|
|
19
20
|
list_issues,
|
|
20
21
|
update_issue,
|
|
21
22
|
)
|
|
@@ -45,6 +46,7 @@ __all__ = [
|
|
|
45
46
|
"get_pull_request_checks",
|
|
46
47
|
"get_pull_request_diff",
|
|
47
48
|
"get_workflow_run",
|
|
49
|
+
"list_issue_comments",
|
|
48
50
|
"list_issues",
|
|
49
51
|
"list_pull_request_comments",
|
|
50
52
|
"list_pull_requests",
|
|
@@ -88,6 +88,62 @@ async def get_issue(
|
|
|
88
88
|
return await client.get(f"/repos/{owner}/{repo}/issues/{issue_number}")
|
|
89
89
|
|
|
90
90
|
|
|
91
|
+
async def list_issue_comments(
|
|
92
|
+
owner: str | None,
|
|
93
|
+
repo: str,
|
|
94
|
+
issue_number: int,
|
|
95
|
+
per_page: int = 30,
|
|
96
|
+
page: int = 1,
|
|
97
|
+
) -> dict[str, Any]:
|
|
98
|
+
"""List the comments on an issue or a pull request conversation, oldest first.
|
|
99
|
+
|
|
100
|
+
Diff comments and reviews on a pull request are not included; use
|
|
101
|
+
list_pull_request_comments for those.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
owner: Repository owner (defaults to GITHUB_DEFAULT_ORG if unset)
|
|
105
|
+
repo: Repository name
|
|
106
|
+
issue_number: Issue or pull request number
|
|
107
|
+
per_page: Results per page (max 100)
|
|
108
|
+
page: Page number
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
{"issue_number", "items", "pagination"?}; each item carries id,
|
|
112
|
+
author, author_association, created_at, updated_at, body, html_url
|
|
113
|
+
"""
|
|
114
|
+
owner = resolve_owner(owner)
|
|
115
|
+
repo = validate_name(repo, "repo")
|
|
116
|
+
issue_number = validate_positive_int(issue_number, "issue_number")
|
|
117
|
+
|
|
118
|
+
client = get_client()
|
|
119
|
+
result = await client.get(
|
|
120
|
+
f"/repos/{owner}/{repo}/issues/{issue_number}/comments",
|
|
121
|
+
params={
|
|
122
|
+
"per_page": clamp_per_page(per_page),
|
|
123
|
+
"page": validate_positive_int(page, "page"),
|
|
124
|
+
},
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
response: dict[str, Any] = {
|
|
128
|
+
"issue_number": issue_number,
|
|
129
|
+
"items": [
|
|
130
|
+
{
|
|
131
|
+
"id": raw.get("id"),
|
|
132
|
+
"author": (raw.get("user") or {}).get("login"),
|
|
133
|
+
"author_association": raw.get("author_association"),
|
|
134
|
+
"created_at": raw.get("created_at"),
|
|
135
|
+
"updated_at": raw.get("updated_at"),
|
|
136
|
+
"body": raw.get("body") or "",
|
|
137
|
+
"html_url": raw.get("html_url"),
|
|
138
|
+
}
|
|
139
|
+
for raw in result.get("items", [])
|
|
140
|
+
],
|
|
141
|
+
}
|
|
142
|
+
if "pagination" in result:
|
|
143
|
+
response["pagination"] = result["pagination"]
|
|
144
|
+
return response
|
|
145
|
+
|
|
146
|
+
|
|
91
147
|
async def create_issue(
|
|
92
148
|
owner: str | None,
|
|
93
149
|
repo: str,
|
|
@@ -169,9 +225,7 @@ async def update_issue(
|
|
|
169
225
|
json_data["state"] = state
|
|
170
226
|
if state_reason is not None:
|
|
171
227
|
if state_reason not in ("completed", "not_planned", "reopened"):
|
|
172
|
-
raise ValidationError(
|
|
173
|
-
"state_reason must be 'completed', 'not_planned', or 'reopened'"
|
|
174
|
-
)
|
|
228
|
+
raise ValidationError("state_reason must be 'completed', 'not_planned', or 'reopened'")
|
|
175
229
|
json_data["state_reason"] = state_reason
|
|
176
230
|
if title is not None:
|
|
177
231
|
json_data["title"] = title
|