Skip to content

Commit 1f2efec

Browse files
jgravelleclaude
andcommitted
release: v1.108.77 — raise http/all extras' starlette floor to >=1.3.1
starlette is a direct dependency of the `http` and `all` extras, previously floored at >=0.27.0, so `jcodemunch-mcp[http]` installers could resolve a starlette older than 1.3.1 — vulnerable to the request-form size-limit bypass and the StaticFiles UNC-path SSRF/NTLM-credential advisories. Raise the published floor to >=1.3.1 to close that resolution window downstream. Compatibility verified: mcp 1.26.0 declares no starlette upper bound and `uv lock` re-resolves cleanly at the new floor (148 packages). Base install and all other extras unaffected. Full suite 4820 passed / 8 skipped. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 0ee85fd commit 1f2efec

4 files changed

Lines changed: 1991 additions & 1990 deletions

File tree

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,22 @@
22

33
All notable changes to jcodemunch-mcp are documented here.
44

5+
## [1.108.77] - 2026-06-23 - Raise the `http` extra's starlette floor to a patched release
6+
7+
### Security / Packaging
8+
9+
- **`http` and `all` extras now require `starlette>=1.3.1`** (was `>=0.27.0`).
10+
A user installing `jcodemunch-mcp[http]` could previously resolve a starlette
11+
older than 1.3.1, which carries two advisories: request-form size limits
12+
silently ignored, and an SSRF / NTLM-credential-theft path via UNC paths in
13+
`StaticFiles`. Raising the published floor closes that resolution window for
14+
downstream installers of the HTTP transport. The lockfile already resolved
15+
1.3.1; this propagates the same floor to the wheel metadata.
16+
- Compatibility verified: `mcp` (1.26.0) declares no starlette upper bound and
17+
the full lock re-resolves cleanly at the new floor, so no install conflict.
18+
- Note: this raises the minimum starlette to 1.x for the `http`/`all` extras
19+
only. The base install and all other extras are unaffected.
20+
521
## [1.108.76] - 2026-06-22 - Quality gates: CI lint (ruff) + coverage floor
622

723
### CI / Tooling (WI-3.1 / F-Q01)

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# jcodemunch-mcp — Project Brief
22

