Skip to content

Commit 0b27797

Browse files
jgravelleclaude
andcommitted
release: v1.108.67 - demote the language enum under compact_schemas
The language filter's ~76-value enum (the full LANGUAGE_REGISTRY, ~200 tokens of mechanical names an agent already knows) is demoted to a plain string filter under compact_schemas, while the full surface keeps the enum. The param stays fully usable (the tool accepts any language string). This brings core_compact back under the section-10 <=4000 success criterion (4162 -> 3956) after recent tool additions pushed it over, and also trims standard_compact. Generalized by param name via _COMPACT_DEMOTE_ENUM_PARAMS so every tool with a language enum benefits. schema_baseline.json refreshed to honest live counts; new test_compact_demotes_language_enum_keeps_capability pins the behavior. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent b57f82e commit 0b27797

6 files changed

Lines changed: 76 additions & 10 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.67] - 2026-06-21 - Demote the language enum under compact_schemas
6+
7+
### Changed
8+
9+
- **The `language` filter's ~76-value enum is demoted to a plain string under
10+
`compact_schemas`**, reclaiming ~200 tokens from the smallest tool surfaces
11+
while keeping the parameter fully usable (the tool already accepts any
12+
language string; an unknown one simply matches nothing). The full
13+
(non-compact) surface keeps the enum unchanged. This brings `core_compact`
14+
back under the 4,000-token §10 success criterion (4,162 → 3,956) after recent
15+
releases had pushed it over, and also trims `standard_compact`. Generalized by
16+
parameter name via `_COMPACT_DEMOTE_ENUM_PARAMS`, so every tool exposing a
17+
`language` enum benefits. `benchmarks/schema_baseline.json` refreshed to the
18+
new honest live counts; new `test_compact_demotes_language_enum_keeps_capability`
19+
pins the behavior (enum gone under compact, present in full).
20+
521
## [1.108.66] - 2026-06-21 - The Counter: adaptive tool surface (order / menu / route)
622

