Skip to content

feat: port to the mcp 2.x SDK; grammar prefetch retries; pilot grammar cache; serverInfo version (#124, #128, #127, #131) - #132

Merged
ViperJuice merged 5 commits into
mainfrom
feat/124-mcp2-port
Nov 9, 2026
Merged

ViperJuice merged 5 commits into
mainfrom
feat/124-mcp2-port

Conversation

@ViperJuice

@ViperJuice ViperJuice commented Nov 9, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Code changes for the next minor release (1.6.0). The version is not bumped and nothing is released, deployed or restarted.

Fixes #124
Fixes #128
Fixes #127
Fixes #131

mcp 2.0–2.3 API changes that applied

Sources: docs/migration.md at v2.3.0 and the v2.0.0–v2.3.0 release notes.

Change What this PR does
McpError → MCPError stdio_runner re-raises MCPError; MCPError(code, message) constructor
Lowlevel decorators (@server.list_tools(), @server.call_tool()) replaced by Server(..., on_list_tools=, on_call_tool=) with (ctx, params) handlers that return full result types _build_server(), _on_list_tools, _make_call_tool_handler()
request_ctx contextvar and server.request_context removed the session comes from ctx.session and is passed to call_tool(..., session=, task_request=)
The call_tool decorator's jsonschema input validation was removed the adapter validates against inputSchema and returns the identical Input validation error: ... result
Tool handler exceptions no longer become isError results (they become JSON-RPC errors) the adapter catches them and returns v1's isError result
Experimental Tasks runtime removed (server.experimental.enable_tasks, ServerTaskContext, mcp.shared.experimental.*); only the Task* wire types remain new mcp_server/cli/task_runtime.py: TaskRuntime (background task group, tasks/get / tasks/result / tasks/list / tasks/cancel via add_request_handler), TaskContext, TaskRequest. MCPTaskRegistry no longer subclasses the removed TaskStore and inlines create_task_state / is_terminal.
Server results validated against the negotiated revision's schema; the vendored 2025-11-25 tools/call surface accepts only CallToolResult a narrow middleware returns CreateTaskResult for task-augmented calls on handshake-era connections. The custom tasks/result payload is passed through the public mcp.types.methods.serialize_server_result so no 2026-era fields leak.
Schema validation: Tool.outputSchema must be type: object _tool() adds top-level "type": "object"; every oneOf branch was already an object
Field names camelCase → snake_case for attribute access (is_error, structured_content, input_schema, task_id, client_info, read_only_hint, ...) source and tests ported; wire JSON is unchanged (aliases)
Task.created_at / last_updated_at typed str (were datetime) wire_time() serializes them byte-identically to v1's pydantic datetime JSON
Extra fields on MCP types no longer preserved tasks/result returns a wire dict instead of GetTaskPayloadResult.model_validate(...), which would drop content and structuredContent
Unversioned servers report "" (1.x reported the mcp version, which was #128) Server(name, version=__version__)
Constructor params keyword-only; instructions= on the constructor Server(_SERVER_NAME, version=..., instructions=...)
2.3.0: initialize no longer sends experimental: {} create_initialization_options(experimental_capabilities={}) keeps v1's capability object
Sampling (ServerSession.create_message) deprecated (SEP-2577, MCPDeprecationWarning) still used on handshake-era connections; the warning is filtered at that call only (summarization.py)
streamablehttp_client removed; streamable_http_client(url, http_client=httpx2.AsyncClient) yields a 2-tuple PMCP pilot gateway(), keeping v1's Timeout(30, read=300) and trust_env=False
Timeouts are float seconds, not timedelta installed smoke, pilot
ClientRequest unions are not RootModels; the 2.x ClientSession has no Tasks client the SDK smoke test drives the task surface over raw 2025-11-25 JSON-RPC
httpx/httpx-sse → httpx2; pydantic-settings dropped from mcp's deps uv.lock refreshed; the repo imports neither httpx_sse nor pydantic_settings (httpx remains a direct project dependency)
stdio_server() signature unchanged (moves the wire to private fds) custom cancellable stdin/stdout pipes are still passed through unchanged

Not ported: scripts/patch_mcp_server.py and the two scripts/cli/ servers it generates (mcp_server_cli_patched.py, mcp_server_bm25.py). They are unreferenced legacy generators, and mcp_server_cli_patched.py already imports a module that does not exist (fix_mcp_bm25_integration).

Behavior-equivalence evidence

Raw STDIO wire diff, v1 vs v2. One request script (27 steps) ran against origin/main (mcp 1.27.0) and this branch (mcp 2.3.0) on the production-matrix fixture. It covers initialize, ping, tools/list, every tool, ready / not-found / fail-closed index_unavailable / path_outside_allowed_roots searches, invalid arguments, unknown tool, task mode on a non-task tool, a whole-repo task refusal (task_scope_unsupported), a file-scoped reindex task with tasks/result, tasks/list, then tasks/get and tasks/cancel on unknown and terminal tasks (a live cancel is covered by the in-process lifecycle test), write_summaries, summarize_sample, unserved resources/list / prompts/list, and an unknown method. After normalizing paths, UUIDs, timestamps and SHAs, the only differences are:

  1. serverInfo.version: 1.27.0 → 1.5.1 (STDIO initialize reports the mcp library version instead of the package version #128, intended).
  2. Each of the 8 outputSchemas gains top-level "type": "object" (required by the spec and enforced by the 2.x SDK; no change to what validates).
  3. tasks/result content blocks no longer carry "_meta": null, "annotations": null, a v1 serialization artifact. Every value is otherwise identical.
  4. Requests for methods the server does not serve answer JSON-RPC -32601 with the method name in data (v1: -32601 without data for unserved spec methods, and -32602 "Invalid request parameters" for unknown methods). This is SDK protocol behaviour (migration guide: "Unknown request methods now return -32601").

Every tool result, isError flag, structuredContent, readiness payload, error code and task status is otherwise byte-identical.

In-process wire tests (tests/test_mcp2_stdio_wire.py, 15 tests) pin: the v1 capability set, serverInfo.version, negotiation of each handshake revision, protocol-valid tools/list with execution only on reindex / write_summaries, the exact v1 input-validation and exception results, task-mode refusal, the full task lifecycle (create, _meta immediate response, list, cancel, result with related-task _meta, terminal-cancel code 0, unknown-task -32602), failed work → failed, and 2026-07-28 parity.

Protocol negotiation

Run against the wheel installed from this branch (index-it-mcp 1.5.1, mcp 2.3.0). Each client also completed reindex and search on a registered repo, except Claude Code, whose check covers only discover and tools/list.

Client First request Negotiated Result
Official Python SDK 2.3.0 ClientSession.initialize() initialize 2025-11-25 8 tools, reindex OK, search hit
Official Python SDK 2.3.0 Client(mode="auto") server/discover 2026-07-28 8 tools (no execution), reindex OK, search hit
Official Python SDK 1.27.0 ClientSession initialize 2025-11-25 8 tools, reindex OK, search hit
PMCP 3.0.3 gateway (STDIO), child spawned by the gateway initialize (clientInfo mcp-gateway 1.0.0) 2025-11-25 provision complete, catalog lists all 8 tools, gateway.invoke reindex / search_code / get_status OK
Claude Code 2.1.321 (claude mcp list, isolated HOME and config, no LLM call) server/discover 2026-07-28 ✔ Connected, tools/list served

Behaviour change for Claude Code: against origin/main (v1 SDK), Claude Code's server/discover probe failed and it fell back to initialize at 2025-11-25. With this port it stays on 2026-07-28. On that revision the tools and results are the same (test_modern_revision_serves_the_same_tools_and_results); results add the spec's resultType: "complete" and a serverInfo _meta stamp (version 1.5.1), tools/list omits execution, and the Tasks surface is absent (the revision has no Tasks). Claude Code never used tasks. This is recorded in CHANGELOG and SUPPORT_MATRIX.

Co-install proof

uv build → index_it_mcp-1.5.1-py3-none-any.whl (originally Requires-Dist: mcp<3,>=2.2; re-proven after review with mcp<2.4,>=2.2, see Review fixes), jsonschema>=4.20.0. Each case is a fresh Python 3.12.14 venv with uv pip install --no-config:

Install uv pip check Resolved mcp STDIO initialize + tools/list (raw JSON-RPC)
(a) wheel alone, unconstrained All installed packages are compatible (116) 2.3.0 (mcp-types 2.3.0) protocol 2025-11-25, serverInfo code-index-mcp-fast-search 1.5.1, 8 tools, get_status OK
(b) wheel + pmcp (latest → pmcp 3.0.3) compatible (118) 2.3.0 same
(c) wheel + phase-loop-runtime==0.7.30 compatible (126) 2.3.0 same

scripts/release_smoke.py --wheel --stdio --unconstrained also passed on the final head: the locked wheel (source contract, schema, Python client, two STDIO sessions, six lifecycle cases) and the unconstrained wheel (resolved mcp 2.3.0, raw initialize with 8 tools, two STDIO sessions).

#131: prefetch retries

The language pack raises RuntimeError("Download error: Failed to fetch manifest from <url>: http status: 504"), and resets read io: Connection reset by peer; both shapes were confirmed against a local HTTP server. Prefetch now behaves as follows:

  • Retried: HTTP 5xx and 429, connection resets, timeouts.
  • Never retried: 4xx, checksum mismatch (ChecksumMismatchError or message), anything unrecognized.
  • Budget: 5 attempts, equal-jitter backoff (4 s doubling, capped at 60 s; worst case 4+8+16+32 = 60 s of sleep), within 120 s. No retry starts past the budget.
  • Logging: logs and the final GrammarPrefetchError name only the URL, without credentials, query or fragment, and the status. The pack's raw text, which includes the full query string, is not logged or chained.
  • Coverage: CI, Release Automation and Dockerfile.production already call mcp-index grammars prefetch, so all of them retry.

tests/test_grammar_prefetch_retry.py (16 tests, fake downloader and fake clock): 504 then success passes after one sleep; persistent 504 fails after exactly 5 attempts with sleeps [4, 8, 16, 32] ≤ 120 s; 404 fails immediately with no sleep; 403, checksum and other failures are not retried; 429, 500, 502, reset and timeout are retried; the deadline stops retries; URL sanitization; and the CLI prints a sanitized error.

#127: pilot grammar cache

  • prepare: resolves --grammar-cache, else the caller's TREE_SITTER_LANGUAGE_PACK_CACHE_DIR, else <root>/grammar-cache (0700). It then prefetches with the installed wheel's own CLI and records grammar_cache in the manifest.
  • Every mode: validate_manifest refuses a missing cache, and with execute=True runs mcp-index grammars check offline (grammar_cache_unavailable).
  • Environment: the fixture, the .mcp.json server env, registration commands and the lifecycle smokes get TREE_SITTER_LANGUAGE_PACK_CACHE_DIR (kept by caps_apply for sandbox workers) and TREE_SITTER_LANGUAGE_PACK_MANIFEST_URL=sandbox-offline://network-denied, so no pilot process can download a grammar after prepare.
  • Contract tests in tests/test_v13_pmcp_pilot.py, 168 passing:
    • prepare with the default, argument and environment cache sources;
    • fixture, server and command envs carry the cache and the refusing manifest URL;
    • a manifest whose cache is gone is refused;
    • an end-to-end STDIO reindex with the pilot's fixture env and network-denied workers: it succeeds from the prefetched cache, and fails closed ("not in the local grammar cache … may not download it") from a cold one without writing to it.

Tests

  • Affected test files, one per invocation on dev0 (serial, nice, headroom-checked), all passing. Highlights: test_mcp2_stdio_wire.py (new, 15), test_grammar_prefetch_retry.py (new, 16), test_v13_pmcp_pilot.py (168), smoke/test_mcpbase_stdio_smoke.py (now also asserts serverInfo.version == __version__), smoke/test_mcpeval_sdk_surface.py (SDK metadata, structured and fail-closed surfaces, plus the task lifecycle over raw JSON-RPC), the mcptasks / mcpstruct / mcpmeta doc contracts, release metadata and the release smoke contract, and 66 doc-contract files.
  • black and isort are clean on every touched file. flake8 is unchanged from origin/main: one pre-existing F401 in the pilot.
  • The mypy ratchet reports exactly the same six violations as origin/main (all in files this PR does not touch, or pre-existing in grammar_cache.py).

Full suite on host ai, before the review fixes: passed at head 8941315: 4362 passed, 127 skipped, exit 0 in 14m22s (coverage 56.53%, above the 35% floor). Run as systemd-run --user unit ci-code-index-124-8941315 (MemoryMax=16G, CPUQuota=400%) under the shared Code-Index-MCP project lock, with an isolated HOME outside ~/tmp, ~/.local/bin on PATH, a prefetched private TREE_SITTER_LANGUAGE_PACK_CACHE_DIR, and CI's MCP_REQUIRE_GRAMMARS=1 and MCP_SKIP_PLUGIN_PREINDEX=true. Fresh clone, uv sync --locked --extra dev --extra semantic --python 3.12 (mcp 2.3.0), pytest tests/ -n 2 --timeout=300, logged to a durable file that was polled, not streamed.

Review fixes

The independent review graded this PR MERGE-READY, with 0 blocking findings, 2 should-fix and 6 nits. All eight are applied in b63bbc6, a new commit on top of the reviewed head; no history was rewritten.

Finding Fix Evidence
S1: the Tasks path relies on Server.middleware, marked provisional ("may change in a 2.x minor release"), and mcp>=2.2,<3 would admit an untested 2.4 mcp>=2.2,<2.4 (lock unchanged at 2.3.0; uv lock --check passes). TaskRuntime.install() calls ensure_middleware_contract(), which requires Server.middleware to be a list and ServerMiddleware.__call__ to take (ctx, call_next), and refuses to start with an actionable message otherwise; the guarded mcp.server.context import does the same. CHANGELOG and SUPPORT_MATRIX record the cap, with the follow-up to re-test and widen it per mcp minor. test_startup_refuses_an_mcp_without_the_middleware_contract. Capped wheel (Requires-Dist: mcp<2.4,>=2.2), each in a fresh Python 3.12 venv: alone → mcp 2.3.0; + pmcp → pmcp 3.0.3, mcp 2.3.0; + phase-loop-runtime==0.7.30 → mcp 2.3.0. All three have a clean uv pip check and answer initialize, tools/list and get_status. Floor check: wheel + mcp==2.2.0 passes the guard and serves too.
S2: a second concurrent tasks/result orphaned the first, which never got a reply (pre-existing in v1) One shared anyio.Event per task. notify_update sets it and drops it, so every waiter wakes and re-checks. Deleting, expiring and evicting a task set the event, so parked waiters wake and get -32602. test_concurrent_task_results_for_one_task_are_all_answered (the reviewer's repro, now with 3 waiters; all answered). test_running_tasks_never_expire_and_deleting_wakes_waiters. The reviewer's disconnect repro is ported as test_client_disconnect_mid_task_finishes_the_task_without_crashing.
N1: task retention was unbounded and client-controlled ttl is clamped to 1 h, and defaults to 1 h when absent; the effective value is returned, as the spec allows. Expiry starts at the terminal transition, so a long reindex is never purged mid-run. At most 256 tasks are kept: the oldest terminal ones are evicted first, and a new task is refused only while 256 are running. test_task_ttl_is_clamped_and_defaulted (×3), test_task_retention_evicts_finished_tasks_and_refuses_when_all_run
N2: sampling was attempted for every chunk on 2026-07-28, where it cannot work _has_sampling_capability returns False when the session's protocol version is not a handshake version, and logs once per writer test_sampling_is_skipped_on_protocol_revisions_without_back_channel (×2)
N3: malformed JSON lines were dropped with no reply The repo's STDIO input wrapper runs the SDK's own jsonrpc_message_adapter.validate_json check. Invalid JSON gets -32700 (id null); JSON that is not JSON-RPC gets -32600, echoing a string or integer id (JSON-RPC 2.0 §5.1). Blank lines are skipped. A warning logs only the size and the code. test_malformed_stdio_lines_get_json_rpc_errors_and_valid_lines_pass. End to end through the real server's pipes: not json → -32700, {"jsonrpc":"2.0","id":8} → -32600 id 8, then initialize and ping answered. A ping sent right before stdin EOF goes unanswered on both trees, because of the pre-existing EOF shutdown, unchanged here.
N4: #131 did not retry DNS failures, refused connections or 408 Added connection refused, dns error / failed to lookup / temporary failure in name resolution / name or service not known, and HTTP 408. The docstring says the 120 s budget bounds the backoff, not a single attempt. The real pack messages for a refused port and an unresolvable .invalid host were captured locally and both classify as transient; 4 new parametrized cases, 20 tests total
N5: the docstring said Tasks are defined on 2025-11-25 and earlier Reworded: served on every handshake revision (2024-11-05 → 2025-11-25), as the v1 SDK did; the spec defines Tasks only from 2025-11-25. Same for supports_tasks. —
N6: the CHANGELOG said "same … error codes" Now "except for methods the server does not serve (below)" —

Re-verification on dev0. Each affected file ran alone, serially, under nice, with no xdist, after a load and I/O-pressure check (1-minute load 0.9–3.5):

File Result
test_mcp2_stdio_wire.py 26 passed
test_grammar_prefetch_retry.py 20 passed
test_v13_pmcp_pilot.py 168 passed
test_mcptasks_reindex.py 8 passed
test_mcptasks_write_summaries.py 2 passed
docs/test_mcptasks_protocol_contract.py 4 passed
test_mcp_server_cli.py 18 passed
test_mcpauth_stdio_contract.py 4 passed
docs/test_mcpstruct_call_tool_contract.py 2 passed
smoke/test_mcpbase_stdio_smoke.py 2 passed
smoke/test_mcpeval_sdk_surface.py 1 passed
test_release_metadata.py 27 passed
smoke/test_release_smoke_contract.py 30 passed
test_release_notes.py 4 passed
test_grammar_registry.py 33 passed
test_stdio_tool_descriptions.py 13 passed
test_v13_safety.py 39 passed
doc-contract files 61 passed

black and isort are clean, and flake8 is clean on every changed file. The mypy ratchet output is identical to origin/main.

Full suite on host ai at the final head b63bbc6: passed: 4377 passed, 127 skipped, exit 0 in 12m10s (coverage 56.62%). That is 15 more passing tests than at 8941315, matching the new review-fix tests. It used the same isolation as the first run: unit ci-code-index-124-b63bbc6 under systemd-run --user (MemoryMax=16G, CPUQuota=400%), the shared Code-Index-MCP project lock, a fresh clone, an isolated HOME outside ~/tmp, ~/.local/bin on PATH, a freshly prefetched private grammar cache (47/47), MCP_REQUIRE_GRAMMARS=1, MCP_SKIP_PLUGIN_PREINDEX=true, uv sync --locked --extra dev --extra semantic --python 3.12 (mcp 2.3.0), and pytest tests/ -n 2 --timeout=300, logged to a durable file that was polled.

Follow-ups / not done here

  • Version bump to 1.6.0 and release: separate, owner-approved.
  • mcp cap: re-test against each new mcp minor (starting with 2.4) and widen mcp>=2.2,<2.4 only after the Tasks middleware path, the wire tests and the co-installs pass on it (review S1).
  • The PMCP pilot still pins pmcp 2.7.3 (prepare / validate_manifest), but the latest pmcp is 3.0.3, the version this PR co-installs with. Re-running PMCP offline and rehearsal acceptance, against 1.5.1 or this candidate, needs that pin decision and was not run here.
  • Task-augmented calls need a client that speaks the 2025-11-25 Tasks wire. The official Python SDK 2.x has no Tasks client, and the 2026-07-28 Tasks extension (SEP-2663) is not implemented by the SDK yet.

🤖 Generated with Claude Code

ViperJuice and others added 4 commits November 9, 2026 16:14
…rsion (#124, #128)

- Require mcp>=2.2,<3 (co-installs with pmcp 3.x) and jsonschema; refresh uv.lock.
- Register list_tools/call_tool through the 2.x constructor handlers. A
  tools/call adapter keeps the v1 decorator contract: jsonschema input
  validation and exceptions as isError results.
- mcp 2.x removed the experimental Tasks runtime. A repo-owned TaskRuntime keeps
  task-augmented reindex/write_summaries, the tasks capability and
  tasks/get|result|list|cancel on handshake-era connections.
- serverInfo.version is mcp_server.__version__, not the mcp library version.
- outputSchemas carry the top-level "type": "object" the MCP schema requires
  and the 2.x SDK enforces.
- Port camelCase attribute access, MCPError, task timestamps, sampling
  deprecation, the installed smoke and the PMCP pilot client to the 2.x API.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…131)

`mcp-index grammars prefetch` made one attempt at the language pack's GitHub
release assets, so a transient 504 failed CI, image builds and release
automation. Prefetch now retries HTTP 5xx and 429, connection resets and
timeouts with equal-jitter exponential backoff (5 attempts, 4s doubling to a
60s cap, within a 120s budget). 4xx such as 404, checksum mismatches and
unrecognized failures fail on the first attempt. Retries and the final error
name only the URL (no credentials, query or fragment) and the status; the
pack's raw message, which carries the full URL, is not logged or chained.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… grammar cache (#127)

The pilot gave its fixture a fresh XDG_CACHE_HOME and no grammar location, so
network-denied sandbox workers could not load grammars and offline mode's
reindex failed with grammar_unavailable.

- prepare resolves an explicit cache (--grammar-cache, else the caller's
  TREE_SITTER_LANGUAGE_PACK_CACHE_DIR, else <root>/grammar-cache, mode 0700),
  prefetches into it with the installed wheel's CLI, and binds it in the
  manifest.
- Every mode re-checks the cache offline before running
  (grammar_cache_unavailable otherwise).
- Fixture, server, registration and lifecycle environments get
  TREE_SITTER_LANGUAGE_PACK_CACHE_DIR, which sandbox workers keep, plus the
  offline manifest URL, so no pilot process downloads a grammar after prepare.
- Contract tests, including an end-to-end STDIO reindex with downloads refused:
  passes from the prefetched cache, fails closed from a cold one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#124, #128, #127, #131)

- CHANGELOG [Unreleased]: the port, the serverInfo version, prefetch retries,
  the pilot grammar cache, and upgrade notes, including dropping any mcp<2 pin
  installed alongside index-it-mcp.
- SUPPORT_MATRIX: the mcp>=2.2,<3 requirement, PMCP 3.x co-install, and the
  protocol revisions each verified client negotiates.
- README, GETTING_STARTED, MCP_CONFIGURATION: source and main wheels need
  mcp>=2.2,<3 and co-install with PMCP 3.x.
- A wire test pins 2026-07-28 parity with the handshake era: same tools and
  results, no Tasks surface.
- black/isort on touched files; snake_case ToolAnnotations and typed helpers,
  so the mypy ratchet adds nothing beyond main's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 9, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-09T21:47:31.013911Z 8941315 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

… tasks/result, nits)

S1: cap mcp at >=2.2,<2.4 (still locked at 2.3.0; co-installs with pmcp 3.0.3
and phase-loop-runtime 0.7.30). The Tasks runtime depends on Server.middleware,
which mcp marks provisional. TaskRuntime.install() now checks the middleware
contract (a list of (ctx, call_next) middleware) and refuses to start with an
actionable message if it is missing; the guarded import does the same. The
cap, and re-testing each mcp minor before widening it, are noted in CHANGELOG
and SUPPORT_MATRIX.

S2: concurrent tasks/result requests for one task are all answered. The
registry keeps one shared event per task that notify_update sets and drops, so
every waiter wakes. Deleting, expiring or evicting a task wakes its waiters,
which then get -32602.

N1: task TTLs are clamped to 1 h (default 1 h; the effective ttl is returned).
Expiry starts when a task finishes. At most 256 tasks are kept: the oldest
finished tasks are evicted first, and a new task is refused only while 256 are
running.
N2: skip MCP sampling on 2026-07-28 sessions, which have no back-channel; log once.
N3: answer malformed STDIO lines with JSON-RPC -32700 (id null) or -32600
(echoing a valid id) instead of dropping them; log only size and code.
N4: prefetch also retries refused connections, DNS lookup failures and HTTP
408; the docstring says the budget bounds the backoff, not one attempt.
N5: the runtime docstring says Tasks are served on every handshake revision,
as v1 did, and that the spec defines them only from 2025-11-25.
N6: the CHANGELOG's error-code line now notes the -32601 exception.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ViperJuice
ViperJuice merged commit e193baf into main Nov 9, 2026
14 checks passed
ViperJuice added a commit that referenced this pull request Nov 9, 2026
test_fixture_server_and_workers_get_the_prefetched_cache_with_downloads_denied
(added in #132) calls create_fixture, which imports the sibling module
v13_pilot_estimate the way the pilot does when run as a script. It passed only
when an earlier test on the same xdist worker had put scripts/ on sys.path.
The ai full suite on 93b7e9f hit the other ordering (ModuleNotFoundError), and
running the test alone reproduces it. Prepend scripts/ with monkeypatch, as the
file's other create_fixture test already does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ViperJuice added a commit that referenced this pull request Nov 9, 2026
…acity error (#132 re-check nits) (#135)

* fix(mcp): task retention floor, drop malformed responses, clearer capacity error (#132 re-check nits)

R1: task TTLs have a one-minute floor (MIN_TASK_TTL_MS), so `ttl: 0` can no
longer expire a finished task before a woken tasks/result reads it. Expiry and
eviction never remove a finished task that still has a tasks/result waiter
(counted in wait_for_update, so a woken waiter that has not yet resumed is
covered) or that finished within the floor. Eviction takes the oldest
evictable finished task. With every slot running, awaited or just finished, a
new task is refused instead.

R2: the POSIX STDIO pre-check drops a malformed JSON-RPC response (`result` or
`error` with no `method`) with a debug log. It is not answered, so its id,
which belongs to the server's own request-id space, is never echoed.

R3: the CHANGELOG says the malformed-line replies apply to the POSIX STDIO
transport; Windows uses the SDK's reader, which still drops them silently.

R4: a full registry raises TaskCapacityError, which TaskRuntime.run_task
re-raises as MCPError. The client therefore gets "Too many tasks (N running or
awaiting retrieval); retry after one finishes" instead of
"details": "ValueError".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(pilot): do not depend on test order for the pilot's helper import

test_fixture_server_and_workers_get_the_prefetched_cache_with_downloads_denied
(added in #132) calls create_fixture, which imports the sibling module
v13_pilot_estimate the way the pilot does when run as a script. It passed only
when an earlier test on the same xdist worker had put scripts/ on sys.path.
The ai full suite on 93b7e9f hit the other ordering (ModuleNotFoundError), and
running the test alone reproduces it. Prepend scripts/ with monkeypatch, as the
file's other create_fixture test already does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@ViperJuice ViperJuice mentioned this pull request Nov 10, 2026
ViperJuice added a commit that referenced this pull request Nov 10, 2026
Prepare index-it-mcp 1.6.0: the mcp 2.x port (#124 with #128, #131 and #127
in #132, re-check fixes in #135), so index-it-mcp co-installs with PMCP 3.x,
and the watcher sweep, incremental sync and generation-retention fixes (#114
and #111 in #129, follow-ups in #130).

- Bump to 1.6.0 in pyproject, __version__, the project's uv.lock entry, the
  Release Automation default, both Docker installers, the release docs and the
  release-gate tests.
- Turn [Unreleased] into [1.6.0] - 2026-11-09 (Changed / Fixed / Upgrade notes
  / Known follow-ups). Upgrade notes: mcp>=2.2,<2.4 is required, so drop any
  mcp<2 pin; PMCP 3.x co-install; Claude Code negotiates 2026-07-28 without
  Tasks; the cap widens per re-tested mcp minor; retention is automatic but
  never prunes pre-upgrade generations. Follow-ups #133, #134, #121, #117.
- Docs: 1.6.0 is the prepared candidate, 1.5.1 is published with partial
  delivered acceptance, and 1.3.0 through 1.5.0 are yanked on PyPI.
- The raw JSON-RPC initialize probe now requires serverInfo.version to equal
  the installed index-it-mcp version (#128), so release smoke guards it.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant