---
name: flutter-mcp-nixos
description: Configura e usa o flutter-mcp-toolkit no NixOS via Nix flakes sem instalação manual. Use quando precisar conectar um LLM a um app Flutter em modo debug no NixOS — inspecionar UI, capturar screenshots, controlar widgets, verificar erros e hot reload via MCP.
---

# Flutter MCP Toolkit no NixOS

Esta skill encapsula o setup completo do `flutter-mcp-toolkit` no NixOS usando Nix flakes. Nenhuma instalação manual é necessária — o Nix cuida de tudo automaticamente.

> **Plataforma:** apenas `x86_64-linux`. O binário do release upstream só existe para `linux-x64`; em NixOS aarch64 (ex.: Asahi/ARM) esta skill não funciona.

---

## Resolvendo `SKILL_DIR`

Todos os comandos abaixo usam `SKILL_DIR` = diretório absoluto desta skill (onde está este `SKILL.md`). **Resolva-o uma vez e substitua nos comandos:**

```bash
# Rodado de dentro do diretório da skill:
SKILL_DIR="$(dirname "$(realpath SKILL.md)")"
echo "$SKILL_DIR"   # ex.: /home/USER/.claude/plugins/.../skills/flutter-mcp-nixos
```

> ⚠️ **Caminho estável:** o `SKILL_DIR` é gravado de forma **persistente** em `.claude/settings.json` (Passo 2). Se o plugin for atualizado ou movido, o caminho hardcoded quebra o servidor MCP silenciosamente. Se a skill viver num diretório que pode mudar, copie `scripts/` para um local fixo do usuário (ex.: `~/.config/flutter-mcp-toolkit/`) e aponte o `settings.json` para esse local estável.

---

## Arquitetura

Três camadas cooperam:

| Camada | Componente | Papel |
|--------|-----------|-------|
| **MCP Server** | `flutter-mcp-toolkit-server` | Servidor stdio que expõe 27+ ferramentas `fmt_*` ao Claude Code |
| **In-app toolkit** | pacote Dart `mcp_toolkit` | Adicionado ao app Flutter — habilita screenshots, layout, erros |
| **CLI** | `flutter-mcp-toolkit` | Doctor, validate-runtime, init, exec avulso |

---

## Execução de comandos

Todos os comandos shell devem ser executados via Nix para garantir o ambiente correto. **Sempre use `--no-write-lock-file`** para não tentar reescrever o `flake.lock` commitado (falha se o diretório da skill for read-only):

- Comandos `flutter`: `nix shell nixpkgs#flutter -c flutter ...` (registry do usuário) **ou**, para reprodutibilidade, o Flutter pinado no flake: `nix run --no-write-lock-file SKILL_DIR/scripts#flutter -- ...`
- Comandos `flutter-mcp-toolkit`: `nix run --no-write-lock-file SKILL_DIR/scripts#flutter-mcp-toolkit -- ...`
- Shell interativo com `flutter-mcp-toolkit` + Flutter pinado no PATH: `nix develop --no-write-lock-file SKILL_DIR/scripts -c <cmd>`
- Nunca invocar `flutter` ou `flutter-mcp-toolkit` diretamente sem prefixar com nix.

> **Flutter pinado vs. registry:** `nixpkgs#flutter` usa o nixpkgs do registry do usuário (versão imprevisível). O Flutter exposto pelo flake (`#flutter`) é pinado pelo mesmo `flake.lock` do toolkit, garantindo compatibilidade Flutter↔toolkit. Use o pinado se tiver incompatibilidades.

---

## Passo 1 — Verificar pré-requisitos

```bash
nix --version   # >= 2.18; requer experimental-features = nix-command flakes
nix shell nixpkgs#flutter -c flutter --version
```

Se `nix-command` ou `flakes` não estiver habilitado, adicione ao `/etc/nixos/configuration.nix`:
```nix
nix.settings.experimental-features = [ "nix-command" "flakes" ];
```

