Skip to content

fix: composition defects across codegen, replay, daemon elicitation, and public types - #262

Merged
steipete merged 7 commits into
mainfrom
steipete/mcp2-hardening-integration
Aug 2, 2026
Merged

fix: composition defects across codegen, replay, daemon elicitation, and public types#262
steipete merged 7 commits into
mainfrom
steipete/mcp2-hardening-integration

Conversation

@steipete

@steipete steipete commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator

Fixes composition failures that survived the green suite — cases where two individually-correct pieces of the MCP 2.0 work broke at their boundary. Found by adversarial review; each fix ships with a regression test that fails before and passes after.

  • generate-cli silently dropped pinned protocol versions. normalizeDefinition() didn't copy protocolVersion, so a server pinned to 2026-07-28 or legacy reverted to auto-negotiation in generated CLIs and inline-JSON targets. Auditing that function against the config schema turned up four more silently-dropped aliases: token_cache_dir, client_name, oauth_redirect_url, oauth_scope.
  • Modern recordings still broke across client-version drift. fix: resources/list pagination + replay tolerance for protocol drift (MCP 2.0 prep) #254 made initialize/server/discover matching drift-tolerant, but every other modern request still compared per-request client identity _meta byte-for-byte — so a recording made by 0.12.4 failed at tools/list under 0.12.5. The advertised tolerance was passing vacuously because the test stopped after discovery. Matching now normalizes only the three reserved io.modelcontextprotocol/* identity keys; user-supplied _meta is still compared strictly.
  • Keep-alive servers silently lost interactive elicitation. Calls to lifecycle: "keep-alive" servers route through the daemon, which always installed the non-interactive responder — so a tool needing input declined instantly with no prompt and no hint. Forwarding prompts across the daemon socket needs protocol multiplexing beyond this scope, so the daemon now returns the actionable hint to the calling CLI, and the limitation plus its lifecycle: "ephemeral" workaround is documented rather than hidden.
  • ConnectionInfo and other publicly-reachable types weren't exported, so import type { ConnectionInfo } from 'mcporter' failed TS2305.
  • e2e fixtures leaked children on failed startup, and a partial Promise.all startup left the successfully-started sibling unreferenced by cleanup.
  • New coverage for subscriptions/listen + toggle_tool and modern cache metadata (ttlMs/cacheScope) — previously implemented in the fixtures but never asserted in CI.
  • Two false README claims corrected, now guarded by a test: legacy handshakes are no longer "byte-identical" (the client advertises elicitation capabilities), and CI exercises representative fixture paths rather than the full surface.

Proof: pnpm check clean; 970 passed / 13 skipped (the 10 new skips are the opt-in live suite from #261); autoreview clean (0.98).

🤖 Generated with Claude Code

@clawsweeper

clawsweeper Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper status: review started.

I am starting a fresh review of this pull request: fix: composition defects across codegen, replay, daemon elicitation, and public types This is item 1/1 in the current shard. Shard 0/1.

This placeholder means the worker is alive and reading the current context. I will edit this same comment with the actual review when the claws are done clicking.

Crustacean status: shell secured, claws on keyboard, evidence pebbles being sorted.

@steipete
steipete merged commit 1d49d46 into main Aug 2, 2026
5 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6b01dca397

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/daemon/host.ts
const snapshot = await loadConfigSnapshot(options);
const elicitation = createNonInteractiveElicitationResponder();
const elicitation = createNonInteractiveElicitationResponder({
onDecline: () => daemonRequestNotices.getStore()?.add(NON_INTERACTIVE_ELICITATION_HINT),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Capture daemon elicitation notices for warmed servers

When a keep-alive stdio server is already connected before the call that prompts (for example after listTools/pre-warm or an earlier non-prompting call), the MCP client's stream callbacks run under the async context that existed when the transport was created, not under the current processRequestWithNotices() call. This getStore() can therefore be undefined or point at an old Set, so the daemon response omits notices and the caller still gets a silent decline despite the new warning path; store the notice collector on the in-flight daemon operation rather than relying on AsyncLocalStorage attached to long-lived transports.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant