Skip to content

Tools: x-mcp-header annotation tooling + -32602 unknown-tool rendering (#1632) - #1735

Merged
cliffhall merged 6 commits into
v2/mainfrom
v2/1632-x-mcp-header-tooling
Jul 22, 2026
Merged

Tools: x-mcp-header annotation tooling + -32602 unknown-tool rendering (#1632)#1735
cliffhall merged 6 commits into
v2/mainfrom
v2/1632-x-mcp-header-tooling

Conversation

@cliffhall

@cliffhall cliffhall commented Jul 21, 2026

Copy link
Copy Markdown
Member

Closes #1632

Card 9/10 of the SDK-v2 + New Spec stack. Adds SEP-2243 x-mcp-header tooling to the Tools tab and adjusts result rendering for SDK v2's unknown-tool behavior. Also upgrades the MCP SDK to 2.0.0-beta.5 (from beta.4) across all clients.

What's in it

core/json/xMcpHeader.ts — a faithful port of the SDK's (non-public) scanXMcpHeaderDeclarations. Validates every SEP-2243 constraint: primitive types only (string/integer/boolean, plus number to match the SDK's conformance-referee carve-out), RFC 9110 header-name token, case-insensitive uniqueness, and the statically-reachable properties-only chain (an annotation under items/oneOf/additionalProperties/$defs/etc. invalidates the tool). Also derives the mirrored Mcp-Param-{Name} params for a kept tool. Pure and 100%-covered.

Excluded tools shown with reasons. The spec makes exclusion mandatory — a modern Streamable HTTP client MUST drop a tool with an invalid annotation, and the SDK's listTools() does so silently. InspectorClient now re-lists the raw (unfiltered) tools/list and scans it to recompute the excluded set with each reason — gated to the modern non-stdio era (matching the SDK's own gate), recomputed on every aggregate refresh. Surfaced via a new excludedToolsChange event + getExcludedTools() and useInspectorClient().excludedTools. The Tools sidebar lists them struck-through under an "Excluded (SEP-2243)" divider, with the reason on hover.

Mirrored args indicated. ToolDetailPanel shows a "Mirrored request headers (SEP-2243)" section listing each arg → Mcp-Param-{Name}, with a note that the browser omits Mcp-Param-* (mirroring is Node/proxy-only).

-32602 rendering. SDK v2 rejects an unknown-tool call with -32602 instead of returning an isError result — which previously rendered nothing (the result panel only shows on a result). New ToolCallErrorPanel renders a thrown tool-call error. Because -32602 is the generic Invalid params code, the heading is chosen from the message: "Unknown Tool" (with the excluded/removed hint) when the message names a missing tool, else "Invalid Parameters" (with a schema hint).

Proof screenshots

End-to-end verification in the web client against the modern showcase server (test-servers/configs/xmcpheader-modern-http.json). More detail in pr-screenshots/README.md.

Excluded tools + reasoninvalid_header_tool is dropped from tools/list; the Inspector surfaces it struck-through under an "Excluded (SEP-2243)" divider, with the scan reason on hover.

Mirrored request headersget_weather's city arg mirrors to Mcp-Param-City.

-32602 → "Unknown Tool" — a -32602 whose message names a missing tool (reproduced by swapping the sessionless server to one without echo while the cached list still showed it).

-32602 → "Invalid Parameters" — a -32602 that is not an unknown tool renders under a distinct heading with a schema hint (triggered live via the trigger_invalid_params test tool, which returns a real -32602 JSON-RPC error). This is the disambiguation added in the review response.

Testing

  • New unit tests for the scan module, ToolControls excluded section, ToolDetailPanel mirrored headers, ToolCallErrorPanel (all three error kinds), ToolsScreen error branch, and the useInspectorClient excluded-tools subscription.
  • New Storybook stories for ToolCallErrorPanel.
  • New integration suite (inspectorClient-excluded-tools.test.ts) driving a real modern server with an invalid-header tool fixture (createInvalidHeaderTool), covering exclusion, the reason/event, pagination walk, legacy no-op, disconnect clear, and the best-effort recompute.
  • Added manual-testing showcase config (xmcpheader-modern-http.json) + invalid_header_tool / trigger_invalid_params presets, documented in the README.
  • validate + per-file ≥90 coverage gate (web/cli/tui/launcher) + Storybook + smoke all green locally. SDK beta.5 bump verified end-to-end via the CLI/TUI/web smokes and the browser smoke above.

