Skip to content

feat(mcp): support protocol revision 2026-07-28 on SDK v2 - #508

Open
anxkhn wants to merge 7 commits into
getsentry:mainfrom
anxkhn:feat/mcp-2026-07-28
Open

feat(mcp): support protocol revision 2026-07-28 on SDK v2#508
anxkhn wants to merge 7 commits into
getsentry:mainfrom
anxkhn:feat/mcp-2026-07-28

Conversation

@anxkhn

@anxkhn anxkhn commented Aug 6, 2026

Copy link
Copy Markdown

Summary

This migrates XcodeBuildMCP to the split MCP TypeScript SDK v2 packages and adds dual-era support for the July 28, 2026 MCP specification.

Modern stdio connections now use stateless per-request metadata, server/discover, required result/cache fields, and subscriptions/listen. Legacy initialization-based clients continue to work through the same serveStdio entry. Application session state remains process-scoped rather than protocol-session-scoped.

The change also:

  • makes server registration and teardown safe across connection-scoped and per-request HTTP instances
  • preserves Xcode IDE bridge ownership when HTTP requests create temporary servers
  • updates Sentry context from modern request envelopes
  • keeps idle shutdown correct for long-lived subscriptions and cancellation
  • refreshes MCP contract fixtures for the SDK v2 JSON Schema 2020-12 wire shape

Validation

  • npm run typecheck
  • npm run lint
  • npm run format:check
  • npm run build
  • npx vitest run (2,656 tests)
  • npm run test:schema-fixtures (333 tests)
  • modern and legacy stdio idle-timeout and wire-level smoke coverage

Device-dependent snapshot suites were not run locally.

anxkhn and others added 3 commits August 6, 2026 15:58
Adopt the official TypeScript SDK v2 packages (@modelcontextprotocol/server,
@modelcontextprotocol/client) and serve MCP protocol revision 2026-07-28
alongside the 2025 revisions on the same stdio connection.

Modern era:
- No initialize handshake and no Mcp-Session-Id. The protocol revision and the
  client's declared capabilities arrive in the _meta envelope of every request.
- server/discover advertises the supported modern revisions, capabilities and
  instructions.
- Results carry resultType, and cacheable operations carry ttlMs/cacheScope
  via server-level cache hints (private scope, since every result depends on
  local workspace and Xcode state).
- Direct requests are served without prior discovery.
- Per-request io.modelcontextprotocol/logLevel replaces logging/setLevel.
- Notifications are delivered over subscriptions/listen.

Legacy era is unchanged: an initialize opening pins the connection to the 2025
handshake, keeps logging/setLevel, and never sees modern-only result members.

Serving contexts now build a fresh server instance per connection (and per HTTP
request for the fetch-shaped handler, which the SDK entry uses to enforce the
MCP-Protocol-Version / Mcp-Method / Mcp-Name header contract). The expensive
manifest, tool-module and resource-module resolution stays process level and is
replayed cheaply onto each instance; application session state (session
defaults, debugger sessions, log captures) is process scoped and survives
instance replacement, including the discarded server/discover probe instance.

Sentry MCP instrumentation still wraps every instance - the SDK v2 McpServer
passes its structural check. Because the released integration derives protocol
and client identity from the initialize handshake, modern-era identity is now
published from the validated _meta envelope so no observability is lost.

Adds lifecycle, modern discovery/direct, legacy, notification, protocol-helper
and instrumentation test coverage.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
`npm run test:schema-fixtures` failed after the SDK v2 migration. Regenerated
with `npm run test:schema-fixtures:update`; the whole 164-file diff is exactly
two uniform, intentional changes and nothing else:

- inputSchema `$schema` moves from draft-07 to
  `https://json-schema.org/draft/2020-12/schema`, which is the draft the v2
  Zod-to-JSON-Schema conversion targets.
- the always-constant `execution: {"taskSupport":"forbidden"}` member is no
  longer emitted, because v2 only publishes `execution` when a tool explicitly
  configures it.

