github-mcp-connector 0.7.0__tar.gz → 0.8.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.
- {github_mcp_connector-0.7.0/src/github_mcp_connector.egg-info → github_mcp_connector-0.8.0}/PKG-INFO +3 -2
- {github_mcp_connector-0.7.0 → github_mcp_connector-0.8.0}/README.md +2 -1
- {github_mcp_connector-0.7.0 → github_mcp_connector-0.8.0}/pyproject.toml +1 -1
- {github_mcp_connector-0.7.0 → github_mcp_connector-0.8.0}/src/github_mcp/server.py +93 -0
- {github_mcp_connector-0.7.0 → github_mcp_connector-0.8.0/src/github_mcp_connector.egg-info}/PKG-INFO +3 -2
- {github_mcp_connector-0.7.0 → github_mcp_connector-0.8.0}/tests/test_server.py +54 -0
- {github_mcp_connector-0.7.0 → github_mcp_connector-0.8.0}/LICENSE +0 -0
- {github_mcp_connector-0.7.0 → github_mcp_connector-0.8.0}/setup.cfg +0 -0
- {github_mcp_connector-0.7.0 → github_mcp_connector-0.8.0}/src/github_mcp/__init__.py +0 -0
- {github_mcp_connector-0.7.0 → github_mcp_connector-0.8.0}/src/github_mcp/__main__.py +0 -0
- {github_mcp_connector-0.7.0 → github_mcp_connector-0.8.0}/src/github_mcp/client.py +0 -0
- {github_mcp_connector-0.7.0 → github_mcp_connector-0.8.0}/src/github_mcp/config.py +0 -0
- {github_mcp_connector-0.7.0 → github_mcp_connector-0.8.0}/src/github_mcp_connector.egg-info/SOURCES.txt +0 -0
- {github_mcp_connector-0.7.0 → github_mcp_connector-0.8.0}/src/github_mcp_connector.egg-info/dependency_links.txt +0 -0
- {github_mcp_connector-0.7.0 → github_mcp_connector-0.8.0}/src/github_mcp_connector.egg-info/entry_points.txt +0 -0
- {github_mcp_connector-0.7.0 → github_mcp_connector-0.8.0}/src/github_mcp_connector.egg-info/requires.txt +0 -0
- {github_mcp_connector-0.7.0 → github_mcp_connector-0.8.0}/src/github_mcp_connector.egg-info/top_level.txt +0 -0
- {github_mcp_connector-0.7.0 → github_mcp_connector-0.8.0}/tests/test_client.py +0 -0
- {github_mcp_connector-0.7.0 → github_mcp_connector-0.8.0}/tests/test_config.py +0 -0
{github_mcp_connector-0.7.0/src/github_mcp_connector.egg-info → github_mcp_connector-0.8.0}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: github-mcp-connector
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0
|
|
4
4
|
Summary: A Model Context Protocol (MCP) connector for GitHub, for use with Claude.
|
|
5
5
|
Author: winnerlose2026
|
|
6
6
|
License: MIT
|
|
@@ -51,7 +51,7 @@ Enterprise Server.
|
|
|
51
51
|
- 🧾 **Commits** — history, single-commit detail, and diffs between refs
|
|
52
52
|
- 🐛 **Issues** — list, read, create, comment, read comments, edit/close, label, assign
|
|
53
53
|
- 🔀 **Pull requests** — list, read, diffs/files, comments, open, update, merge, and review
|
|
54
|
-
- ⚙️ **Actions & releases** — list runs/jobs, read failed-job logs, re-run/dispatch workflows, commit statuses & check runs, list/create releases
|
|
54
|
+
- ⚙️ **Actions & releases** — list runs/jobs, read failed-job logs, re-run/dispatch/**schedule** workflows, commit statuses & check runs, list/create releases
|
|
55
55
|
- 🔐 **Security** — secret-scanning, code-scanning, and Dependabot alerts
|
|
56
56
|
- 🔔 **Notifications** — list and mark read across all your repos
|
|
57
57
|
- ✍️ **Repo writes** — create branches, commit files, manage labels/assignees, create gists (gated by read-only mode)
|
|
@@ -101,6 +101,7 @@ Enterprise Server.
|
|
|
101
101
|
| `add_pull_request_review_comment` | Inline comment on a PR diff line | ✅ |
|
|
102
102
|
| `rerun_workflow_run` | Re-run a workflow run (or just failed jobs) | ✅ |
|
|
103
103
|
| `trigger_workflow` | Dispatch a workflow_dispatch run | ✅ |
|
|
104
|
+
| `create_scheduled_workflow` | Commit a cron-scheduled workflow ("do X later") | ✅ |
|
|
104
105
|
| `create_release` | Create a release (and its tag) | ✅ |
|
|
105
106
|
| `create_issue` | Open a new issue | ✅ |
|
|
106
107
|
| `update_issue` | Edit/close/reopen an issue | ✅ |
|
|
@@ -21,7 +21,7 @@ Enterprise Server.
|
|
|
21
21
|
- 🧾 **Commits** — history, single-commit detail, and diffs between refs
|
|
22
22
|
- 🐛 **Issues** — list, read, create, comment, read comments, edit/close, label, assign
|
|
23
23
|
- 🔀 **Pull requests** — list, read, diffs/files, comments, open, update, merge, and review
|
|
24
|
-
- ⚙️ **Actions & releases** — list runs/jobs, read failed-job logs, re-run/dispatch workflows, commit statuses & check runs, list/create releases
|
|
24
|
+
- ⚙️ **Actions & releases** — list runs/jobs, read failed-job logs, re-run/dispatch/**schedule** workflows, commit statuses & check runs, list/create releases
|
|
25
25
|
- 🔐 **Security** — secret-scanning, code-scanning, and Dependabot alerts
|
|
26
26
|
- 🔔 **Notifications** — list and mark read across all your repos
|
|
27
27
|
- ✍️ **Repo writes** — create branches, commit files, manage labels/assignees, create gists (gated by read-only mode)
|
|
@@ -71,6 +71,7 @@ Enterprise Server.
|
|
|
71
71
|
| `add_pull_request_review_comment` | Inline comment on a PR diff line | ✅ |
|
|
72
72
|
| `rerun_workflow_run` | Re-run a workflow run (or just failed jobs) | ✅ |
|
|
73
73
|
| `trigger_workflow` | Dispatch a workflow_dispatch run | ✅ |
|
|
74
|
+
| `create_scheduled_workflow` | Commit a cron-scheduled workflow ("do X later") | ✅ |
|
|
74
75
|
| `create_release` | Create a release (and its tag) | ✅ |
|
|
75
76
|
| `create_issue` | Open a new issue | ✅ |
|
|
76
77
|
| `update_issue` | Edit/close/reopen an issue | ✅ |
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "github-mcp-connector"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.8.0"
|
|
8
8
|
description = "A Model Context Protocol (MCP) connector for GitHub, for use with Claude."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -12,6 +12,7 @@ variable ``GITHUB_MCP_READ_ONLY`` is truthy.
|
|
|
12
12
|
from __future__ import annotations
|
|
13
13
|
|
|
14
14
|
import base64
|
|
15
|
+
import re
|
|
15
16
|
from typing import Any
|
|
16
17
|
from urllib.parse import quote
|
|
17
18
|
|
|
@@ -1628,6 +1629,98 @@ async def create_gist(
|
|
|
1628
1629
|
return _summarize_gist(gist)
|
|
1629
1630
|
|
|
1630
1631
|
|
|
1632
|
+
@mcp.tool()
|
|
1633
|
+
async def create_scheduled_workflow(
|
|
1634
|
+
owner: str,
|
|
1635
|
+
repo: str,
|
|
1636
|
+
name: str,
|
|
1637
|
+
cron: str,
|
|
1638
|
+
run: str,
|
|
1639
|
+
runs_on: str = "ubuntu-latest",
|
|
1640
|
+
branch: str | None = None,
|
|
1641
|
+
filename: str | None = None,
|
|
1642
|
+
message: str | None = None,
|
|
1643
|
+
) -> dict[str, Any]:
|
|
1644
|
+
"""Schedule recurring work by committing a cron GitHub Actions workflow.
|
|
1645
|
+
|
|
1646
|
+
This is GitHub's native "do X later / on a schedule": it creates (or updates)
|
|
1647
|
+
a workflow file at `.github/workflows/<filename>` that runs `run` (a shell
|
|
1648
|
+
script) on the `cron` schedule, and also adds a `workflow_dispatch` trigger
|
|
1649
|
+
so you can run it on demand. `cron` is standard 5-field UTC cron
|
|
1650
|
+
(e.g. "0 9 * * 1" = 09:00 UTC every Monday).
|
|
1651
|
+
|
|
1652
|
+
IMPORTANT: GitHub only fires `schedule` triggers from a repository's DEFAULT
|
|
1653
|
+
branch, so `branch` defaults to it — committing to another branch will run on
|
|
1654
|
+
demand but never on the schedule. `filename` defaults to a slug of `name`.
|
|
1655
|
+
Disabled in read-only mode; requires write access (and the token needs the
|
|
1656
|
+
`workflow` scope to add workflow files).
|
|
1657
|
+
"""
|
|
1658
|
+
_require_token()
|
|
1659
|
+
_require_write()
|
|
1660
|
+
if len(cron.split()) != 5:
|
|
1661
|
+
raise GitHubError(
|
|
1662
|
+
400,
|
|
1663
|
+
"CRON",
|
|
1664
|
+
f"/repos/{owner}/{repo}",
|
|
1665
|
+
f"`cron` must be a 5-field cron expression (got: {cron!r}).",
|
|
1666
|
+
)
|
|
1667
|
+
slug = re.sub(r"[^a-z0-9]+", "-", name.lower()).strip("-") or "scheduled"
|
|
1668
|
+
fname = filename or f"{slug}.yml"
|
|
1669
|
+
path = f".github/workflows/{fname}"
|
|
1670
|
+
indented_run = "\n".join(" " + line for line in run.splitlines())
|
|
1671
|
+
content = (
|
|
1672
|
+
f"name: {name}\n"
|
|
1673
|
+
"on:\n"
|
|
1674
|
+
" schedule:\n"
|
|
1675
|
+
f' - cron: "{cron}"\n'
|
|
1676
|
+
" workflow_dispatch: {}\n"
|
|
1677
|
+
"permissions:\n"
|
|
1678
|
+
" contents: read\n"
|
|
1679
|
+
"jobs:\n"
|
|
1680
|
+
" scheduled:\n"
|
|
1681
|
+
f" runs-on: {runs_on}\n"
|
|
1682
|
+
" steps:\n"
|
|
1683
|
+
" - uses: actions/checkout@v4\n"
|
|
1684
|
+
" - name: Run scheduled command\n"
|
|
1685
|
+
" run: |\n"
|
|
1686
|
+
f"{indented_run}\n"
|
|
1687
|
+
)
|
|
1688
|
+
encoded = base64.b64encode(content.encode("utf-8")).decode("ascii")
|
|
1689
|
+
commit_message = message or f"Add scheduled workflow: {name}"
|
|
1690
|
+
async with GitHubClient(config) as gh:
|
|
1691
|
+
if branch is None:
|
|
1692
|
+
repo_data = await gh.get(f"/repos/{owner}/{repo}")
|
|
1693
|
+
branch = repo_data.get("default_branch")
|
|
1694
|
+
payload: dict[str, Any] = {
|
|
1695
|
+
"message": commit_message,
|
|
1696
|
+
"content": encoded,
|
|
1697
|
+
"branch": branch,
|
|
1698
|
+
}
|
|
1699
|
+
# If the workflow file already exists on this branch, update it.
|
|
1700
|
+
try:
|
|
1701
|
+
existing = await gh.get(
|
|
1702
|
+
f"/repos/{owner}/{repo}/contents/{path}", params={"ref": branch}
|
|
1703
|
+
)
|
|
1704
|
+
if isinstance(existing, dict) and existing.get("sha"):
|
|
1705
|
+
payload["sha"] = existing.get("sha")
|
|
1706
|
+
except GitHubError as exc:
|
|
1707
|
+
if exc.status_code != 404:
|
|
1708
|
+
raise
|
|
1709
|
+
result = await gh.put(f"/repos/{owner}/{repo}/contents/{path}", json=payload)
|
|
1710
|
+
commit = result.get("commit", {})
|
|
1711
|
+
content_info = result.get("content", {})
|
|
1712
|
+
return {
|
|
1713
|
+
"path": content_info.get("path", path),
|
|
1714
|
+
"branch": branch,
|
|
1715
|
+
"cron": cron,
|
|
1716
|
+
"runs_on": runs_on,
|
|
1717
|
+
"commit_sha": commit.get("sha"),
|
|
1718
|
+
"html_url": content_info.get("html_url"),
|
|
1719
|
+
"note": "Scheduled runs fire only from the default branch and start "
|
|
1720
|
+
"from the next cron tick; use trigger_workflow to run it now.",
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1723
|
+
|
|
1631
1724
|
def main(argv: list[str] | None = None) -> None:
|
|
1632
1725
|
"""Console entry point. Selects the transport from CLI args/env."""
|
|
1633
1726
|
import argparse
|
{github_mcp_connector-0.7.0 → github_mcp_connector-0.8.0/src/github_mcp_connector.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: github-mcp-connector
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0
|
|
4
4
|
Summary: A Model Context Protocol (MCP) connector for GitHub, for use with Claude.
|
|
5
5
|
Author: winnerlose2026
|
|
6
6
|
License: MIT
|
|
@@ -51,7 +51,7 @@ Enterprise Server.
|
|
|
51
51
|
- 🧾 **Commits** — history, single-commit detail, and diffs between refs
|
|
52
52
|
- 🐛 **Issues** — list, read, create, comment, read comments, edit/close, label, assign
|
|
53
53
|
- 🔀 **Pull requests** — list, read, diffs/files, comments, open, update, merge, and review
|
|
54
|
-
- ⚙️ **Actions & releases** — list runs/jobs, read failed-job logs, re-run/dispatch workflows, commit statuses & check runs, list/create releases
|
|
54
|
+
- ⚙️ **Actions & releases** — list runs/jobs, read failed-job logs, re-run/dispatch/**schedule** workflows, commit statuses & check runs, list/create releases
|
|
55
55
|
- 🔐 **Security** — secret-scanning, code-scanning, and Dependabot alerts
|
|
56
56
|
- 🔔 **Notifications** — list and mark read across all your repos
|
|
57
57
|
- ✍️ **Repo writes** — create branches, commit files, manage labels/assignees, create gists (gated by read-only mode)
|
|
@@ -101,6 +101,7 @@ Enterprise Server.
|
|
|
101
101
|
| `add_pull_request_review_comment` | Inline comment on a PR diff line | ✅ |
|
|
102
102
|
| `rerun_workflow_run` | Re-run a workflow run (or just failed jobs) | ✅ |
|
|
103
103
|
| `trigger_workflow` | Dispatch a workflow_dispatch run | ✅ |
|
|
104
|
+
| `create_scheduled_workflow` | Commit a cron-scheduled workflow ("do X later") | ✅ |
|
|
104
105
|
| `create_release` | Create a release (and its tag) | ✅ |
|
|
105
106
|
| `create_issue` | Open a new issue | ✅ |
|
|
106
107
|
| `update_issue` | Edit/close/reopen an issue | ✅ |
|
|
@@ -1056,3 +1056,57 @@ async def test_get_repository_tree_errors_without_default_branch(monkeypatch):
|
|
|
1056
1056
|
with pytest.raises(GitHubError) as exc:
|
|
1057
1057
|
await server.get_repository_tree("o", "r")
|
|
1058
1058
|
assert exc.value.status_code == 404
|
|
1059
|
+
|
|
1060
|
+
|
|
1061
|
+
async def test_create_scheduled_workflow_read_only(monkeypatch):
|
|
1062
|
+
install_mock(monkeypatch, lambda r: httpx.Response(200, json={}),
|
|
1063
|
+
read_only=True)
|
|
1064
|
+
with pytest.raises(GitHubError) as exc:
|
|
1065
|
+
await server.create_scheduled_workflow("o", "r", "Nightly", "0 9 * * *",
|
|
1066
|
+
"echo hi")
|
|
1067
|
+
assert exc.value.status_code == 403
|
|
1068
|
+
|
|
1069
|
+
|
|
1070
|
+
async def test_create_scheduled_workflow_bad_cron(monkeypatch):
|
|
1071
|
+
install_mock(monkeypatch, lambda r: httpx.Response(200, json={}))
|
|
1072
|
+
with pytest.raises(GitHubError) as exc:
|
|
1073
|
+
await server.create_scheduled_workflow("o", "r", "x", "not cron", "echo")
|
|
1074
|
+
assert exc.value.status_code == 400
|
|
1075
|
+
|
|
1076
|
+
|
|
1077
|
+
async def test_create_scheduled_workflow_commits_yaml(monkeypatch):
|
|
1078
|
+
import base64
|
|
1079
|
+
captured = {}
|
|
1080
|
+
|
|
1081
|
+
def handler(request):
|
|
1082
|
+
import json
|
|
1083
|
+
p = request.url.path
|
|
1084
|
+
if request.method == "GET" and p == "/repos/o/r":
|
|
1085
|
+
return httpx.Response(200, json={"default_branch": "main"})
|
|
1086
|
+
if request.method == "GET" and p.startswith("/repos/o/r/contents/"):
|
|
1087
|
+
return httpx.Response(404, json={"message": "Not Found"})
|
|
1088
|
+
if request.method == "PUT" and p == "/repos/o/r/contents/.github/workflows/nightly-backup.yml":
|
|
1089
|
+
captured["body"] = json.loads(request.content)
|
|
1090
|
+
captured["path"] = p
|
|
1091
|
+
return httpx.Response(
|
|
1092
|
+
201,
|
|
1093
|
+
json={"content": {"path": ".github/workflows/nightly-backup.yml",
|
|
1094
|
+
"sha": "s", "html_url": "u"},
|
|
1095
|
+
"commit": {"sha": "c"}},
|
|
1096
|
+
)
|
|
1097
|
+
raise AssertionError(f"unexpected {request.method} {p}")
|
|
1098
|
+
|
|
1099
|
+
install_mock(monkeypatch, handler)
|
|
1100
|
+
result = await server.create_scheduled_workflow(
|
|
1101
|
+
"o", "r", "Nightly Backup", "0 9 * * 1", "echo hi\nrun-backup.sh"
|
|
1102
|
+
)
|
|
1103
|
+
body = captured["body"]
|
|
1104
|
+
assert body["branch"] == "main" # defaulted to default branch
|
|
1105
|
+
assert "sha" not in body # new file
|
|
1106
|
+
yaml = base64.b64decode(body["content"]).decode()
|
|
1107
|
+
assert "schedule:" in yaml
|
|
1108
|
+
assert 'cron: "0 9 * * 1"' in yaml
|
|
1109
|
+
assert "workflow_dispatch: {}" in yaml
|
|
1110
|
+
assert "run-backup.sh" in yaml
|
|
1111
|
+
assert result["cron"] == "0 9 * * 1"
|
|
1112
|
+
assert result["branch"] == "main"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|