# ADR 0001 — Capability kernel and tool-name prefix model

- **Status:** Accepted
- **Shipped:** v3.0.0
- **Authoritative source:** [`packages/server_capability_kernel/lib/`](https://github.com/Arenukvern/mcp_flutter/tree/main/packages/server_capability_kernel/lib)

## Context

Pre-v3.0, the dependency graph was upside-down at the server boundary:

- `flutter_live_edit_toolkit → mcp_toolkit` was correct (app-side).
- `mcp_server_dart → flutter_live_edit_toolkit` was a leak: the server statically
  depended on Flutter, which it never renders. This was the source of the
  long-standing `uses-material-design` warnings in server builds and tests.
- Every binary advertised live-edit's tool surface unconditionally; there was
  no way to compose a custom server with a different capability set.
- Tool names were flat across the MCP boundary — `tap_widget` from core and a
  hypothetical future `tap_widget` from another capability would collide
  silently.

## Decision

Introduce a **capability kernel** (`flutter_mcp_toolkit_capability_kernel`): a Dart package
that defines the contracts a unit of MCP functionality must satisfy. Each
capability is its own package, registered into a host (server or CLI) at
compile time. The kernel applies a `<capabilityId>_` prefix to every tool and
resource name the capability exposes.

The shape of the contract:

```dart
abstract interface class Capability {
  /// Stable id for the tool-name prefix (`<id>_<tool>`).
  /// Must match ^[a-z][a-z0-9_]*$. Reserved: 'app'.
  String get id;
  String get description;
  String get version;

  Future<void> register(CapabilityContext context);
  Future<void> dispose();
}

abstract interface class CapabilityContext {
  CapabilityConfig get config;
  void registerTool(ToolRegistration registration);
  void registerResource(ResourceRegistration registration);
  T require<T extends HostService>();
}
```

`HostService` is a marker interface for capabilities to request host machinery
(today: `CommandRunner` for the shared-core executor). Host services are
provided by the host at registration time; missing services throw
`HostServiceUnavailableError`.

### Prefix enforcement (kernel-only)

All rules apply at registration time and throw on violation:

- Capability id must match `^[a-z][a-z0-9_]*$` (`InvalidCapabilityIdError`).
- Capability id must not be `app` — reserved for unscoped dynamic
  registrations (`InvalidCapabilityIdError`).
- Tool name passed to `registerTool` must not start with the capability's
  prefix (`PrePrefixedToolNameError`). Capabilities pass bare names; the
  kernel composes the public name.
- Two static registrations resolving to the same fully-qualified name throw
  `ToolNameCollisionError` at host startup.
- A dynamic entry whose name collides with a static registration is rejected
  at the bridge with a structured error.

### Hard cut at v3.0.0

No deprecation aliases. No soft errors. No `--no-use-capability-kernel` opt-out.

Reasoning: a capability rename is rare, and a clean `tool_not_found` is easier
to debug than a silent fallback. Migration tooling would have outlived the
migration window by years.

## Consequences

**What changed:**

- `mcp_server_dart`'s static dep tree contains zero Flutter packages.
  `uses-material-design` warnings in server builds disappeared.
- All MCP tool names from the bundled `fmt` capability are prefixed: `tap_widget` → `fmt_tap_widget`,
  `hot_reload_and_capture` → `fmt_hot_reload_and_capture`, etc. Unprefixed
  names return `tool_not_found`. The locked surface is in
  [`tool/contracts/expected_tool_surface.txt`](https://github.com/Arenukvern/mcp_flutter/blob/main/tool/contracts/expected_tool_surface.txt).
- The dynamic-registry host trio is exposed as `fmt_list_client_tools_and_resources`,
  `fmt_client_tool`, and `fmt_client_resource` on MCP and in the CLI catalog,
  matching the `fmt_` naming used in `tools/list`.
- CLI catalog names remain bare for intrinsic fmt tools (`flutter-mcp-toolkit exec --name tap_widget`, …);
  the MCP kernel still publishes those as `fmt_tap_widget`. Dynamic-registry
  commands are the exception: their catalog name already includes the `fmt_` prefix.
- `mcp_server_dart` and `flutter-mcp-toolkit` became thin shells. Their default
  `main.dart` registers the bundled capability set; power users write their
  own `main.dart` to compose a different set.

**What we paid:**

- Every existing client-side configuration referencing a tool by name had to
  be updated. v3.0.0 ships the breaking change without a shim.
- Future capability work (live-edit re-integration, network introspection)
  has to shape itself as a capability rather than as a server feature.

## Non-goals

- Plugin-discovery / runtime capability loading. Capabilities register at
  compile time. A user wanting a different surface composes a different
  binary.
- Cross-process or remote capabilities.
- Per-capability semantic versioning beyond what the `version` field surfaces
  in `doctor`.

## Open follow-ups (optional v3.x)

- Capability-level configuration via a config file (today: CLI flags only).
- A `flutter-mcp-toolkit capabilities list` subcommand.
- `DynamicRegistryBridge` host service interface — designed but not yet
  implemented; planned alongside live-edit re-integration.

## Related work

- The post-v3.0 live-edit re-integration plan, which is the first new
  capability beyond `core`, is tracked in
  [`todo/live_edit_reintegration.md`](https://github.com/Arenukvern/mcp_flutter/blob/main/todo/live_edit_reintegration.md).
- Network introspection (P3), the other deferred capability-shaped feature,
  is specified in
  [`todo/p3_network_introspection.md`](https://github.com/Arenukvern/mcp_flutter/blob/main/todo/p3_network_introspection.md).