Both are client-visible, so they are called out in the changelog.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Two serving-layer leaks found in review of the 2026-07-28 work.

Idle shutdown never fired once a modern client opened a subscription. The
serving entry answers `subscriptions/listen` out of band - an acknowledgement
notification now, the JSON-RPC result only at teardown - and a cancelled
request receives no response at all, because the SDK drops the reply for an
aborted handler. The transport observer counted both as ordinary in-flight
work, so the count never returned to zero and the process stayed alive forever.
Long-lived requests are now tracked separately and never counted as in-flight,
and `notifications/cancelled` settles whichever kind of request it names.
Ordinary request start/complete metrics are unchanged.

HTTP serving contexts leaked a server instance and its tool registrations on
every request. Teardown was hooked on `McpServer.close`, but `createMcpHandler`
closes the low-level `server.server` per request and never touches the
high-level wrapper, so `activeServers` and `registrationsByServer` grew without
bound. Teardown is now wired to the low-level close lifecycle as well, and is
idempotent so an unconnected instance closed through the high-level API is
still released.

Two consequences of the per-request model are fixed with it:
- the process-level Xcode tools bridge is bound only by connection-scoped
  contexts, so an HTTP request can no longer move the proxied tools off the
  live stdio connection;
- server instances carry their serving scope, so `getServer()` prefers the
  connection-scoped instance and a per-request instance can never shadow it.

Adds a modern stdio e2e regression driving listen plus cancellation under
XCODEBUILDMCP_MCP_IDLE_TIMEOUT_MS (verified failing before the fix), observer
unit coverage for every settle path, HTTP sequential/concurrent/handler.close
teardown assertions, and bridge-binding coverage.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings August 6, 2026 11:35

Copilot AI 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.

Pull request overview

Migrates XcodeBuildMCP to the split MCP TypeScript SDK v2 packages and adds dual-era support for the MCP 2026-07-28 protocol revision (modern stateless _meta envelopes + headers) while keeping 2025-era legacy initialize clients working on the same serving entrypoints.

Changes:

  • Introduces modern-era protocol helpers + request-lifecycle accounting for cancellations and long-lived subscriptions/listen so idle shutdown doesn’t hang.
  • Refactors serving state to support multiple active SDK v2 server instances (connection-scoped stdio + request-scoped HTTP) and updates shutdown/lifecycle plumbing accordingly.
  • Refreshes MCP contract fixtures and harness/test imports for SDK v2 + JSON Schema 2020-12 tool schema shape.

Reviewed changes