723
### Added

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.67 — Demote the `language` enum under `compact_schemas`. The `language` filter's full LANGUAGE_REGISTRY enum (~76 values, ~200 tokens of mechanical names an agent already knows) is dropped to a plain `type:string` filter under compact (capability preserved — the tool accepts any language string; unknown → empty match); the full surface keeps the enum. Brings `core_compact` back under the §10 ≤4000 line (4162→**3956**, ~44 margin) after 1.108.55-66 tool additions pushed it to 4146; also trims `standard_compact` (16339→16149). New `_COMPACT_DEMOTE_ENUM_PARAMS` frozenset (keyed by PARAM name, so search_symbols/search_ast/etc. all benefit) + an enum-demotion pass alongside the existing `_COMPACT_STRIP_PARAMS` loop in `_build_tools_list`. `schema_baseline.json` refreshed to honest live counts (the wholesale `capture_schema_baseline.py` run is now CORRECT — it writes 3956, the very thing that made it wrong at v1.108.66 when it wrote 4146). New `test_compact_demotes_language_enum_keeps_capability` pins it (enum gone under compact, present in full). Closes the open follow-up from the Counter release. No behavior change on the full tier.
45
- **Version:** 1.108.66 — The Counter: adaptive tool surface (`order` / `menu` / `route`). Collapses the ~83-tool footprint behind a 3-tool front door to cut the per-turn schema tax + dispatch dilution, without removing any capability. New `JCODEMUNCH_TOOL_SURFACE` env / `tool_surface` config: `counter` makes `list_tools` return only the front door + `_ALWAYS_PRESENT_TOOLS`; any other value (default `full`) is **byte-identical** to prior behavior (front-door tools hidden, still callable). **`order(action,args)`** re-enters the normal `call_tool` pipeline after a charter gate (read-only by default; index/session state-changers need `allow_state_change=true`; exec/file-write verbs refused unconditionally — a forward-looking tripwire, the suite ships none). **`menu(query?,limit?)`** = idf-weighted catalog search. **`route(task,repo?,execute?)`** = curated intent map (`_INTENT_RULES`) + catalog-search fallback → ranked actions w/ arg templates; `execute=true` recommends AND dispatches in one call; recommends `assemble_task_context`/`plan_turn` for context intents (honest mapper, NOT a plan_turn reskin — plan_turn recommends symbols, not tools). New `counter.py` (pure logic, no server import) + 4 surgical `server.py` edits (front-door globals after `_UNDISABLEABLE_TOOLS`; surface-collapse in `_build_tools_list`; handlers before `call_tool`; early front-door branch inside `call_tool`). `tests/test_counter.py` (19) incl. a charter CI gate (no catalog action trips the exec/write tripwire) + a STATE_CHANGING-in-catalog drift guard. Full surface stays 83 tools; front door appears only under `tool_surface=counter`. Pre-existing schema-budget drift (unrelated, proven via stash) fixed by a surgical `schema_baseline.json` refresh of the two breached keys (`core_full`→5097, `standard_full`→17360); core_compact LIVE 4146 is over the 4000 v2 design intent (prior-release bloat, separate trim-or-relax follow-up). See `docs/prd-adaptive-tool-surface.md` + memory `project_tool_router_prd`.
56
- **Version:** 1.108.65 — Surface the composition tools (`assemble_task_context` / `plan_turn`) in agent-facing docs. Answers the "these code-index tools work in isolation" critique (a competitor's launch claim, see memory `reference_competitor_atelier`): the suite already HAS an orchestration layer — `assemble_task_context` (one-call, intent-classified, single-budget capsule) and `plan_turn` (confidence-routed opening move) — it just wasn't legible. `assemble_task_context` was absent from every agent-facing surface; now added to `_CLAUDE_MD_POLICY` (init.py — the injected CLAUDE.md/Cursor/Windsurf policy) "Opening move" as the one-call shortcut, plus a new README "What you get → One-call task orchestration" subsection + "Why agents need this" bullet. `skills.py` SKILL.md already had a "Task orchestration" section (unchanged). Docs + one policy constant; **no tool behavior change**. `test_claude_md_policy.py` REQUIRED_POLICY_TERMS pins `assemble_task_context` so visibility can't regress (the line is full-tier only, so `_filter_policy_for_tools` drops it under core/standard exactly like `plan_turn` — existing filter tests unaffected, 22 pass). Legibility fix, not capability chase ([[feedback_clean_room_innovation]]).
67
- **Version:** 1.108.64 — Logging honors the `log_file` / `log_level` config keys. `_setup_logging` read only `args.log_file`/`args.log_level` (argparse defaults baked from `JCODEMUNCH_LOG_FILE`/`JCODEMUNCH_LOG_LEVEL` env), so a path set via `config set log_file <path>` was **inert** — the `log_file` config key existed (DEFAULTS/CONFIG_TYPES/template, "=== Logging ===") but the server never read it. New pure helper `_resolve_log_config(args)` applies one precedence chain: **explicit CLI flag > env var > config key > hardcoded default** (WARNING / stderr); `_add_common_args` `--log-level`/`--log-file` defaults changed `None` so the chain (not argparse) owns env resolution. **Why:** lets the jMunch Console enable server file logging by writing the config key + restarting the server (the server is client-launched, so the Console can't inject an env var into it; and `_setup_logging` ignored config). Purely additive — nothing set anywhere → WARNING+stderr exactly as before; an explicit env/CLI from the launching client still wins over config. `_setup_logging` is the only reader of those args (verified). New `tests/test_v1_108_64.py` (6: default/config-file/config-level/env-overrides-config/cli-wins/uppercased). See [[project_jmunch_console]].

benchmarks/schema_baseline.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
2-
"core_compact": 3977,
2+
"core_compact": 3956,
33
"core_full": 5097,
4-
"standard_compact": 15585,
4+
"standard_compact": 16149,
55
"standard_full": 17360,
6-
"full_compact": 16895,
7-
"full_full": 17840
6+
"full_compact": 17459,
7+
"full_full": 18670
88
}

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "jcodemunch-mcp"
3-
version = "1.108.66"
3+
version = "1.108.67"
44
description = "Token-efficient MCP server for source code exploration via tree-sitter AST parsing"
55
readme = "README.md"
66
requires-python = ">=3.10"

src/jcodemunch_mcp/server.py

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,14 @@ async def _apply_model_announcement(model: str) -> dict:
525525
"index_folder": {"extra_ignore_patterns", "incremental"},
526526
}
527527

