Migrating from v2.x to v3.0

v3.0 is the capability-kernel cut. The most visible change for users is that every MCP tool now surfaces under a capability prefix. This guide covers what changed, what stayed, and the smallest possible diff to get a v2 client running again.

TL;DR

  • Add fmt_ to the front of every MCP tools/call name. That's it for 90% of clients.
  • The CLI catalog (flutter-mcp-toolkit exec --name <name>) is unchanged for intrinsic tools (tap_widget, get_vm, …): bare catalog names still map to fmt_* MCP tools. Dynamic-registry commands use their full fmt_* spelling in both CLI and MCP (fmt_list_client_tools_and_resources, fmt_client_tool, fmt_client_resource).
  • Resource URIs (visual://localhost/...) are unchanged.

If you see tool_not_found, prepend fmt_ to the tool name.

Config quick reference: use MCP server binary flutter-mcp-toolkit-server, registry key flutter-mcp-toolkit under mcpServers (legacy flutter-inspector still works). Copy-paste examples: mcp_server_dart README (Cline / Cursor / Claude sections).

flutter-inspector above means only the legacy mcpServers object key. The Claude Code subagent definition in this repo is flutter-mcp-toolkit-runtime (plugin/agents/flutter-mcp-toolkit-runtime.md), not flutter-inspector.

Binaries and mcpServers registry keys

Surfacev2.x (legacy)v3.0 (current)
CLI binaryflutter_mcp_cliflutter-mcp-toolkit
MCP server binaryflutter_inspector_mcpflutter-mcp-toolkit-server
JSON key in mcpServersoften flutter-inspectorflutter-mcp-toolkit (canonical); flutter-inspector still accepted

After make build, binaries live under mcp_server_dart/build/. The canonical plugin mcp.json shows recommended args (--resources, --images, --dynamics) and ${FLUTTER_MCP_BIN:-flutter-mcp-toolkit-server} for the server command.

validate-runtime and VM targeting (CLI)

The smoke test is flutter-mcp-toolkit validate-runtime:

  • Pass --target ws://… (exact app.debugPort.wsUri from flutter run --machine), or global --vm-service-uri when --target is omitted. If both differ, --target wins (warning on stderr).
  • If host desktop_window screenshot fails, the CLI retries once with flutter_layer. Inspect data.summary.captureFallbackUsed in the JSON result.

Details: mcp_server_dart README — validate-runtime.

What changed

MCP tool names are prefixed

The server now composes its tool surface from Capability instances loaded into a host registry. Each capability registers its tools under a bare name and the kernel applies the <capabilityId>_ prefix before publishing to tools/list. Today there is one capability — fmt — so every tool is prefixed fmt_.

v2.xv3.0
tap_widgetfmt_tap_widget
enter_textfmt_enter_text
scrollfmt_scroll
long_pressfmt_long_press
swipefmt_swipe
dragfmt_drag
hoverfmt_hover
press_keyfmt_press_key
semantic_snapshotfmt_semantic_snapshot
wait_forfmt_wait_for
fill_formfmt_fill_form
navigatefmt_navigate
handle_dialogfmt_handle_dialog
connect_debug_appfmt_connect_debug_app
discover_debug_appsfmt_discover_debug_apps
get_vmfmt_get_vm
get_extension_rpcsfmt_get_extension_rpcs
hot_reload_flutterfmt_hot_reload_flutter
hot_restart_flutterfmt_hot_restart_flutter
hot_reload_and_capturefmt_hot_reload_and_capture
evaluate_dart_expressionfmt_evaluate_dart_expression
get_recent_logsfmt_get_recent_logs
get_view_detailsfmt_get_view_details
get_app_errorsfmt_get_app_errors
get_screenshotsfmt_get_screenshots
capture_ui_snapshotfmt_capture_ui_snapshot
inspect_widget_at_pointfmt_inspect_widget_at_point
debug_dump_layer_tree (--dumps)fmt_debug_dump_layer_tree
debug_dump_semantics_tree (--dumps)fmt_debug_dump_semantics_tree
debug_dump_render_tree (--dumps)fmt_debug_dump_render_tree
debug_dump_focus_tree (--dumps)fmt_debug_dump_focus_tree
listClientToolsAndResourcesfmt_list_client_tools_and_resources
runClientToolfmt_client_tool
runClientResourcefmt_client_resource

The locked v3.0 surface is checked in at tool/contracts/expected_tool_surface.txt and pinned by mcp_server_dart/test/tool_surface_snapshot_test.dart. Any change to the surface fails CI, so what you see there is what tools/list will return.

Live-edit was removed

The flutter_live_edit/ packages and the live-edit tool surface were excised from v3.0. Five error codes that targeted live-edit are no longer emitted:

  • live_edit_backend_failed
  • live_edit_proposal_not_found
  • live_edit_apply_failed
  • live_edit_validation_failed
  • live_edit_disabled

If your client surfaced these to users, drop the corresponding branches. Re-integration is tracked in todo/selection_state_machine.md for a future minor release.

What did NOT change

  • Tool schemas. Every migrated tool kept its v2 input-schema shape byte-for-byte. The same connection override object, the same arg names, the same defaults.
  • Error envelope. Still {code, message, details, descriptor, recovery}. error.descriptor is still the machine-readable shape.
  • Resource URIs. visual://localhost/app/errors/{count}, visual://localhost/view/details, visual://localhost/view/screenshots are unchanged. Resources are not subject to the capability prefix.
  • CLI commands. flutter-mcp-toolkit exec --name tap_widget --args ... works exactly as in v2. The CLI uses the catalog vocabulary directly, not the MCP wire surface.
  • validate-runtime CLI — still the one-command smoke test; current CLI also accepts global --vm-service-uri when --target is omitted and retries flutter_layer after a failed host desktop_window capture (above).
  • Dynamic-registry host tools. These use the same fmt_* names on MCP and in exec --name: fmt_list_client_tools_and_resources, fmt_client_tool, fmt_client_resource.
  • VM service extension names. ext.mcp.toolkit.app_errors and friends are still emitted by mcp_toolkit exactly as before.

Smallest diff for a v2 client

 const tap = await mcp.callTool({
-  name: 'tap_widget',
+  name: 'fmt_tap_widget',
   arguments: { ref, snapshotId, connection: { uri } },
 });

If you wrap MCP tool calls in a helper, the cleanest fix is to prepend the prefix at the helper boundary so callers stay v2-style:

// helper.ts
const FMT_TOOLS = new Set([
  'tap_widget', 'enter_text', 'scroll', /* ... */
]);

function mcpName(bare: string) {
  return FMT_TOOLS.has(bare) ? `fmt_${bare}` : bare;
}

await mcp.callTool({ name: mcpName('tap_widget'), arguments: {...} });

(Source the list from expected_tool_surface.txt to stay in sync.)

Why this changed

v2 had every tool registered through a flat hand-written mixin. Adding a new vertical of functionality required touching the server's mixin, the shared command catalog, the schema helper, and the dispatch path — and there was no way to plug in third-party capabilities without editing the core repo.

v3 inverts that: a Capability is a thing that registers its tools/ resources through a CapabilityContext it gets handed at load time. The host applies the prefix and brokers dart_mcp dispatch. New verticals land as new capabilities; the prefix keeps their surfaces from colliding with fmt_.

See ARCHITECTURE.md "MCP Server Layer (Dart-based)" and the kernel sources at mcp_capability_kernel/

Troubleshooting

tool_not_found for a v2-named tool

You're still calling the unprefixed name. Add fmt_. v3.0 has no opt-out fallback for the legacy surface — the --use-capability-kernel flag was removed when the legacy path was deleted in T9.

Plugin / agent guides recommend v2 names

Update to the v3 names. The bundled plugin/skills/ tree tracks v3; if you have a fork or a vendored copy, refresh it.

MCP config still uses flutter-inspector or flutter_inspector_mcp

Rename the mcpServers entry to flutter-mcp-toolkit and set command to …/build/flutter-mcp-toolkit-server (see migration guide — Binaries). Keeping flutter-inspector as the key is still valid but legacy.

CI tests now break on snapshot diffs

If your CI ran tools/list against a pinned set of tool names, update the fixture to match expected_tool_surface.txt. The repo's own snapshot test enforces this exact contract.