Copilot reviewed 218 out of 219 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/utils/sentry.ts Add protocol-era/client identity tags + serving-context metric helpers.
src/utils/tests/tool-registry.test.ts Update server-state mock to new getActiveServers() API.
src/snapshot-tests/resource-harness.ts Switch snapshot harness to SDK v2 client imports.
src/snapshot-tests/mcp-tool-contract-fixtures.ts Update Tool type import for SDK v2.
src/snapshot-tests/mcp-harness.ts Update v2 client imports and callTool signature usage.
src/snapshot-tests/json-schema-validation.ts Update Tool type import for SDK v2.
src/snapshot-tests/tests/json-fixture-schema.test.ts Update Tool type import for SDK v2.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/xcode_tools_bridge_sync.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/xcode_tools_bridge_status.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/xcode_tools_bridge_disconnect.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/xcode_ide_list_tools.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/xcode_ide_call_tool.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/wait_for_ui.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/type_text.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/touch.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/toggle_software_keyboard.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/toggle_connect_hardware_keyboard.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/test_sim.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/test_macos.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/test_device.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/tap.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/sync_xcode_defaults.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/swipe.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/swift_package_test.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/swift_package_stop.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/swift_package_run.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/swift_package_list.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/swift_package_clean.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/swift_package_build.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/stop_mac_app.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/stop_app_sim.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/stop_app_device.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/snapshot_ui.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/sim_statusbar.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/show_build_settings.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/set_sim_location.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/set_sim_appearance.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/session_use_defaults_profile.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/session_show_defaults.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/session_set_defaults.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/session_clear_defaults.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/screenshot.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/scaffold_macos_project.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/scaffold_ios_project.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/reset_sim_location.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/record_sim_video.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/open_sim.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/manage-workflows.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/long_press.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/list_sims.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/list_schemes.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/list_devices.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/launch_mac_app.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/launch_app_sim.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/launch_app_device.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/key_sequence.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/key_press.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/install_app_sim.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/install_app_device.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/get_sim_app_path.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/get_mac_bundle_id.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/get_mac_app_path.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/get_file_coverage.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/get_device_app_path.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/get_coverage_report.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/get_app_bundle_id.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/gesture.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/erase_sims.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/drag.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/doctor.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/discover_projs.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/debug_variables.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/debug_stack.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/debug_lldb_command.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/debug_detach.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/debug_continue.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/debug_breakpoint_remove.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/debug_breakpoint_add.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/debug_attach_sim.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/clean.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/button.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/build_sim.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/build_run_sim.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/build_run_macos.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/build_run_device.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/build_macos.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/build_device.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/boot_sim.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-enabled/batch.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/xcode_tools_bridge_sync.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/xcode_tools_bridge_status.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/xcode_tools_bridge_disconnect.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/xcode_ide_list_tools.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/xcode_ide_call_tool.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/wait_for_ui.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/type_text.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/touch.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/toggle_software_keyboard.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/toggle_connect_hardware_keyboard.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/test_sim.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/test_macos.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/test_device.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/tap.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/sync_xcode_defaults.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/swipe.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/swift_package_test.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/swift_package_stop.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/swift_package_run.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/swift_package_list.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/swift_package_clean.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/swift_package_build.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/stop_mac_app.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/stop_app_sim.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/stop_app_device.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/snapshot_ui.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/sim_statusbar.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/show_build_settings.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/set_sim_location.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/set_sim_appearance.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/session_use_defaults_profile.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/session_show_defaults.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/session_set_defaults.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/session_clear_defaults.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/screenshot.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/scaffold_macos_project.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/scaffold_ios_project.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/reset_sim_location.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/record_sim_video.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/open_sim.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/manage-workflows.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/long_press.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/list_sims.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/list_schemes.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/list_devices.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/launch_mac_app.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/launch_app_sim.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/launch_app_device.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/key_sequence.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/key_press.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/install_app_sim.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/install_app_device.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/get_sim_app_path.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/get_mac_bundle_id.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/get_mac_app_path.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/get_file_coverage.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/get_device_app_path.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/get_coverage_report.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/get_app_bundle_id.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/gesture.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/erase_sims.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/drag.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/doctor.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/discover_projs.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/debug_variables.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/debug_stack.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/debug_lldb_command.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/debug_detach.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/debug_continue.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/debug_breakpoint_remove.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/debug_breakpoint_add.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/debug_attach_sim.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/clean.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/button.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/build_sim.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/build_run_sim.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/build_run_macos.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/build_run_device.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/build_macos.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/build_device.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/boot_sim.json Fixture: JSON Schema 2020-12 + drop execution.
src/snapshot-tests/fixtures/mcp-contracts/session-defaults-disabled/batch.json Fixture: JSON Schema 2020-12 + drop execution.
src/smoke-tests/mcp-test-harness.ts Switch smoke harness to SDK v2 client imports.
src/smoke-tests/tests/e2e-mcp-idle-timeout.test.ts Switch smoke test to SDK v2 client imports.
src/server/start-mcp-server.ts Use stdio serving handle + runtime bootstrap split.
src/server/server-state.ts Track multiple active servers + registration/unregistration API.
src/server/request-lifecycle.ts Track cancellation + long-lived listen requests; observe modern envelopes.
src/server/mcp-shutdown.ts Close via serving-handle abstraction (not raw server instance).
src/server/mcp-protocol.ts New: modern protocol constants, header helpers, envelope reader, cache hints.
src/server/mcp-lifecycle.ts Store serving handle in lifecycle coordinator (instead of server).
src/server/mcp-instrumentation.ts New: publish modern _meta identity to Sentry + serving-context metrics.
src/server/bootstrap.ts Split process bootstrap from per-serving-context registration replay.
src/server/tests/serving-test-fixtures.ts New: minimal tool/resource plan for serving-layer tests.
src/server/tests/server.test.ts Update transport/types imports; add lifecycle accounting tests.
src/server/tests/raw-mcp-peer.ts New: raw JSON-RPC peer to test modern wire behavior.
src/server/tests/mcp-xcode-bridge-binding.test.ts New: ensure bridge binding stays connection-scoped.
src/server/tests/mcp-shutdown.test.ts Update shutdown tests to serving-handle close.
src/server/tests/mcp-protocol.test.ts New: unit tests for header/envelope/cache-hint helpers.
src/server/tests/mcp-notifications.test.ts New: validate list-changed notifications across eras.
src/server/tests/mcp-lifecycle.test.ts Update assertions for serving handle lifecycle.
src/server/tests/mcp-legacy-protocol.test.ts New: legacy-era handshake and compatibility assertions.
src/server/tests/mcp-instrumentation.test.ts New: Sentry integration compatibility + modern identity publication.
src/runtime/types.ts Update MCP ToolAnnotations type import for SDK v2.
src/integrations/xcode-tools-bridge/tool-service.ts Update MCP types import for SDK v2.
src/integrations/xcode-tools-bridge/registry.ts Support rebinding proxied tools onto new server instances.
src/integrations/xcode-tools-bridge/manager.ts Add bindServer + rebind handling for per-serving server instances.
src/integrations/xcode-tools-bridge/index.ts Update MCP server type import for SDK v2.
src/integrations/xcode-tools-bridge/core.ts Update Tool type import for SDK v2.
src/integrations/xcode-tools-bridge/client.ts Switch to SDK v2 client + schema helpers.
src/integrations/xcode-tools-bridge/bridge-tool-result.ts Update CallToolResult type import for SDK v2.
src/integrations/xcode-tools-bridge/bridge-response-artifact.ts Update CallToolResult type import for SDK v2.
src/integrations/xcode-tools-bridge/tests/registry.integration.test.ts Update server/client imports for SDK v2.
src/integrations/xcode-tools-bridge/tests/manager.test.ts Update MCP types import for SDK v2.
src/integrations/xcode-tools-bridge/tests/fixtures/fake-xcode-tools-server.mjs Update stdio transport/server imports for SDK v2.
src/daemon/protocol.ts Update ToolAnnotations type import for SDK v2.
src/daemon/daemon-server.ts Update CallToolResult type import for SDK v2.
src/core/resources.ts Split load vs register; attach cache hints for resources.
src/core/tests/resources.test.ts Update mocked server API to registerResource + cacheHint.
src/cli/cli-tool-catalog.ts Update ToolAnnotations type import for SDK v2.
scripts/probe-xcode-mcpbridge.ts Update script to SDK v2 client + schema helper.
package.json Replace @modelcontextprotocol/sdk with v2 client/server packages.
CHANGELOG.md Document protocol support + SDK v2 migration + contract/fixture changes.
AGENTS.md Update architecture description to reflect SDK v2 dual-era serving.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/server/bootstrap.ts Outdated
Comment thread src/server/request-lifecycle.ts
…dle on listen