528+
# Params whose enum is demoted to a plain string filter under compact_schemas.
529+
# The `language` enum is the full LANGUAGE_REGISTRY (~76 values) — ~200 tokens
530+
# of mechanical names an agent already knows. Dropping the enum keeps the param
531+
# fully usable as a free-string filter (the tool tolerates any language string)
532+
# while reclaiming the tokens. Keyed by param name so every tool that exposes a
533+
# `language` enum (search_symbols, search_ast, ...) benefits across all tiers.
534+
_COMPACT_DEMOTE_ENUM_PARAMS: frozenset[str] = frozenset({"language"})
535+
528536
# Tools eligible for Agent Selector complexity scoring
529537
_AGENT_SELECTOR_TOOLS = frozenset({
530538
"get_ranked_context", "get_context_bundle", "search_symbols",
@@ -3566,12 +3574,21 @@ def _build_tools_list() -> list[Tool]:
35663574
# --- Compact schemas: strip rarely-used params ---------------------------
35673575
if config_module.get("compact_schemas", False):
35683576
for tool in tools:
3577+
if not isinstance(tool.inputSchema, dict):
3578+
continue
3579+
props = tool.inputSchema.get("properties")
3580+
if not props:
3581+
continue
35693582
strip_set = _COMPACT_STRIP_PARAMS.get(tool.name)
3570-
if strip_set and isinstance(tool.inputSchema, dict):
3571-
props = tool.inputSchema.get("properties")
3572-
if props:
3573-
for param in strip_set:
3574-
props.pop(param, None)
3583+
if strip_set:
3584+
for param in strip_set:
3585+
props.pop(param, None)
3586+
# Demote large mechanical enums to free-string filters (capability
3587+
# preserved; the tool accepts any string for these params).
3588+
for param in _COMPACT_DEMOTE_ENUM_PARAMS:
3589+
pschema = props.get(param)
3590+
if isinstance(pschema, dict) and "enum" in pschema:
3591+
props[param] = {k: v for k, v in pschema.items() if k != "enum"}
35753592

35763593
# Merge descriptions from config (runs after disabled_tools filter)
35773594
_apply_description_overrides(tools)

tests/test_schema_budget.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,35 @@ def test_v2_success_criterion_core_compact_under_4000():
9393
f"v2.0.0 success criterion requires core + compact_schemas <= 4000 tokens; "
9494
f"current baseline is {core_compact}."
9595
)
96+
97+
98+
def test_compact_demotes_language_enum_keeps_capability():
99+
"""Under compact_schemas, the ~76-value `language` enum is demoted to a
100+
plain string filter (reclaims ~200 tokens) but the param stays usable. The
101+
full surface keeps the enum. Guards the core_compact-under-4000 headroom."""
102+
from jcodemunch_mcp import config as config_module
103+
from jcodemunch_mcp.server import _build_tools_list
104+
105+
cfg = config_module._GLOBAL_CONFIG # type: ignore[attr-defined]
106+
original = {k: cfg.get(k) for k in ("tool_profile", "compact_schemas")}
107+
try:
108+
cfg["tool_profile"] = "core"
109+
110+
cfg["compact_schemas"] = True
111+
ss = next(t for t in _build_tools_list() if t.name == "search_symbols")
112+
lang = ss.inputSchema["properties"]["language"]
113+
assert "enum" not in lang, "language enum must be demoted under compact"
114+
assert lang.get("type") == "string", "demoted language must remain a string filter"
115+
assert lang.get("description"), "demoted language must keep its description"
116+
117+
cfg["compact_schemas"] = False
118+
ss_full = next(t for t in _build_tools_list() if t.name == "search_symbols")
119+
assert len(ss_full.inputSchema["properties"]["language"].get("enum", [])) > 10, (
120+
"full surface must keep the language enum"
121+
)
122+
finally:
123+
for k, v in original.items():
124+
if v is None:
125+
cfg.pop(k, None)
126+
else:
127+
cfg[k] = v

0 commit comments

Comments
 (0)