---

## Passo 2 — Configurar o servidor MCP no projeto Flutter

Crie ou edite `.claude/settings.json` na **raiz do projeto Flutter** (não use config global). Adicione a entrada `flutter-mcp-toolkit` em `mcpServers` sem remover entradas existentes:

```json
{
  "mcpServers": {
    "flutter-mcp-toolkit": {
      "command": "nix",
      "args": [
        "run",
        "--no-write-lock-file",
        "SKILL_DIR/scripts#flutter-mcp-toolkit-server"
      ],
      "type": "stdio"
    }
  }
}
```

> Use o `SKILL_DIR` **absoluto e estável** resolvido acima — esse caminho fica gravado aqui permanentemente.

Na primeira invocação o Nix faz download (~7 MB) e patcha o binário (~30s). Execuções seguintes são instantâneas (cache no Nix store).

---

## Passo 3 — Adicionar mcp_toolkit ao app Flutter

Execute a partir da raiz do projeto Flutter:

```bash
nix run --no-write-lock-file SKILL_DIR/scripts#flutter-mcp-toolkit -- codegen-init
```

Isso roda `flutter pub add mcp_toolkit` e imprime o trecho para `lib/main.dart`. Insira **antes** de `runApp()`:

```dart
import 'package:mcp_toolkit/mcp_toolkit.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  MCPToolkitBinding.instance
    ..initialize()
    ..initializeFlutterToolkit();
  runApp(const MyApp());
}
```

Se o app usa `runZonedGuarded`, propague erros:

```dart
runZonedGuarded(
  () => runApp(const MyApp()),
  (error, stack) => MCPToolkitBinding.instance.handleZoneError(error, stack),
);
```

---

## Passo 4 — Rodar o Flutter em modo debug

```bash
nix shell nixpkgs#flutter -c flutter run --debug --host-vmservice-port=8181 -d <device>
```

Use `--machine` para obter o URI exato do WebSocket (útil com múltiplos apps rodando):

```bash
nix shell nixpkgs#flutter -c flutter run --debug --machine --host-vmservice-port=8181 -d <device>
# Copie o valor de app.debugPort.wsUri da saída JSON
```

---

## Passo 5 — Validar o setup

```bash
nix run --no-write-lock-file SKILL_DIR/scripts#flutter-mcp-toolkit -- doctor --json
```

Saída esperada:
```json
{
  "summary": { "criticalFailures": 0 },
  "checks": [
    { "id": "vm_target_reachable",        "status": "pass", "critical": true },
    { "id": "mcp_toolkit_extensions",     "status": "pass", "critical": true },
    { "id": "dynamic_registry_available", "status": "pass", "critical": false }
  ]
}
```

---

## Ferramentas MCP disponíveis

Após o setup, estas ferramentas ficam disponíveis no Claude Code (prefixo `fmt_`):

| Categoria | Ferramentas |
|-----------|------------|
| **Descoberta** | `fmt_discover_debug_apps`, `fmt_connect_debug_app`, `fmt_get_vm` |
| **Inspeção** | `fmt_get_screenshots`, `fmt_get_view_details`, `fmt_semantic_snapshot`, `fmt_get_extension_rpcs` |
| **Erros/Logs** | `fmt_get_app_errors`, `fmt_get_recent_logs` |
| **Controle de UI** | `fmt_tap_widget`, `fmt_enter_text`, `fmt_scroll`, `fmt_swipe`, `fmt_drag`, `fmt_fill_form`, `fmt_navigate` |
| **Recarga** | `fmt_hot_reload_flutter`, `fmt_hot_restart_flutter`, `fmt_hot_reload_and_capture` |
| **Debug** | `fmt_evaluate_dart_expression`, `fmt_inspect_widget_at_point` |
| **Dinâmico** | `fmt_list_client_tools_and_resources`, `fmt_client_tool`, `fmt_client_resource` |

Liste todas com `fmt_capabilities`.

---

## Comandos de referência rápida

```bash
# Versão
nix run --no-write-lock-file SKILL_DIR/scripts#flutter-mcp-toolkit -- --version

# Doctor
nix run --no-write-lock-file SKILL_DIR/scripts#flutter-mcp-toolkit -- doctor --json

# Listar apps Flutter em debug
nix run --no-write-lock-file SKILL_DIR/scripts#flutter-mcp-toolkit -- exec --name discover_debug_apps --args '{}'

# Validação ponta a ponta
nix run --no-write-lock-file SKILL_DIR/scripts#flutter-mcp-toolkit -- validate-runtime --target ws://127.0.0.1:8181/<token>/ws

# Shell interativo com flutter-mcp-toolkit + Flutter pinado no PATH
nix develop --no-write-lock-file SKILL_DIR/scripts -c flutter-mcp-toolkit doctor --json
```

---

## Troubleshooting

### `vm_not_connected`
App não rodando ou URI stale após restart:
```bash
nix run --no-write-lock-file SKILL_DIR/scripts#flutter-mcp-toolkit -- exec --name discover_debug_apps --args '{}'
```
Passe o novo `targetId` explicitamente na próxima chamada de ferramenta.

### `connection_selection_required`
Múltiplos apps em debug — escolha o correto:
```bash
nix run --no-write-lock-file SKILL_DIR/scripts#flutter-mcp-toolkit -- exec --name discover_debug_apps --args '{}'
```
Use o `targetId` em `details.availableTargets`.

### `mcp_toolkit_extensions` falhando
1. Confirme que `MCPToolkitBinding.instance.initialize()` está antes de `runApp()`
2. Faça **hot restart** (não hot reload)
3. Rode doctor novamente

### Porta em uso
```bash
nix shell nixpkgs#flutter -c flutter run --debug --host-vmservice-port=8182 -d <device>
nix run --no-write-lock-file SKILL_DIR/scripts#flutter-mcp-toolkit -- --dart-vm-port 8182 doctor --json
```

### Servidor MCP não inicia / caminho inválido
Confirme que o `SKILL_DIR` gravado em `.claude/settings.json` ainda existe e contém `scripts/flake.nix`. Após atualizar/mover o plugin, o caminho antigo fica stale — re-resolva o `SKILL_DIR` e atualize o `settings.json`.

### Falha de `autoPatchelfHook` (lib faltando) após bump
Se o `nix build` reclamar de `.so` ausente, adicione a lib correspondente a `buildInputs` em `scripts/flake.nix` (já inclui `glibc` e `stdenv.cc.cc.lib`/libstdc++).

---

## Atualizar para nova versão

> Prefira releases **estáveis**. A versão pinada atual (`4.0.0-dev.1`) é um *dev release*; cheque https://github.com/Arenukvern/mcp_flutter/releases por uma versão estável antes de pinar.

```bash
VERSION="X.Y.Z"

# 1. Calcular o novo hash
nix shell nixpkgs#nix-prefetch-tools -c nix-prefetch-url --unpack \
  "https://github.com/Arenukvern/mcp_flutter/releases/download/v${VERSION}/flutter_mcp_${VERSION}_linux-x64.tar.gz" \
  | xargs -I{} nix hash convert --hash-algo sha256 --to sri {}

# 2. Editar SKILL_DIR/scripts/flake.nix: atualizar `version` e `hash`

# 3. (Opcional) Atualizar inputs pinados e refletir no flake.lock
nix flake update --flake SKILL_DIR/scripts

# 4. Testar — valida o hash, o autoPatchelfHook e a execução
nix build SKILL_DIR/scripts#flutter-mcp-toolkit --no-link
nix run --no-write-lock-file SKILL_DIR/scripts#flutter-mcp-toolkit -- --version
```