Both findings from the PR getsentry#508 review reproduce.

`ServerRegistrations.toolPlan` captured `getToolRegistrationPlan()` once during
bootstrap, and every serving context replayed that snapshot. `manage_workflows`
rewrites the process-level plan at runtime and applies it to the servers that
are already active, so the live context was correct, but any context built
afterwards re-registered the boot-time workflows and silently undid the
client's selection. It is now a resolver (`resolveToolPlan`) read at apply
time, so each context registers the plan that is current when it is built. The
Xcode tools bridge binding is untouched: per-request contexts still never take
it, so this adds no rebind churn.

Opening a `subscriptions/listen` stream was excluded from the lifecycle
callbacks entirely. That kept it out of the in-flight count, which is required,
but it also meant the interaction never refreshed the idle window, so a
subscription opened near the deadline could be killed by the very next idle
check. Long-lived requests now report activity exactly once on open through a
new `onRequestActivity` observer hook and `McpIdleShutdownController.markActivity`,
which moves the idle deadline without touching the in-flight count. Idle
shutdown still runs once the client goes quiet.

Regressions, all verified failing before the fix:
- new HTTP context and stdio probe-to-pinned replacement after a plan change;
- an idle-timeout e2e where the client only ever opens listen streams - it
  previously died mid-loop with "opening a subscription did not refresh the
  idle window";