🤖 Generated with Claude Code

https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5

#1632)

Card 9/10 of the SDK-v2 + New Spec stack. Adds SEP-2243 `x-mcp-header`
tooling to the Tools tab and adjusts result rendering for SDK v2's
unknown-tool behavior, and bumps the SDK to 2.0.0-beta.5.

- **`core/json/xMcpHeader.ts`**: a faithful port of the SDK's (non-public)
  `scanXMcpHeaderDeclarations` — validates every SEP-2243 constraint
  (primitive types incl. `number`, RFC 9110 token, case-insensitive
  uniqueness, statically-reachable `properties`-only chain) and derives the
  mirrored `Mcp-Param-{Name}` params for a kept tool.

- **Excluded tools**: the SDK silently drops tools with invalid annotations
  from `tools/list`. InspectorClient now re-lists the RAW (unfiltered) list
  and scans it to recompute the excluded set with reasons — gated to the
  modern non-stdio era (matching the SDK's gate), recomputed on every
  aggregate refresh, surfaced via a new `excludedToolsChange` event +
  `getExcludedTools()` and `useInspectorClient().excludedTools`. The Tools
  sidebar lists them struck-through under an "Excluded (SEP-2243)" divider
  with the reason on hover.

- **Mirrored args**: ToolDetailPanel shows a "Mirrored request headers"
  section listing each arg → `Mcp-Param-{Name}` (with the browser-omits note).

- **-32602 rendering**: SDK v2 rejects an unknown-tool call with `-32602`
  instead of an `isError` result, which previously rendered nothing. New
  ToolCallErrorPanel renders a thrown tool-call error, with a targeted
  "Unknown Tool" hint for `-32602`.

Adds unit + Storybook + integration coverage (a real modern server with an
invalid-header tool fixture) for every path. `npm run ci`-equivalent checks
green.

Closes #1632

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5
@cliffhall cliffhall added the v2 Issues and PRs for v2 label Jul 21, 2026
cliffhall and others added 2 commits July 22, 2026 09:24
Register the `invalid_header_tool` fixture as a preset and add
`xmcpheader-modern-http.json` — a modern HTTP showcase serving echo, a
valid-header `get_weather`, and the invalid-header tool — so the Tools-tab
x-mcp-header tooling (mirrored headers, excluded-tools surface, -32602
rendering) can be exercised manually. Documented in the README alongside the
other per-SEP showcase configs. Smoke-tested end to end in the web client.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5
End-to-end web-client verification of the x-mcp-header Tools tooling against
the modern showcase server: excluded-tools surface + reason, mirrored-headers
section, and the -32602 unknown-tool error panel.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5
@cliffhall

Copy link
Copy Markdown
Member Author

@claude review

@claude

claude Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Claude finished @cliffhall's task in 2m 51s —— View job


Code review — PR #1735 (x-mcp-header tooling + -32602 rendering)

  • Read core scan module (xMcpHeader.ts)
  • Read InspectorClient excluded-tools wiring
  • Read web components (ToolControls, ToolDetailPanel, ToolCallErrorPanel, ToolsScreen)
  • Review tests + integration coverage
  • Post review

Solid work. The scan module is a careful, well-documented port; the exclusion is correctly gated to the modern non-stdio era; the integration suite drives a real server (including a page-size-1 pagination walk that genuinely proves the raw tools/list is unfiltered while the SDK aggregate drops the tool). Nothing blocking. A few things worth a look:

1. -32602 → "Unknown Tool" is a heuristic that can mislabel (minor, UX)

isUnknownToolError maps any -32602 (ProtocolErrorCode.InvalidParams) to the "Unknown Tool" title (toolResultUtils.ts:12, ToolCallErrorPanel.tsx). But -32602 is the generic Invalid params code — a server that rejects a known tool call with bad/missing arguments (server-side schema validation) throws the same code, and would then render under the definitive title "Unknown Tool". The body copy is nicely hedged ("It may have been excluded… or removed"), but the heading isn't. Since you can't disambiguate from the code alone, consider a softer title (e.g. "Invalid Parameters") with the unknown-tool explanation as the hedged hint, or match on the error message/data if the SDK includes the tool name. Not a blocker — just flagging the conflation.

2. Every modern-era tools refresh now does a second full pagination walk (minor, efficiency)

listAllTools calls the SDK's cache-aware client.listTools(), then unconditionally calls refreshExcludedTools() (inspectorClient.ts:2579), which walks the raw tools/list page-by-page over the wire. Two things fall out:

  • On modern non-stdio it roughly doubles the list round-trips per refresh.
  • When the aggregate is served from cache (cacheMode: 'use', no wire hit), the raw walk still hits the wire — so it partially defeats the cache it sits next to.

For a debugging tool this is probably an acceptable trade, and the comment already frames it as best-effort — but it's worth a note in the code that the recompute is deliberately un-cached, or gating it so a cache-served aggregate doesn't trigger a full uncached re-walk.

3. Doc comment mentions $ref handling the code doesn't do (nit)

scanXMcpHeaderDeclarations's doc (xMcpHeader.ts:119-124) says the walk visits "$ref targets", but $ref isn't in NON_REACHABLE_SUBSCHEMA_KEYWORDS and is never resolved — the code only visits $defs/definitions (marking anything there unreachable). The behavior is fine (an x-mcp-header reachable only through a $ref is correctly treated as non-statically-reachable), but the comment overstates what happens. Suggest dropping the "$ref targets" clause or rewording to "$ref/$defs bodies (refs are never followed)".

4. Excluded-list search filters on name only (nit)

The main tools filter matches name and title (ToolControls.tsx), but filteredExcluded matches tool.name only. Harmless, but a titled excluded tool won't surface when searching by its title. Worth aligning for consistency.

5. Rejection-type message lists three types, permits four (cosmetic)

PERMITTED_X_MCP_HEADER_TYPES includes number (the documented conformance carve-out), but the rejection reason at xMcpHeader.ts:153 says "primitive-typed properties (string, integer, boolean)". Since number is accepted, the message is only shown for genuinely-rejected types, so this is purely cosmetic — mentioning for completeness.

None of these are merge-blockers; 1 and 2 are the ones I'd weigh most.
· v2/1632-x-mcp-header-tooling

Addresses the review on PR #1735:

1. -32602 "Unknown Tool" mislabel: `-32602` is generic Invalid params, so a
   known tool called with bad arguments hit the same code and rendered under
   the definitive "Unknown Tool" heading. Replace `isUnknownToolError` with
   `classifyToolCallError(code, message)` → "unknown-tool" only when the
   message names it (not found / unknown tool / …), else "invalid-params"
   (new "Invalid Parameters" heading + schema hint), else "generic".

2. Double pagination walk: expand the `listAllTools` comment to document that
   the excluded-tools recompute is a deliberately un-cached second raw walk
   (doubles list round-trips on modern non-stdio, runs even on a cache-served
   aggregate) — accepted for a debugging tool, no-op on legacy/stdio.

3. Doc nit: `scanXMcpHeaderDeclarations` no longer claims to visit "$ref
   targets" — reworded to note `$ref` is never followed (refs resolve into the
   unreachable `$defs` body).

