mcp-github-crunchtools 1.3.0__tar.gz → 1.3.1__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.0 → mcp_github_crunchtools-1.3.1}/CHANGELOG.md +8 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/PKG-INFO +1 -1
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/pyproject.toml +1 -1
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/src/mcp_github_crunchtools/__init__.py +1 -1
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/src/mcp_github_crunchtools/client.py +19 -4
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/src/mcp_github_crunchtools/server.py +1 -1
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/tests/test_tools.py +23 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/uv.lock +1 -1
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/.gatehouse-ignore +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/.github/dependabot.yml +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/.github/workflows/ashigaru.yml +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/.github/workflows/ci.yml +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/.github/workflows/constitution.yml +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/.github/workflows/container.yml +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/.github/workflows/dependabot-automerge.yml +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/.github/workflows/gatehouse-retriage.yml +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/.github/workflows/gatehouse.yml +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/.github/workflows/publish.yml +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/.github/workflows/security.yml +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/.gitignore +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/.pre-commit-config.yaml +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/.specify/memory/constitution.md +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/.specify/specs/000-baseline/spec.md +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/.specify/specs/001-pr-merge-logs-release/spec.md +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/.specify/specs/002-request-token-merge-gate/spec.md +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/.specify/templates/plan-template.md +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/.specify/templates/spec-template.md +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/CLAUDE.md +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/Containerfile +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/LICENSE +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/README.md +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/SECURITY.md +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/gourmand-exceptions.toml +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/gourmand.toml +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/server.json +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/src/mcp_github_crunchtools/__main__.py +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/src/mcp_github_crunchtools/config.py +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/src/mcp_github_crunchtools/errors.py +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/src/mcp_github_crunchtools/models.py +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/src/mcp_github_crunchtools/tools/__init__.py +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/src/mcp_github_crunchtools/tools/actions.py +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/src/mcp_github_crunchtools/tools/files.py +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/src/mcp_github_crunchtools/tools/issues.py +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/src/mcp_github_crunchtools/tools/pull_requests.py +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/src/mcp_github_crunchtools/tools/releases.py +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/src/mcp_github_crunchtools/tools/search.py +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/tests/__init__.py +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/tests/conftest.py +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/tests/test_merge_gate.py +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/tests/test_request_token.py +0 -0
- {mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/tests/test_validation.py +0 -0
|
@@ -6,6 +6,14 @@ All notable changes to this project are documented here. The format follows
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [1.3.1] - 2026-10-16
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- A primary rate limit reported GitHub's `x-ratelimit-reset` epoch as the
|
|
14
|
+
wait ("Retry after 1792152901 seconds"). It now reports the seconds left
|
|
15
|
+
until the reset.
|
|
16
|
+
|
|
9
17
|
## [1.3.0] - 2026-10-14
|
|
10
18
|
|
|
11
19
|
### Added
|
{mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/src/mcp_github_crunchtools/client.py
RENAMED
|
@@ -7,6 +7,7 @@ All requests go through this client to ensure consistent security practices.
|
|
|
7
7
|
import ipaddress
|
|
8
8
|
import logging
|
|
9
9
|
import re
|
|
10
|
+
import time
|
|
10
11
|
from typing import Any
|
|
11
12
|
from urllib.parse import urlparse
|
|
12
13
|
|
|
@@ -25,6 +26,23 @@ from .errors import (
|
|
|
25
26
|
|
|
26
27
|
logger = logging.getLogger(__name__)
|
|
27
28
|
|
|
29
|
+
|
|
30
|
+
def _retry_after_seconds(headers: httpx.Headers) -> int | None:
|
|
31
|
+
"""Seconds until a rate-limited call may be retried.
|
|
32
|
+
|
|
33
|
+
``retry-after`` is already a duration; ``x-ratelimit-reset`` is the UTC
|
|
34
|
+
epoch second the window resets, so it is converted to a duration.
|
|
35
|
+
"""
|
|
36
|
+
try:
|
|
37
|
+
if (retry_after := headers.get("retry-after")) is not None:
|
|
38
|
+
return max(0, int(retry_after))
|
|
39
|
+
if (reset := headers.get("x-ratelimit-reset")) is not None:
|
|
40
|
+
return max(0, int(reset) - int(time.time()))
|
|
41
|
+
except ValueError:
|
|
42
|
+
return None
|
|
43
|
+
return None
|
|
44
|
+
|
|
45
|
+
|
|
28
46
|
MAX_RESPONSE_SIZE = 10 * 1024 * 1024
|
|
29
47
|
# A job log is streamed and only its tail is kept, so it may be larger than a
|
|
30
48
|
# buffered API response; past this the download is abandoned.
|
|
@@ -302,10 +320,7 @@ class GitHubClient:
|
|
|
302
320
|
rate_remaining = response.headers.get("x-ratelimit-remaining")
|
|
303
321
|
|
|
304
322
|
if status_code == 429 or (status_code == 403 and rate_remaining == "0"):
|
|
305
|
-
|
|
306
|
-
if retry_after is None:
|
|
307
|
-
retry_after = response.headers.get("x-ratelimit-reset")
|
|
308
|
-
raise RateLimitError(int(retry_after) if retry_after else None)
|
|
323
|
+
raise RateLimitError(_retry_after_seconds(response.headers))
|
|
309
324
|
|
|
310
325
|
if status_code == 401:
|
|
311
326
|
raise PermissionDeniedError("Valid token with required scopes")
|
{mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/src/mcp_github_crunchtools/server.py
RENAMED
|
@@ -44,7 +44,7 @@ READ_ONLY = {"readOnlyHint": True}
|
|
|
44
44
|
|
|
45
45
|
mcp = FastMCP(
|
|
46
46
|
name="mcp-github",
|
|
47
|
-
version="1.3.
|
|
47
|
+
version="1.3.1",
|
|
48
48
|
instructions=(
|
|
49
49
|
"Secure MCP server for GitHub repositories: issues, pull requests "
|
|
50
50
|
"(open, review discussion, diffs, CI checks, merge), Actions runs and "
|
|
@@ -6,6 +6,7 @@ Integration tests with a real GitHub account should be run separately.
|
|
|
6
6
|
|
|
7
7
|
import base64
|
|
8
8
|
import os
|
|
9
|
+
import time
|
|
9
10
|
|
|
10
11
|
import httpx
|
|
11
12
|
import pytest
|
|
@@ -1909,6 +1910,28 @@ class TestClientErrorHandling:
|
|
|
1909
1910
|
with _patch_client(resp), pytest.raises(RateLimitError):
|
|
1910
1911
|
await get_issue(owner="o", repo="r", issue_number=1)
|
|
1911
1912
|
|
|
1913
|
+
@pytest.mark.asyncio
|
|
1914
|
+
async def test_403_rate_limit_reset_is_a_duration(self) -> None:
|
|
1915
|
+
"""x-ratelimit-reset is an epoch second; the error reports the wait,
|
|
1916
|
+
not the timestamp."""
|
|
1917
|
+
from mcp_github_crunchtools.errors import RateLimitError
|
|
1918
|
+
from mcp_github_crunchtools.tools import get_issue
|
|
1919
|
+
|
|
1920
|
+
resp = _mock_response(
|
|
1921
|
+
status_code=403,
|
|
1922
|
+
json_data={"message": "API rate limit exceeded"},
|
|
1923
|
+
headers={
|
|
1924
|
+
"x-ratelimit-remaining": "0",
|
|
1925
|
+
"x-ratelimit-reset": str(int(time.time()) + 600),
|
|
1926
|
+
},
|
|
1927
|
+
)
|
|
1928
|
+
|
|
1929
|
+
with _patch_client(resp), pytest.raises(RateLimitError) as exc_info:
|
|
1930
|
+
await get_issue(owner="o", repo="r", issue_number=1)
|
|
1931
|
+
|
|
1932
|
+
seconds = int(str(exc_info.value).split("Retry after ")[1].split()[0])
|
|
1933
|
+
assert 590 <= seconds <= 600
|
|
1934
|
+
|
|
1912
1935
|
@pytest.mark.asyncio
|
|
1913
1936
|
async def test_403_preserves_github_message_when_not_rate_limited(
|
|
1914
1937
|
self,
|
|
File without changes
|
{mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/.github/ISSUE_TEMPLATE/bug_report.yml
RENAMED
|
File without changes
|
{mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/.github/ISSUE_TEMPLATE/config.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/.github/workflows/ashigaru.yml
RENAMED
|
File without changes
|
|
File without changes
|
{mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/.github/workflows/constitution.yml
RENAMED
|
File without changes
|
{mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/.github/workflows/container.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/.github/workflows/gatehouse.yml
RENAMED
|
File without changes
|
|
File without changes
|
{mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/.github/workflows/security.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/.specify/memory/constitution.md
RENAMED
|
File without changes
|
{mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/.specify/specs/000-baseline/spec.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/.specify/templates/plan-template.md
RENAMED
|
File without changes
|
{mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/.specify/templates/spec-template.md
RENAMED
|
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
|
{mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/src/mcp_github_crunchtools/__main__.py
RENAMED
|
File without changes
|
{mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/src/mcp_github_crunchtools/config.py
RENAMED
|
File without changes
|
{mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/src/mcp_github_crunchtools/errors.py
RENAMED
|
File without changes
|
{mcp_github_crunchtools-1.3.0 → mcp_github_crunchtools-1.3.1}/src/mcp_github_crunchtools/models.py
RENAMED
|
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
|