- controller timing tests pinning the restarted window, the unchanged original
  deadline, and that activity cannot mask in-flight work;
- observer tests for once-per-stream activity and no activity for ordinary
  requests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings August 6, 2026 12:18
Comment thread src/server/bootstrap.ts

Copilot AI 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.

Pull request overview

Copilot reviewed 221 out of 222 changed files in this pull request and generated no new comments.

The previous commit made tool registration follow the live workflow selection,
but the Xcode tools bridge was still gated on `xcodeIdeEnabled`, a boolean
captured during bootstrap. The two sources disagreed as soon as
`manage_workflows` ran, in both directions:

- xcode-ide enabled after startup: a later context registered the xcode-ide
  manifest tools from the current plan but never constructed, bound or enabled
  the bridge manager, so the proxied `xcode_tools_*` tools were missing;
- xcode-ide disabled after startup: a later context still bound and re-enabled
  the bridge, undoing the client's disable.

The boot-time flag is removed rather than refreshed, so there is no stale value
left to read: `ServerRegistrations` now exposes only `resolveToolPlan`, and
both the tool registrations and the bridge decision are derived from that one
plan via `planEnablesXcodeIde`.

Disable is now a real teardown. `setWorkflowEnabled(false)` drops the proxied
registrations on the transition, so a context built after a disable cannot keep
serving tools the client turned off, and a later re-enable syncs fresh instead
of replaying a stale catalogue through `rebind`. A disabled context only ever
peeks at the manager, so it never constructs one just to switch it off.

HTTP ownership is unchanged and now explicit in both directions: a per-request
context neither takes the binding nor disables a bridge the live stdio
connection still owns.

Regressions (enable-after-start and disable-after-start both verified failing
before the fix): serving-context gating for stdio and HTTP, no-manager-creation
on the disabled path, and manager-level coverage that disabling clears the
proxied registrations, is idempotent, and stops listChanged-driven resyncs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings August 6, 2026 12:35

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 92b8abd. Configure here.

Comment thread src/integrations/xcode-tools-bridge/manager.ts Outdated
Comment thread src/integrations/xcode-tools-bridge/manager.ts

Copilot AI 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.

Pull request overview

Copilot reviewed 222 out of 223 changed files in this pull request and generated no new comments.

Suppressed comments (1)

src/utils/sentry.ts:680

  • clientCapabilities is optional but an empty array is truthy, so this currently records clientCapabilities: '' (empty string) into the Sentry context. That makes "no capabilities" indistinguishable from "capabilities missing" and adds noise to observability payloads. Consider only emitting the field when the array is non-empty.

…entry capabilities

Three follow-ups from the review of 92b8abd.

Re-enabling the workflow left `suppressListChangedSync` set by the preceding
disable, so `onToolCatalogInvalidated` returned early forever and the proxied
catalogue never refreshed again unless something happened to run a manual sync.
Enabling now clears the suppression, and `setWorkflowEnabled` short-circuits
when the state is unchanged so repeated calls cannot double-clear the registry.