4. Search nit: the excluded-list filter now matches name AND title, matching
   the main tools filter.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5
@cliffhall

Copy link
Copy Markdown
Member Author

Thanks for the review! Addressed in 679e776:

  1. -32602 → "Unknown Tool" mislabel — good catch. -32602 is the generic Invalid params code, so a known tool called with bad arguments hit the same path and got the definitive "Unknown Tool" heading. Replaced isUnknownToolError(code) with classifyToolCallError(code, message): it returns "unknown-tool" only when the message actually names it (not found / unknown tool / no such tool / …), else "invalid-params" (a new "Invalid Parameters" heading + a "check the argument values against the tool's schema" hint), else "generic". The unknown-tool copy stays hedged. Added tests for all three kinds.

  2. Double pagination walk / cache defeat — kept the behavior (a debugging tool wants the current wire truth for "why did this tool vanish", and it's a no-op on legacy/stdio), but expanded the listAllTools comment to state explicitly that the recompute is a deliberately un-cached second raw walk that roughly doubles list round-trips on modern non-stdio and runs even when the aggregate was cache-served — so the trade-off is documented at the call site rather than implicit.

  3. $ref doc overstates — reworded scanXMcpHeaderDeclarations's doc: it no longer claims to visit "$ref targets"; it now notes $ref is never followed (a property reachable only via $ref is correctly non-statically-reachable, its annotation living in the unreachable $defs body). Behavior unchanged.

  4. Excluded-list search filters name only — aligned: filteredExcluded now matches name and title, like the main tools filter.

  5. Rejection message lists 3 types, permits 4 — left as-is intentionally: the string is a verbatim port of the SDK's own message, and since it's only ever shown for a genuinely-rejected type the number omission is cosmetic. Keeping parity with the SDK's wording so the two agree.

validate + per-file ≥90 coverage + Storybook all green locally.

@cliffhall

Copy link
Copy Markdown
Member Author

@claude review

@claude

claude Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Claude finished @cliffhall's task in 1m 41s —— View job


Code review — PR #1735 (re-review after 679e776)

  • Read the diff vs origin/v2/main
  • Verify the 5 prior review items were addressed
  • Review the new classifyToolCallError logic + tests
  • Check for any new issues
  • Post review

The five items from the prior review are all handled well. Quick verification pass:

# Prior item Resolution
1 -32602 → "Unknown Tool" mislabel Replaced with classifyToolCallError(code, message)unknown-tool / invalid-params / generic, with a new "Invalid Parameters" heading. Message-gated, hedged copy, three-way tests.
2 Double pagination walk / cache defeat Behavior kept (reasonable for a debugging tool), but listAllTools now documents at the call site that the recompute is a deliberately un-cached second raw walk (inspectorClient.ts:2575-2586).
3 $ref doc overstates Reworded (xMcpHeader.ts:113-126) — now states $ref is never followed. Behavior unchanged.
4 Excluded-list search filters name only filteredExcluded now matches name and title (ToolControls.tsx:85-90), aligned with the main filter.
5 Rejection message lists 3 types, permits 4 Left as a verbatim SDK-message port (only shown for genuinely-rejected types) — reasonable, deliberate parity.

Also spot-checked: the SDK bump to 2.0.0-beta.5 is consistent across all four package.jsons + root; errorCodeOf/errorMessage duck-type the thrown error into ToolCallState.errorCode, wiring App.tsx:2946-2949ToolsScreenToolCallErrorPanel cleanly; the raw-vs-aggregate pagination integration test genuinely proves the exclusion path.

One small residual on the -32602 heuristic (nit, non-blocking)

The disambiguation is a clear improvement, but note it's now symmetric — the same best-effort matching that avoids mislabelling bad-args as "Unknown Tool" can inversely mislabel a genuine invalid-params error whose message happens to contain one of the marker phrases. E.g. a schema-validation message like "property 'region' does not exist in enum" matches does not exist and would render "Unknown Tool". The heading is cosmetic and the body copy is hedged either way, so this is purely a label nicety — but if the SDK surfaces the tool name in error.data for the true unknown-tool case, matching on that (falling back to the message) would remove the ambiguity entirely. Fine to leave as-is for a debugging tool.

Nothing blocking — the response addressed the feedback faithfully and the new logic is well covered. LGTM. ✅
· branch v2/1632-x-mcp-header-tooling

The re-review noted the -32602 disambiguation was symmetric: broad markers
("does not exist", "unrecognized") could inversely mislabel a genuine
invalid-params message (e.g. "property 'region' does not exist") as "Unknown
Tool". Scope the not-found/does-not-exist family to require the word "tool" in
the same clause (matching the SDK's "Tool <name> not found"); keep "unknown
tool" / "no such tool" as standalone unambiguous markers. Adds a
tool-less-"does not exist" test asserting it classifies as invalid-params.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5
@cliffhall

Copy link
Copy Markdown
Member Author

Thanks — and good call on the symmetric-mislabel residual. Addressed in 95195e4: the unknown-tool matcher is now tool-scoped. The broad does not exist / unrecognized / not found family only counts when the word tool is in the same clause (matching the SDK's Tool <name> not found), while unknown tool / no such tool stay as standalone unambiguous markers. So your example "property 'region' does not exist in enum" now classifies as invalid-params, not unknown-tool — added a test asserting exactly that. (A tool name in error.data would be the fully-unambiguous signal if the SDK ever surfaces it there; noted in the code comment.)

validate + coverage + Storybook green.

@cliffhall

Copy link
Copy Markdown
Member Author

@claude review

@claude

claude Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Claude finished @cliffhall's task in 1m 47s —— View job


Code review — PR #1735 (re-review after 95195e45)

  • Read core scan module + InspectorClient wiring
  • Read web components (ToolControls, ToolDetailPanel, ToolCallErrorPanel, toolResultUtils, ToolsScreen)
  • Verify the tool-scoped classifyToolCallError change (95195e4)
  • Check tests + integration coverage
  • Post review

Re-reviewed the current tree against origin/v2/main. The tool-scoping change is well done and the earlier five items all hold. Verification:

Tool-scoped matcher (toolResultUtils.ts:24-42). The regex now requires the word tool in the same clause ([^.!?]*, terminator-bounded) for the broad not found / does not exist / unrecognized family, with unknown tool / no such tool as standalone markers. Two nice details fall out for free:

  • \btool\b won't match tool_name (underscore is a \w, so no boundary) — schema messages naming a tool_name property won't trip it.
  • The clause bound stops a match from bleeding across .!? boundaries.

The ToolCallErrorPanel.test.tsx cases pin exactly the right behavior, including the regression case "property 'region' does not exist in enum"invalid-params (:69-73) and -32601generic (:75).

Wiring spot-checks:

  • errorCodeOf (App.tsx:470-476) duck-types the numeric code off the thrown error and feeds ToolCallState.errorCodeToolsScreen (:228-240) → ToolCallErrorPanel. Cleanly decoupled from the SDK error class, consistent with formatErrorDetails.
  • The URL-elicitation-with-no-list branch (App.tsx:2933) deliberately omits errorCode — correct, that path renders a toast, not the error panel.
  • refreshExcludedTools (inspectorClient.ts:2614-2640) is correctly gated to modern non-stdio, cursor-loop guarded, and best-effort at the listAllTools call site with the un-cached-second-walk trade-off now documented (:2575-2586).
  • ToolControls excluded-list search matches name and title (:85-90); ToolDetailPanel's mirrored-headers section reads well and keeps the browser-omits-Mcp-Param-* note.

One residual, already known (nit, non-blocking)

The tool-scoping removes the realistic inverse-mislabel. The only remaining corner is a genuine invalid-params error whose message names an argument literally called tool — e.g. "property 'tool' does not exist" would still classify as unknown-tool. It's contrived, the heading is cosmetic, and the body copy is hedged; the code comment already points at the fully-unambiguous fix (a tool name in error.data) for if/when the SDK surfaces it. Fine to leave.

Nothing blocking — the change addressed the last nit faithfully and is well covered. LGTM. ✅
· branch v2/1632-x-mcp-header-tooling

@cliffhall

Copy link
Copy Markdown
Member Author

Thanks for the thorough re-reviews! Agreed the remaining corner (an argument literally named tool) is contrived and cosmetic — leaving it as-is, with the error.data tool-name path noted in the code comment as the unambiguous fix if the SDK ever surfaces it there. No further changes; thanks for the LGTM. ✅

…hot (#1632)

Adds a `trigger_invalid_params` spec-error trigger to the modern showcase
server: the injector answers its `tools/call` with a real `-32602 Invalid
params` JSON-RPC error whose message does NOT name an unknown tool. This lets
the "Invalid Parameters" branch of the tool-call error panel (added in the
review response) be exercised live, distinct from the unknown-tool `-32602`.

- `SPEC_ERROR_TRIGGERS` gains the `trigger_invalid_params` entry (HTTP 200,
  in-band -32602) + preset registration.
- Enable `injectSpecErrors` on `xmcpheader-modern-http.json` and add the tool.
- README + pr-screenshots README document it; adds the live "Invalid
  Parameters" proof screenshot.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5
@cliffhall

Copy link
Copy Markdown
Member Author

@claude review

@claude

claude Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Claude finished @cliffhall's task in 1m 10s —— View job


Code review — PR #1735 (x-mcp-header tooling + -32602 rendering)

  • Read core scan module (xMcpHeader.ts)
  • Read InspectorClient excluded-tools wiring
  • Read web components (ToolControls, ToolDetailPanel, ToolCallErrorPanel, ToolsScreen, toolResultUtils)
  • Review newest commit (trigger_invalid_params showcase)
  • Review tests + integration coverage
  • Post review

Re-reviewed the full tree against origin/v2/main, including the newest commit d2da4908. All five items from the first review and both follow-up nits are resolved, and the new work is clean. LGTM. ✅

Newest commit (trigger_invalid_params) — verified.

  • SPEC_ERROR_TRIGGERS.trigger_invalid_params returns HTTP 200 with in-band -32602 and a message that deliberately does not name a tool ("Invalid params: 'code' must be a positive integer"), so it exercises the invalid-params branch live — the exact complement to the unknown-tool -32602. Message is well-chosen: it won't trip the tool-scoped matcher (toolResultUtils.ts:24-25). Good end-to-end proof for the disambiguation.
  • Preset + config wiring (injectSpecErrors: true, tool added) and README/pr-screenshots docs are consistent.

Spot-checks that still hold:

  • scanXMcpHeaderDeclarations (xMcpHeader.ts) — the static-reachability walk visits every non-properties keyword with reachable: false and rejects an annotation found there; $ref correctly never followed; case-insensitive uniqueness, RFC 9110 token, and the number carve-out (with the SDK-parity comment) all faithful. The reworded $ref doc (:124-126) matches behavior.
  • classifyToolCallError (toolResultUtils.ts:35-42) — tool-scoped regex (\btool\b[^.!?]*…) resists both the forward and inverse mislabel; error.data tool-name note left for the unambiguous future fix.
  • refreshExcludedTools (inspectorClient.ts:2604-2640) — gated to modern non-stdio, cursor-repeat guarded, best-effort at the listAllTools call site, and the un-cached-second-walk trade-off is documented (:2572-2585). excludedTools is cleared on disconnect (:1670) and re-emitted (:1685).
  • ToolControls excluded-list filter matches name and title (:85-91), aligned with the main filter.
  • ToolCallErrorPanel heading map + hedged hints read well; the h3/size="h4" heading-order note is a nice touch.

Residual (known, non-blocking): the only remaining classification corner is a genuine invalid-params message that literally contains the word tool in-clause (e.g. an argument named tool) — contrived, cosmetic, and already noted in the code as resolvable via error.data. Fine to leave.

Nothing blocking. Well-documented, well-tested (pure scan unit tests, component tests for all three error kinds, and an integration suite driving a real modern server through the raw-vs-aggregate pagination path). ✅

· branch v2/1632-x-mcp-header-tooling

@cliffhall

Copy link
Copy Markdown
Member Author

Thanks for the final pass — glad the trigger_invalid_params live proof and the disambiguation hold up. Agreed the remaining tool-named-argument corner is contrived/cosmetic and stays as-is (with the error.data note in code). No further changes. 🙏

@cliffhall
cliffhall merged commit e70a113 into v2/main Jul 22, 2026
3 checks passed
@cliffhall
cliffhall deleted the v2/1632-x-mcp-header-tooling branch July 22, 2026 15:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

v2 Issues and PRs for v2

1 participant