33
## Current State
4+
- **Version:** 1.108.77 — Raise the `http`/`all` extras' starlette floor `>=0.27.0` → **`>=1.3.1`**. Packaging/security, no source change. Origin: enabling Dependabot alerts (2026-06-22) surfaced 29 vulns; the four lock-only security PRs (#342 cryptography 48.0.1, #343 pydantic-settings, #344 starlette 1.3.1, #345 python-multipart 0.0.31) were merged but those are TRANSITIVE (not in `pyproject` `dependencies`), so the wheel was unchanged → held the no-op release. starlette is the exception: it's a **direct `http`-extra dep** with a loose floor, so a `jcodemunch-mcp[http]` installer could still resolve a vulnerable `<1.3.1` (form-limit-ignored + StaticFiles UNC SSRF/NTLM advisories). Bumping the published floor is the only one of the 10 highs that actually protects downstream users → a real release. Compat verified: `mcp` 1.26.0 declares no starlette upper bound, `uv lock` re-resolves cleanly at the new floor (148 pkgs, exit 0), starlette stays 1.3.1. The other still-open highs (`pyjwt`/`urllib3`/`pyasn1`) are deep-transitive under opt-in `[semantic]`/`[gemini]` extras only — near-zero downstream exposure (loose floors → installers resolve their own), left to self-resolve via parent-lib updates. Future cascades to be tamed via the repo-level "Grouped security updates" toggle (no version-updates `dependabot.yml` — that would add recurring noise). Two-line `pyproject.toml` edit + `uv.lock` specifier refresh.
45
- **Version:** 1.108.76 — Phase 3 of the maintenance PRD (`jcodemunch-mcp-prd.md`), WI-3.1 (CI quality gates: ruff lint + coverage floor; folds in WI-2.4's intent). **Ruff gate:** new `lint` CI job in `.github/workflows/test.yml` runs `ruff check src/` once (platform-independent, dedicated job so it gates without 8x matrix redundancy). `[tool.ruff.lint]` in pyproject KEEPS high-signal pyflakes(F)+pycodestyle-error(E9) rules and `ignore`s this codebase's deliberate patterns: **E402** (lazy/local imports for startup speed + pytest.importorskip), E701/E721/E731/E741 (minor style); per-file-ignore `"__init__.py"=["F401"]` for re-export surfaces (though ruff already respected `__all__`, so no __init__ was touched). **F841 (~31 pre-existing dead locals) is ignored as a tracked ratchet** — safe removal needs per-case RHS side-effect review. ruff>=0.6 added to dev group. **Coverage floor:** test job now `--cov-fail-under=74` (current ~75.6%/"76%"; 74 gives headroom vs per-job variance to avoid flaky false-fails). **Lint cleanup:** `ruff check src/ --fix` applied 83 safe autofixes across 49 modules (unused imports/F401×65, dead vars, empty f-strings, redefinitions) — full suite still 4717 passed, no behavior change. **Two real findings fixed:** server.py `Optional[IO]` used undefined `IO` (added to `from typing import IO,...`; harmless at runtime — local annotations aren't evaluated — but a genuine undefined name), and `storage/sqlite_store.py` had a duplicate `".sh"` dict key (both → "bash"; redundant one removed). **WI-2.4 (ruff BLE/TRY) deliberately NOT enabled** — the codebase's intentional pattern is broad-except-WITH-logging, which BLE001 flags wholesale; enforcing it would fight the "log every silent except" convention rather than the silent-except it targets. **Phase 3 substantive work (WI-3.1/3.2/3.3) DONE; remaining ratchets = F841 cleanup + mypy/pyright report-mode step (both noted, not yet authorized as their own work).**
56
- **Version:** 1.108.75 — Phase 3 of the maintenance PRD (`jcodemunch-mcp-prd.md`, "Strengthen quality gates"), WI-3.2 + WI-3.3 (test-only, no production change). **WI-3.3 (F-Q03):** new `test_live_core_compact_under_4000_hard_ceiling` in test_schema_budget.py recomputes core+compact from `_build_tools_list()` and hard-asserts ≤4000 on the LIVE build — the existing test only read the frozen `schema_baseline.json`, so a breach (e.g. v1.108.70's transient 3956→4011) shipped until the baseline was regenerated. **WI-3.2 (F-P02/F-C02):** new `tests/test_dispatch_schema_parity.py` — the existing registration guard (test_config.py:1740) only checks tool-NAME presence in `_CANONICAL_TOOL_NAMES`, not params; this parses `call_tool`'s source (AST), collects every `arguments["k"]`/`.get("k")` each `if/elif name=="<tool>"` branch reads, and asserts each is a declared inputSchema property (so a caller can pass it). It FOUND two intentional patterns, now handled: config-conditional props (`render_diagram.open_in_viewer`, gated on `render_diagram_viewer_enabled` — test builds the schema with the flag ON) + a forgiving alias (`get_file_outline` accepts `file` for `file_path` — `_KNOWN_FORGIVING_ALIASES`). Guard-the-guard test (`>50 branches found`) prevents the AST walk going vacuous. Full suite 4717 passed. **Remaining Phase 3: WI-3.1 (CI gates: ruff lint+format, mypy/pyright report-mode, `--cov-fail-under`, folds in WI-2.4 ruff BLE/TRY) = next, the bigger one (lint cleanup across 469 modules).** Note: jcm git-HEAD `7762b73` was the v1.108.74 base; server.py line numbers in the stale jcm index drift after every server.py edit — reindex or use inspect/Read offset for precise positions.
67
- **Version:** 1.108.74 — Phase 2 of the maintenance PRD (`jcodemunch-mcp-prd.md`, "Harden"), WI-2.2 (F-P01). **Tool errors now carry `isError`.** Errors were returned in-band only (`{"error": ...}` JSON body in a plain content list → SDK wraps `CallToolResult(isError=False)`), so a non-Claude MCP client that branches on `isError` saw a failure as success — the exact risk for a custom HTTP harness (the Darius case). `call_tool` (server.py:4129) now returns `CallToolResult(isError=True)` for every failure path via new `_error_call_result(text)` helper: input-validation (jsonschema), search_text arg-validation, project-disabled-tool, the common in-band error branch at the JSON return (`isinstance(result,dict) and "error" in result` — covers in-band tool errors + "Unknown tool"), internal-KeyError, missing-argument, and the generic exception handler. The SAME JSON body rides in `content` (v1.108.30 in-band contract preserved); SUCCESS stays a plain `list[TextContent]` (SDK wraps isError=False) → additive on the wire, only failures gain the signal. The SDK supports this directly (`elif isinstance(results, CallToolResult): return ServerResult(results)`), so no raise (raising would mangle the body). The `route` front door (`_handle_route`) updated to surface a routed action's error under its envelope instead of `list()`-ing a non-iterable CallToolResult; `order` passes through naturally. **In-process blast radius (the real cost):** every test that subscripted an error result as a list (`result[0].text`) broke — fixed across test_server.py, test_tier_runtime.py (added `_rtext()` shape-robust reader), test_counter.py (`_call` helper), test_v1_108_55/56.py. New `tests/test_v1_108_74.py` (4: unknown-tool isError + body preserved, input-validation isError, disabled-tool isError, success-stays-plain-list). Full suite 4714 passed/10 skipped. **WI-2.4 (ruff BLE/TRY) remains, deferred to pair with Phase 3 WI-3.1 (CI lint job) — so Phase 2's substantive work is DONE; only the CI-coupled lint rule is left for Phase 3.**

pyproject.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "jcodemunch-mcp"
3-
version = "1.108.76"
3+
version = "1.108.77"
44
description = "Token-efficient MCP server for source code exploration via tree-sitter AST parsing"
55
readme = "README.md"
66
requires-python = ">=3.10"
@@ -54,7 +54,7 @@ openai = ["openai>=1.0.0"]
5454
minimax = ["openai>=1.0.0"]
5555
zhipu = ["openai>=1.0.0"]
5656
dbt = ["pyyaml>=6.0"]
57-
http = ["uvicorn>=0.20.0", "starlette>=0.27.0", "anyio>=4.0.0"]
57+
http = ["uvicorn>=0.20.0", "starlette>=1.3.1", "anyio>=4.0.0"]
5858
watch = ["watchfiles>=1.0.0"]
5959
semantic = ["sentence-transformers>=2.2.0"]
6060
local-embed = ["onnxruntime>=1.16.0"]
@@ -65,7 +65,7 @@ keyring = ["keyring>=24.0"]
6565
bench = ["openai>=1.0.0", "anthropic>=0.40.0", "pyyaml>=6.0", "rich>=13.0", "jinja2>=3.1"]
6666
# `all` is a hand-maintained union of every extra above. When you add a new
6767
# extra or a new dependency to one, mirror it here (there is no auto-union).
68-
all = ["anthropic>=0.40.0", "google-generativeai>=0.8.0", "openai>=1.0.0", "pyyaml>=6.0", "uvicorn>=0.20.0", "starlette>=0.27.0", "anyio>=4.0.0", "watchfiles>=1.0.0", "sentence-transformers>=2.2.0", "onnxruntime>=1.16.0", "sounddevice>=0.4.6", "numpy>=1.24.0", "Pillow>=10.0.0", "rich>=13.0", "jinja2>=3.1", "keyring>=24.0"]
68+
all = ["anthropic>=0.40.0", "google-generativeai>=0.8.0", "openai>=1.0.0", "pyyaml>=6.0", "uvicorn>=0.20.0", "starlette>=1.3.1", "anyio>=4.0.0", "watchfiles>=1.0.0", "sentence-transformers>=2.2.0", "onnxruntime>=1.16.0", "sounddevice>=0.4.6", "numpy>=1.24.0", "Pillow>=10.0.0", "rich>=13.0", "jinja2>=3.1", "keyring>=24.0"]
6969

7070

7171
[project.scripts]

0 commit comments

Comments
 (0)