A disable racing an in-flight sync could resurrect the tools it had just torn
down: `syncTools` awaits bridge availability and then the remote tool list, and
nothing revalidated the workflow after either await, so a sync started before
the disable still reached `registry.sync`. Every transition - enable, disable
and manual disconnect - now advances a workflow epoch, the sync captures it and
revalidates after each await, and an in-flight sync is only shared with callers
from the same epoch. A stale completion returns a zeroed result and touches
neither the registry nor the outbound notification.

`clientCapabilities` is an array, and an empty array is truthy, so the Sentry
`mcp.protocol` context published `clientCapabilities: ''` for every client that
declares none - which is the common case on the modern era. The payload is now
built by a pure `buildMcpProtocolContextPayload` that omits the member unless
it has entries, which also makes the emission unit-testable given the surrounding
function is a no-op under test.

All seven new regressions verified failing before the fix: re-enable restores
invalidation-driven syncing, repeated transitions keep working, disable during a
delayed sync leaves zero tools registered (both awaits covered), a manual
disconnect invalidates the same way, a later re-enable syncs the fresh catalogue,
and empty capabilities are omitted.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings August 6, 2026 12:49
@anxkhn

anxkhn commented Aug 6, 2026

Copy link
Copy Markdown
Author

Also addressed Copilot’s suppressed Sentry observation in 63c7bed: empty client capability arrays are now omitted rather than recorded as an empty string. Added payload-level coverage. Validation: 2,683 tests, typecheck, lint, format, build, 333 schema fixtures, idle e2e, and targeted bridge/Sentry tests.

Copilot AI 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.

Pull request overview

Copilot reviewed 223 out of 224 changed files in this pull request and generated no new comments.

Suppressed comments (1)

src/server/tests/serving-test-fixtures.ts:30

  • ctx.structuredOutput.schema/schemaVersion in this fixture don’t follow the structured output contract used elsewhere in the repo (e.g. snapshot fixtures use schema: "xcodebuildmcp.output.bundle-id" with integer-string versions like "1"/"2"). Keeping schema: 'bundle-id' and schemaVersion: '1.0.0' makes these serving-layer tests less representative and can mask regressions around schema naming/versioning.

…xtures

The serving-layer fixture emitted `schema: 'bundle-id'` with
`schemaVersion: '1.0.0'`. Neither matches the repo contract: real tools emit the
fully-qualified identifier and an integer-string version - for this kind,
`setBundleIdStructuredOutput` emits `xcodebuildmcp.output.bundle-id` at version
`2` - and every snapshot fixture agrees.

That mattered because these tests assert the MCP `structuredContent` envelope
end to end through a fresh SDK v2 server, so they were attesting to an envelope
the published contract rejects. The `2.schema.json` pins both members with
`const`, and validating the two envelopes against it confirms it:

  before -> INVALID: /schema must be equal to constant,
                     /schemaVersion must be equal to constant
  after  -> VALID

The fixture now exports `PROBE_SCHEMA` / `PROBE_SCHEMA_VERSION` sourced from the
same `BUNDLE_ID_STRUCTURED_OUTPUT_SCHEMA` constant production uses, so the two
cannot drift again, and the one assertion that hard-coded the identifier now
checks the version too.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings August 6, 2026 13:15

Copilot AI 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.

Pull request overview

Copilot reviewed 223 out of 224 changed files in this pull request and generated 1 comment.

Comment on lines +30 to +38
function identityChanged(next: ObservedProtocolIdentity): boolean {
const previous = lastObservedIdentity;
return (
previous?.protocolVersion !== next.protocolVersion ||
previous.clientName !== next.clientName ||
previous.clientVersion !== next.clientVersion ||
previous.capabilityKeys !== next.capabilityKeys
);
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Confirmed as a false positive. When previous is null, previous?.protocolVersion !== next.protocolVersion is true because next.protocolVersion is a required string, so || short-circuits before any direct property access. readModernRequestEnvelope only produces this identity after validating the version is a string, and making it optional causes TypeScript to reject the function. Existing instrumentation tests reset the identity to null, drive the first modern request through the Sentry hooks, and pass without throwing.

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.

2 participants