# Analisi tecnica potenziamento MCP Playwright con approccio ChromeDevTools

Documento operativo per evolvere `playwright-node` introducendo feature ispirate a `chrome-devtools-mcp`, mantenendo il focus sul debug manuale di applicazioni business/enterprise PHP, ColdFusion e legacy.

Lo sviluppo è pensato per essere eseguito con Codex sul repository `sophiadeveloper/mcp-servers`.

## Valutazione sintetica aggiornata

### Evidenze osservate

- `playwright-node` ha un punto di ingresso centralizzato (`ensureBrowser`) e uno stato globale browser/context/page già compatibili con un'estensione incrementale.
- Il server espone già tool condensati basati su `action`, quindi le nuove capability devono restare dentro `browser_session` e `browser_interact` salvo decisione esplicita.
- `chrome-devtools-mcp` usa un modello operativo centrato su tab già aperte, snapshot, console, network e performance, coerente con il bisogno di debug su pagine già preparate manualmente.
- Playwright supporta `chromium.connectOverCDP`, ma la documentazione Playwright segnala che il collegamento CDP ha fidelità inferiore rispetto al protocollo Playwright nativo.

### Inferenze operative

- La prima milestone utile non è il debug bundle completo: è l'aggancio CDP sicuro on-demand con selezione tab non distruttiva.
- Le feature successive devono essere progettate come osservabilità progressiva: prima pagina/tab/capability, poi debug bundle, poi console/network completi, poi snapshot UID.
- In modalità CDP ogni policy che modifica il browser reale dell'utente deve essere opt-in, non default.
- Le modalità `launch` e `cdp` non devono essere trattate come configurazioni mutuamente esclusive dell'intero server: il target browser attivo deve poter cambiare tramite action MCP.

### Punti aperti da chiudere in implementazione

- Stabilire se `page_id` resta indice volatile o se introdurre subito anche un identificatore stabile di sessione.
- Definire se mantenere in memoria sia il browser Playwright lanciato internamente sia la connessione CDP, o se chiudere automaticamente il target non attivo per ridurre consumo RAM.
- Verificare con smoke reale quali API Playwright restano pienamente affidabili via CDP nel runtime locale.
- Mantenere performance trace come feature sperimentale esplicitamente marcata e coperta da smoke.

## Obiettivo

Consentire all'agente di lavorare sulla stessa pagina Chrome già aperta dall'utente, senza rifare da capo login, navigazione e passaggi manuali.

Scenario target:

```text
1. L'utente apre Chrome MCP tramite shortcut dedicata.
2. L'utente naviga manualmente fino alla pagina in errore.
3. L'utente chiede a Codex o Copilot di analizzare la tab corrente.
4. MCP seleziona la tab corretta.
5. MCP raccoglie debug bundle, console, network, snapshot e dati essenziali.
6. L'agente propone diagnosi e patch applicativa.
```

## Vincolo operativo Chrome

Modalità ufficialmente supportata:

```text
"C:\Program Files\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9222 --remote-debugging-address=127.0.0.1 --user-data-dir="%LOCALAPPDATA%\ChromeMcpProfile"
```

### Note importanti

- `--remote-debugging-address=127.0.0.1` limita l'esposizione alla macchina locale.
- `--user-data-dir` deve puntare a una directory dedicata diversa dal profilo Chrome standard.
- Con Chrome recente, `--remote-debugging-port` e `--remote-debugging-pipe` richiedono un `--user-data-dir` non standard per proteggere i profili reali; considerarlo requisito, non dettaglio opzionale.
- Non usare mai il profilo Chrome personale per questa modalità.
- La modalità guest può essere utile solo per test temporanei isolati, ma non è il comando ufficiale del workflow MCP perché non conserva login e stato sessione.
- Non esporre la porta CDP su interfacce di rete diverse da `127.0.0.1`.

## Workflow operativo attach on-demand

L'attach a Chrome già aperto non deve avvenire automaticamente all'avvio del server MCP.

Ruoli:

- **Prompt utente**: esprime l'intento, per esempio "Ho Chrome già aperto sulla pagina in errore, non navigare da capo".
- **Skill `mcp-browser-automation`**: traduce l'intento nel workflow corretto e istruisce l'agente a usare `attach_cdp` prima di `list_pages`, `select_page_*` e `debug_bundle`.
- **Tool MCP `browser_session`**: esegue l'operazione reale tramite `action=attach_cdp`, usando `cdp_url` esplicito o il default `PLAYWRIGHT_CDP_URL`.
- **Hook**: opzionale e non esecutivo; può solo ricordare all'agente che, se l'utente parla di Chrome già aperto, deve valutare `attach_cdp` invece di `navigate`.

Sequenza target:

```text
1. L'utente apre Chrome MCP con remote debugging.
2. L'utente naviga manualmente fino alla pagina in errore.
3. L'utente chiede all'agente di analizzare Chrome già aperto.
4. La skill porta l'agente a chiamare browser_session action=attach_cdp.
5. L'agente chiama list_pages e seleziona la tab corretta.
6. L'agente raccoglie debug_bundle, console e network senza navigare da capo.
7. Se serve tornare al browser interno, l'agente chiama use_launch_browser.
```

## Stato attuale sintetico di `playwright-node`

Il server attuale:

- usa `playwright` e `@modelcontextprotocol/sdk`;
- espone due tool principali: `browser_session` e `browser_interact`;
- supporta sia browser interno (`launch`) sia attach on-demand a Chrome già aperto (`cdp`);
- espone action CDP operative: `attach_cdp`, `detach_cdp`, `use_launch_browser`, `close_browser_target`;
- supporta gestione tab CDP: `list_pages`, `select_page`, `select_page_by_url`, `select_page_by_title`, `get_current_page`;
- include raccolta diagnostica strutturata: `debug_bundle`, `console_list`/`console_get`, `network_list`/`network_get`/`network_save_body`;
- include snapshot UID e interazioni UID (`click_uid`, `fill_uid`, `hover_uid`, `press_uid`);
- include performance trace base come capability sperimentale;
- mantiene policy `ALLOWED_URLS`, `ALLOW_FILE_URLS`, `ALLOWED_FILE_ROOTS` sul browser Playwright interno (`launch`).

## Regola generale su package.json e reinstall

Per minimizzare impatti e regressioni:

- preferire implementazioni basate su `playwright` già presente;
- evitare nuove dipendenze se non strettamente necessarie;
- se si modifica `playwright-node/package.json`, eseguire reinstall/build del workspace;
- se si modifica solo `playwright-node/index.js`, di norma basta riavviare il client MCP o il server MCP.

### Quando serve reinstallare o rieseguire setup

| Tipo modifica | Serve `npm install`? | Serve riavvio MCP? | Note |
|---|---:|---:|---|
| Solo `index.js` | No | Sì | Caso previsto per quasi tutti gli step |
| Solo `.env.example` o docs | No | No | Salvo rigenerazione configurazioni |
| `package.json` o `package-lock.json` | Sì | Sì | Eseguire `setup.ps1` o `setup.sh` |
| Nuove dipendenze runtime | Sì | Sì | Da evitare nella prima fase |
| Nuovi script generatori config MCP | Dipende | Sì | Verificare output config |

Comandi consigliati dopo modifica a `package.json`:

```powershell
powershell -ExecutionPolicy Bypass -File .\setup.ps1
```

oppure:

```bash
bash setup.sh
```

---

# Step 0 - Preparazione tecnica e refactor minimo

## Priorità

Propedeutico.

## Obiettivo

Preparare il codice per supportare sia il browser Playwright avviato internamente sia il browser Chrome già aperto via CDP.

## Modifiche consigliate

### 0.1 Introdurre target browser dinamico

Variabili ambiente proposte:

```text
# Target usato per le action implicite quando non esiste ancora un browser attivo.
BROWSER_DEFAULT_TARGET=launch|cdp

# URL CDP predefinito usato da action=attach_cdp se non viene passato cdp_url.
PLAYWRIGHT_CDP_URL=http://127.0.0.1:9222

BROWSER_HEADLESS=true|false
```

Regole:

- `PLAYWRIGHT_CDP_URL` non deve forzare automaticamente la modalità `cdp`;
- `PLAYWRIGHT_CDP_URL` è solo il default per `attach_cdp`;
- `BROWSER_DEFAULT_TARGET=launch` mantiene il comportamento legacy e resta il default;
- `BROWSER_DEFAULT_TARGET=cdp` è un opt-in per chi vuole tentare CDP come primo target implicito;
- anche con default `launch`, l'utente può passare a Chrome già aperto chiamando `browser_session action=attach_cdp`;
- anche dopo `attach_cdp`, l'utente può tornare al browser interno chiamando `browser_session action=use_launch_browser`;
- se nessuna variabile è impostata, mantenere comportamento attuale con `chromium.launch()`.

### 0.2 Separare stato browser da stato pagina

Creare uno stato esplicito, per esempio:

```js
const browserState = {
  activeTarget: "launch",
  launch: {
    browser: null,
    context: null,
    page: null,
    pages: []
  },
  cdp: {
    browser: null,
    context: null,
    page: null,
    pages: [],
    cdpUrl: null,
    attachedAt: null
  },
  activeFrame: null,
  nextPageStableId: 1,
  pageStableIds: new WeakMap()
};
```

Regole:

- le action legacy (`navigate`, `get_dom`, `screenshot`, ecc.) operano sul target attivo;
- `navigate` avvia il browser interno se non esiste alcun target attivo, preservando la compatibilità;
- `attach_cdp` cambia il target attivo a `cdp` senza navigare;
- `use_launch_browser` cambia il target attivo a `launch`, creando il browser interno solo se serve;
- `detach_cdp` deve disconnettere realmente l'handle CDP Playwright (non solo azzerare lo stato locale) e non deve mai chiudere il Chrome remoto dell'utente.

### 0.3 Normalizzare output JSON

Aggiungere helper:

```js
function okText(message, structuredContent = undefined) { ... }
function errorText(message, structuredContent = undefined) { ... }
```

Obiettivo: mantenere compatibilità con i tool esistenti, ma facilitare output strutturati per Codex.

Regola:

- ogni nuova action deve restituire `content` breve e leggibile;
- quando l'output ha dati riusabili, aggiungere `structuredContent` stabile;
- non rimuovere il formato testuale legacy dalle action esistenti nella stessa iterazione.

## Test di verifica Step 0

### Test A - regressione navigazione esistente

1. Avviare MCP senza `PLAYWRIGHT_CDP_URL` e senza action `attach_cdp`.
2. Usare `browser_session` con:

```json
{
  "action": "navigate",
  "url": "http://localhost/"
}
```

3. Verificare che il comportamento sia identico a prima.

### Test B - capability

Aggiungere o aggiornare:

```json
{
  "action": "get_capabilities"
}
```

Output atteso:

```json
{
  "server": {
    "name": "playwright-node"
  },
  "browser": {
    "supportsLaunch": true,
    "supportsCdpAttach": true,
    "activeTarget": "launch",
    "defaultTarget": "launch"
  }
}
```

### Test C - switch on-demand senza restart MCP

1. Avviare MCP in default `launch`.
2. Chiamare `browser_session action=navigate` su una pagina locale.
3. Aprire Chrome MCP con remote debugging.
4. Chiamare `browser_session action=attach_cdp`.
5. Verificare che `get_current_page` ora legga la tab Chrome.
6. Chiamare `browser_session action=use_launch_browser`.
7. Verificare che il browser interno torni target attivo senza riavviare il server MCP.

## Reinstall MCP

Non necessario se si modifica solo `index.js`.

---

# Step 1 - CDP attach on-demand e switch target

## Priorità

1.

## Feature

**CDP attach on-demand** con switch dinamico tra browser Playwright interno e Chrome esistente.

## Motivo

Permette di lavorare sulla pagina Chrome già aperta dall'utente, evitando di rifare login, menu, ricerche e click intermedi.

Il passaggio deve avvenire tramite action MCP, senza riavviare il server e senza cambiare variabili ambiente.

## Comportamento atteso

Se Chrome è avviato con:

```text
--remote-debugging-port=9222 --remote-debugging-address=127.0.0.1
```

il server deve potersi collegare a:

```text
http://127.0.0.1:9222
```

senza aprire un nuovo Chromium Playwright headless.

Se un browser Playwright interno è già stato avviato, l'attach CDP deve cambiare solo il target attivo. Il browser interno può restare disponibile per tornare a `launch` con `use_launch_browser`, salvo flag esplicito di chiusura.

Nota di rischio:

- `connectOverCDP` è sufficiente per l'MVP di attach, tab selection, snapshot e raccolta diagnostica;
- alcune funzionalità avanzate possono avere affidabilità inferiore rispetto al browser lanciato nativamente da Playwright;
- ogni feature CDP avanzata deve quindi avere errore controllato e fallback documentato.

## Implementazione consigliata

### 1.1 Separare risoluzione target e inizializzazione browser

Pseudo-flusso:

```js
async function getActiveBrowser(storageStatePath = undefined) {
  if (browserState.activeTarget === "cdp") {
    return ensureCdpBrowser();
  }

  return ensureLaunchedBrowser(storageStatePath);
}
```

Regole:

- sostituire gradualmente l'uso diretto delle variabili globali `browser`, `context`, `page`, `pages` con helper che leggono il target attivo;
- mantenere wrapper/fallback per ridurre il refactor della prima iterazione;
- `PLAYWRIGHT_CDP_URL` non deve essere letto dentro `getActiveBrowser()` come selettore automatico di modalità.

### 1.2 Aggiungere action di controllo target

Estendere `browser_session` con:

```text
get_browser_status
attach_cdp
detach_cdp
use_launch_browser
close_browser_target
```

Input schema aggiuntivo:

```json
{
  "target": { "type": "string", "enum": ["launch", "cdp"] },
  "cdp_url": { "type": "string" },
  "close_inactive": { "type": "boolean" },
  "close_chrome": { "type": "boolean" }
}
```

Regole action:

- `get_browser_status`: restituisce target attivo, default target, stato `launch`, stato `cdp`, URL e pagina corrente se disponibili;
- `attach_cdp`: usa `cdp_url` se passato, altrimenti `PLAYWRIGHT_CDP_URL`, altrimenti `http://127.0.0.1:9222`;
- `attach_cdp`: non naviga, non crea nuove tab se ne esiste almeno una aperta, non chiude il browser interno;
- `detach_cdp`: deve rilasciare in modo non distruttivo l'handle CDP Playwright e poi pulire lo stato locale MCP; non deve chiudere Chrome remoto;
- `use_launch_browser`: seleziona il browser interno, creandolo lazily se non esiste;
- `close_browser_target`: chiude un target gestito dal server; per `target=launch`, dopo chiusura deve promuovere `cdp` se disponibile, altrimenti evitare stato semanticamente incoerente;
- `close_browser_target` con `target=cdp`: deve comportarsi come `detach_cdp` (disconnessione handle Playwright + cleanup stato locale), senza mai chiudere Chrome remoto;
- `close_chrome=true`: non consentito/blocked in questo progetto (vincolo permanente), sia in debug sia in altri casi.

### 1.3 Implementare `ensureCdpBrowser()`

Indicazione:

```js
async function ensureCdpBrowser(cdpUrl = undefined) {
  const target = browserState.cdp;
  const resolvedCdpUrl = cdpUrl || target.cdpUrl || PLAYWRIGHT_CDP_URL || "http://127.0.0.1:9222";

  if (!target.browser || target.cdpUrl !== resolvedCdpUrl) {
    target.browser = await chromium.connectOverCDP(resolvedCdpUrl);
    target.context = target.browser.contexts()[0];
    target.cdpUrl = resolvedCdpUrl;
    target.attachedAt = new Date().toISOString();

    if (!target.context) {
      throw new Error("CDP attach riuscito ma nessun browser context disponibile.");
    }

    target.pages = target.context.pages();
    target.page = target.pages.find(p => !p.isClosed()) || target.pages[0] || null;
    if (!target.page) {
      throw new Error("CDP attach riuscito ma nessuna pagina disponibile nel browser remoto.");
    }
    browserState.activeFrame = null;

    for (const p of target.pages) {
      attachPageListeners(p);
    }

    target.context.on("page", newPage => {
      target.pages.push(newPage);
      attachPageListeners(newPage);
    });
  }

  refreshPages("cdp");
  browserState.activeTarget = "cdp";
  return { browser: target.browser, context: target.context, page: target.page };
}
```

Regole aggiuntive in modalità CDP:

- non creare una nuova pagina se esiste almeno una tab non chiusa;
- non chiudere automaticamente tab dell'utente per limiti memoria;
- non applicare il limite legacy di massimo 3 tab;
- non resettare storage, cookie o sessione;
- non usare `storageStatePath` per ricreare context in modalità CDP.

### 1.4 Non installare route invasive in modalità CDP

Il codice attuale installa policy `context.route("**/*", ...)`.

In modalità CDP attach il server non deve installare `context.route("**/*", ...)` sul Chrome remoto.

Motivazione:

- il Chrome CDP è avviato con profilo dedicato al debug;
- l'obiettivo è preservare lo stato reale della tab già preparata manualmente;
- bloccare richieste in questa modalità può falsare la diagnosi;
- le policy `ALLOWED_URLS`, `ALLOW_FILE_URLS` e `BLOCK_MEDIA` restano applicate solo al browser Playwright lanciato internamente.
- in modalita' CDP, `navigate` non deve essere bloccata dalle policy legacy `ALLOWED_URLS` / `ALLOW_FILE_URLS` / `ALLOWED_FILE_ROOTS` / `BLOCK_MEDIA`.

### 1.5 Aggiornare capability e status

`get_capabilities` deve esporre:

```json
{
  "browser": {
    "activeTarget": "cdp",
    "defaultTarget": "launch",
    "supportsLaunch": true,
    "supportsCdpAttach": true,
    "launch": {
      "available": true,
      "active": false
    },
    "cdp": {
      "available": true,
      "active": true,
      "cdpUrl": "http://127.0.0.1:9222",
      "attached": true
    }
  }
}
```

## Test di verifica Step 1

### Test A - Chrome raggiungibile

Aprire Chrome dalla shortcut indicata e verificare:

```powershell
curl http://127.0.0.1:9222/json/version
```

Output atteso: JSON con `Browser`, `Protocol-Version`, `webSocketDebuggerUrl`.

### Test B - attach MCP on-demand

Configurare ambiente MCP:

```text
PLAYWRIGHT_CDP_URL=http://127.0.0.1:9222
```

Chiamare:

```json
{
  "action": "get_capabilities"
}
```

Verifica prima dell'attach:

- `supportsCdpAttach=true`;
- `defaultTarget` coerente con configurazione;
- CDP non deve risultare attached solo perché `PLAYWRIGHT_CDP_URL` è configurato.

Poi chiamare:

```json
{
  "action": "attach_cdp"
}
```

Infine chiamare di nuovo:

```json
{
  "action": "get_capabilities"
}
```

Output atteso dopo attach:

```json
{
  "browser": {
    "activeTarget": "cdp",
    "cdp": {
      "attached": true,
      "active": true,
      "cdpUrl": "http://127.0.0.1:9222"
    }
  }
}
```


### Test C - non navigare da capo

1. In Chrome, aprire manualmente `http://localhost/test-mcp.html`.
2. Scrivere qualcosa in un campo input.
3. Chiamare MCP senza `navigate`.
4. Verificare che la pagina selezionata sia quella già aperta e che il valore del campo sia ancora presente.

## Reinstall MCP

Non necessario se si usa `chromium.connectOverCDP`, già incluso in `playwright`.

Serve solo riavviare MCP dopo modifica del codice.

---

# Step 2 - List/select tab con filtro URL/titolo

## Priorità

2.

## Feature

**List/select tab con filtro URL/titolo**.

## Motivo

Se Chrome ha più tab aperte, l'agente deve poter scegliere quella corretta senza aprire un nuovo URL e senza perdere stato.

## Tool/action da aggiungere

Estendere `browser_session` con:

```text
list_pages
select_page
select_page_by_url
select_page_by_title
get_current_page
```

Input schema aggiuntivo:

```json
{
  "page_id": { "type": "number" },
  "stable_page_id": { "type": "string" },
  "url_contains": { "type": "string" },
  "title_contains": { "type": "string" },
  "bring_to_front": { "type": "boolean" }
}
```

## Output `list_pages`

Esempio:

```json
{
  "pages": [
    {
      "page_id": 0,
      "stable_page_id": "p1",
      "selected": false,
      "title": "Dashboard",
      "url": "http://localhost/app/home.cfm",
      "closed": false
    },
    {
      "page_id": 1,
      "stable_page_id": "p2",
      "selected": true,
      "title": "Errore cliente",
      "url": "http://localhost/app/clienti/save.cfm",
      "closed": false
    }
  ]
}
```

## Regole consigliate

- `page_id` coincide con l'indice corrente in `pages` dopo refresh.
- aggiungere anche `stable_page_id` assegnato dal server MCP e stabile finché la pagina resta viva.
- accettare `page_id` per compatibilità e `stable_page_id` per selezioni più robuste.
- Prima di listare, rimuovere pagine chiuse.
- `select_page` deve impostare `page` e resettare `activeFrame`.
- Se `bring_to_front=true`, chiamare `page.bringToFront()`.
- Se il filtro URL/titolo trova più risultati, restituire errore non distruttivo con lista candidati.
- Non chiamare `goto()` in nessuna select action.
- Se una tab viene chiusa tra `list_pages` e `select_page`, restituire errore controllato e chiedere nuovo `list_pages`.

## Test di verifica Step 2

### Test A - più tab

1. Aprire Chrome MCP.
2. Aprire tre tab:

```text
http://localhost/a.html
http://localhost/b.html
http://localhost/clienti/edit.cfm?id=123
```

3. Chiamare:

```json
{
  "action": "list_pages"
}
```

Verifica:

- compaiono tutte le tab;
- ogni tab ha `page_id`, `title`, `url`;
- una sola tab è `selected=true`.

### Test B - select per URL

```json
{
  "action": "select_page_by_url",
  "url_contains": "/clienti/edit.cfm",
  "bring_to_front": true
}
```

Verifica:

- la tab corretta diventa selezionata;
- `get_current_page` restituisce l'URL corretto;
- non viene eseguita alcuna nuova navigazione.

### Test C - filtro ambiguo

Aprire due tab con URL contenente `/clienti/` e chiamare:

```json
{
  "action": "select_page_by_url",
  "url_contains": "/clienti/"
}
```

Output atteso: errore controllato con candidati, senza cambiare pagina attiva.

## Reinstall MCP

Non necessario.

---

# Step 3 - Debug bundle one-shot

## Priorità

3.

## Feature

**Debug bundle one-shot**.

## Motivo

Rende il workflow realmente usabile: l'utente non deve chiedere manualmente console, network, DOM, screenshot, storage e frame in chiamate separate.

Limite esplicito:

- console e network completi sono raccolti dal momento in cui MCP si collega alla pagina;
- eventi già avvenuti prima dell'attach possono non essere recuperabili via Playwright;
- il bundle deve dichiarare `capturedSince` e non fingere copertura storica completa.

## Action da aggiungere

```text
browser_session action=debug_bundle
```

Input suggeriti:

```json
{
  "action": "debug_bundle",
  "include_snapshot": true,
  "include_screenshot": false,
  "include_storage": true,
  "include_network": true,
  "include_console": true,
  "max_items": 30,
  "save_artifacts": true
}
```

## Output consigliato

```json
{
  "capturedSince": "2026-05-25T10:30:00.000Z",
  "page": {
    "title": "Modifica cliente",
    "url": "http://localhost/app/clienti/edit.cfm?id=123",
    "page_id": 1
  },
  "frames": [
    {
      "index": 0,
      "name": "",
      "url": "http://localhost/app/clienti/edit.cfm?id=123",
      "isMainFrame": true
    }
  ],
  "console": {
    "errors": [],
    "warnings": [],
    "total": 0
  },
  "network": {
    "failed": [],
    "httpErrors": [],
    "lastXhrOrFetch": []
  },
  "storage": {
    "localStorageKeys": [],
    "sessionStorageKeys": []
  },
  "snapshotPreview": "..."
}
```

## Regole anti-token

- Restituire un `content` sintetico e il dettaglio in `structuredContent`.
- Default `include_screenshot=false`.
- Se screenshot richiesto, preferire salvataggio file e non base64 inline.
- Limitare ogni sezione con `max_items`.
- Per response body grandi, restituire path file e preview troncata.
- Non includere cookie value, authorization header o dati sensibili in output inline.

## Percorso artifact consigliato

```text
.mcp-browser-artifacts/
  debug-bundles/
  screenshots/
  network/
  traces/
```

Aggiungere `.gitignore` se necessario:

```gitignore
.mcp-browser-artifacts/
```

## Test di verifica Step 3

### Test A - debug bundle su pagina statica

Creare pagina locale:

```html
<!doctype html>
<html>
<body>
  <h1>Test debug bundle</h1>
  <button id="btn">Salva</button>
  <script>console.warn("warning test bundle")</script>
</body>
</html>
```

Aprirla in Chrome MCP e chiamare:

```json
{
  "action": "debug_bundle",
  "include_snapshot": true,
  "include_console": true,
  "include_network": true,
  "include_screenshot": false
}
```

Verifica:

- output contiene URL e titolo;
- output contiene warning console;
- output contiene snapshot o preview pagina;
- output non include base64 screenshot.

### Test B - artifact screenshot opzionale

```json
{
  "action": "debug_bundle",
  "include_screenshot": true,
  "save_artifacts": true
}
```

Verifica:

- viene creato file PNG sotto `.mcp-browser-artifacts/screenshots/`;
- output restituisce il path;
- non viene restituito base64 salvo flag esplicito.

## Reinstall MCP

Non necessario.

---

# Step 4 - Network completo con body su file

## Priorità

4.

## Feature

**Network completo con body su file**.

## Motivo

Fondamentale per debug PHP/ColdFusion/AJAX: spesso il problema è nell'ultima POST, in una XHR 500, in un redirect inatteso o in un payload mancante.

## Limite attuale

Il server registra solo:

- request failed;
- HTTP status >= 400;
- URL bloccati;
- massimo 50 righe testuali.

## Nuovo modello dati

Creare store in memoria:

```js
const networkStore = {
  nextId: 1,
  entries: [],
  maxEntries: 300
};
```

Ogni entry:

```json
{
  "id": 12,
  "timestamp": "2026-05-25T10:30:00.000Z",
  "pageUrl": "http://localhost/app/clienti/edit.cfm?id=123",
  "method": "POST",
  "url": "http://localhost/app/clienti/save.cfm",
  "resourceType": "xhr",
  "status": 500,
  "statusText": "Internal Server Error",
  "requestHeadersPreview": {},
  "responseHeadersPreview": {},
  "postDataPreview": "id=123&provincia=",
  "failed": false,
  "failureText": null,
  "durationMs": 184,
  "requestBodyFile": null,
  "responseBodyFile": null
}
```

## Action da aggiungere

```text
browser_network action=list
browser_network action=get
browser_network action=save_body
browser_network action=clear
```

In alternativa, mantenendo tool condensato:

```text
browser_session action=network_list
browser_session action=network_get
browser_session action=network_save_body
browser_session action=network_clear
```

## Filtri utili

```json
{
  "resource_types": ["xhr", "fetch", "document"],
  "status_min": 400,
  "url_contains": "/clienti/",
  "method": "POST",
  "limit": 50,
  "include_preserved": true
}
```

## Body su file

Per evitare spreco token:

- inline solo preview limitata, per esempio 2 KB;
- salvataggio body completo solo se richiesto;
- default: non leggere/salvare body completi durante `network_list`;
- tentare `response.body()` solo in `network_save_body` o quando un flag esplicito lo richiede;
- path restituito in output;
- limiti dimensione configurabili.
- non salvare automaticamente body binari salvo flag esplicito;
- non fallire la richiesta MCP se il browser/CDP non consente di leggere il body: restituire errore controllato sulla singola entry.

Variabili proposte:

```text
NETWORK_MAX_ENTRIES=300
NETWORK_PREVIEW_BYTES=2048
NETWORK_BODY_MAX_BYTES=5242880
NETWORK_ARTIFACT_DIR=.mcp-browser-artifacts/network
NETWORK_REDACT_HEADERS=authorization,cookie,set-cookie,x-api-key
```

## Note implementative Playwright

Usare listener:

```js
page.on("request", ...)
page.on("response", ...)
page.on("requestfailed", ...)
page.on("requestfinished", ...)
```

Per response body:

```js
const body = await response.body();
```

Applicare try/catch: alcune risposte non sono leggibili, possono essere troppo grandi o possono non essere disponibili in modalità CDP.

## Test di verifica Step 4

### Test A - chiamata XHR 500

Creare endpoint locale o pagina di test che fa:

```js
fetch("/test-500", {
  method: "POST",
  body: new URLSearchParams({ id: "123", provincia: "" })
});
```

Chiamare:

```json
{
  "action": "network_list",
  "resource_types": ["xhr", "fetch"],
  "status_min": 400
}
```

Verifica:

- richiesta presente;
- metodo `POST`;
- status `500`;
- preview payload presente;
- header sensibili redatti.

### Test B - salvataggio body response

```json
{
  "action": "network_save_body",
  "id": 12,
  "body": "response"
}
```

Verifica:

- viene creato file sotto `.mcp-browser-artifacts/network/`;
- output restituisce path file;
- contenuto file corrisponde alla response;
- output MCP non contiene l'intero body se grande.

### Test C - clear

```json
{
  "action": "network_clear"
}
```

Verifica: `network_list` restituisce lista vuota.

## Reinstall MCP

Non necessario se implementato con API Playwright e Node `fs/path`.

---

# Step 5 - Console completa con stack

## Priorità

5, con predisposizione minima già nello Step 3.

## Feature

**Console completa con stack**.

## Motivo

Fondamentale per errori JS reali. Nei gestionali legacy spesso il problema emerge da:

- `console.error`;
- eccezioni non gestite;
- plugin jQuery/Select2/Bootstrap;
- errori in iframe;
- script caricati in ordine sbagliato;
- response AJAX non compatibile con il codice frontend.

Nota: il modello dati console può essere introdotto in modo minimale prima del network completo, così `debug_bundle` produce subito una diagnosi più utile.

## Limite attuale

Il server salva solo errori console e page exception come stringa.

## Nuovo store

```js
const consoleStore = {
  nextId: 1,
  entries: [],
  maxEntries: 300
};
```

Entry:

```json
{
  "id": 7,
  "timestamp": "2026-05-25T10:30:00.000Z",
  "type": "error",
  "text": "Uncaught TypeError: Cannot read properties of null",
  "location": {
    "url": "http://localhost/app/js/clienti.js",
    "lineNumber": 42,
    "columnNumber": 13
  },
  "argsPreview": ["..."],
  "stack": "...",
  "pageUrl": "http://localhost/app/clienti/edit.cfm?id=123"
}
```

## Action da aggiungere

```text
browser_session action=console_list
browser_session action=console_get
browser_session action=console_clear
```

Filtri:

```json
{
  "types": ["error", "warning"],
  "limit": 50,
  "include_stack": false
}
```

## Implementazione consigliata

Listener:

```js
page.on("console", async msg => { ... })
page.on("pageerror", exception => { ... })
```

Per `console`:

- salvare `msg.type()`;
- salvare `msg.text()`;
- salvare `msg.location()`;
- provare a serializzare `msg.args()` con limiti;
- non fallire se un JSHandle non è serializzabile.

Per `pageerror`:

- salvare `exception.message`;
- salvare `exception.stack` se presente;
- tipo `pageerror`.

## Test di verifica Step 5

### Test A - console error con location

Pagina di test:

```html
<script>
console.error("errore console test", { id: 123 });
</script>
```

Chiamare:

```json
{
  "action": "console_list",
  "types": ["error"]
}
```

Verifica:

- compare entry type `error`;
- testo contiene `errore console test`;
- location contiene URL e line number.

### Test B - pageerror con stack

Pagina di test:

```html
<script>
function a() { b(); }
function b() { throw new Error("errore stack test"); }
a();
</script>
```

Chiamare:

```json
{
  "action": "console_list",
  "types": ["pageerror"],
  "include_stack": true
}
```

Verifica:

- compare `errore stack test`;
- stack presente;
- `debug_bundle` include summary errori.

## Reinstall MCP

Non necessario.

---

# Step 6 - Snapshot con UID

## Priorità

6.

## Feature

**Snapshot con UID**.

## Motivo

Migliora l'interazione e riduce token. L'agente riceve una rappresentazione testuale della pagina orientata agli elementi utili invece di HTML rumoroso o screenshot.

## Obiettivo pratico

Output tipo:

```text
Page: Modifica cliente

[uid=12] textbox "Ragione sociale" value="ACME SRL"
[uid=13] textbox "Partita IVA" value="12345678901"
[uid=14] combobox "Provincia" value=""
[uid=15] button "Salva"
[uid=21] alert "Provincia obbligatoria"
```

Poi l'agente può usare:

```json
{
  "action": "click_uid",
  "uid": "15"
}
```

oppure:

```json
{
  "action": "fill_uid",
  "uid": "14",
  "value": "MI"
}
```

## Action da aggiungere

```text
browser_session action=snapshot
browser_interact action=click_uid
browser_interact action=fill_uid
browser_interact action=hover_uid
browser_interact action=press_uid
```

## Strategia implementativa consigliata

### Fase 6A - DOM scanner pragmatico

Implementare prima una funzione JS in pagina che:

- enumera elementi interattivi visibili;
- include alert, role significativi, heading, testo errore;
- calcola label associata;
- assegna UID incrementali;
- salva mappa in `window.__mcpUidMap`;
- restituisce righe testuali compatte.

Selettori iniziali:

```js
a, button, input, textarea, select,
[role="button"], [role="link"], [role="checkbox"], [role="combobox"],
[role="alert"], .alert, .error, .invalid-feedback,
h1, h2, h3
```

### Fase 6B - Miglioramento accessibilità/CDP

In una fase successiva valutare snapshot basato su Accessibility tree via CDP.

Motivo: più vicino al modello ChromeDevTools, ma più complesso per mappare UID a elementi interagibili.

## Regole UID

- UID validi solo fino alla prossima navigazione o nuovo snapshot.
- Dopo ogni `snapshot`, rigenerare `window.__mcpUidMap`.
- Se `click_uid` non trova UID, chiedere nuovo snapshot.
- Non usare UID persistenti su pagine diverse.

## Test di verifica Step 6

### Test A - snapshot form

Pagina di test:

```html
<label for="ragione_sociale">Ragione sociale</label>
<input id="ragione_sociale" value="ACME SRL">
<label for="provincia">Provincia</label>
<select id="provincia"><option value="">Seleziona</option><option>MI</option></select>
<button id="salva">Salva</button>
<div class="alert">Provincia obbligatoria</div>
```

Chiamare:

```json
{
  "action": "snapshot"
}
```

Verifica:

- output contiene textbox Ragione sociale;
- output contiene combobox Provincia;
- output contiene button Salva;
- output contiene alert Provincia obbligatoria;
- ogni elemento ha UID.

### Test B - fill_uid

```json
{
  "action": "fill_uid",
  "uid": "<uid-provincia>",
  "value": "MI"
}
```

Verifica:

- valore campo aggiornato;
- nuovo snapshot mostra `value="MI"`.

### Test C - click_uid

```json
{
  "action": "click_uid",
  "uid": "<uid-salva>"
}
```

Verifica:

- click eseguito;
- eventuale navigazione o AJAX viene catturata da network store;
- debug bundle successivo vede lo stato aggiornato.

## Reinstall MCP

Non necessario.

---

# Step 7 - Performance trace

## Priorità

Implementato nella branch corrente come feature sperimentale a supporto del debug frontend.

## Feature

**Performance trace**.

## Motivo

Serve quando una pagina gestionale è lenta o quando il problema riguarda caricamento frontend, script pesanti, rendering o chiamate network bloccanti.

Decisione adottata: tenere la capability come sperimentale nel capability output e coprirla con smoke dedicato.

## Implementazione consigliata

Evitare dipendenze Lighthouse nella prima fase.

Implementare trace base tramite CDP session:

```js
const client = await context.newCDPSession(page);
await client.send("Tracing.start", { ... });
await client.send("Tracing.end");
```

In alternativa, se sufficiente per test interni, usare API tracing Playwright.

## Action da aggiungere

```text
browser_session action=performance_start_trace
browser_session action=performance_stop_trace
browser_session action=performance_summary
```

Input:

```json
{
  "action": "performance_start_trace",
  "reload": true,
  "save_path": ".mcp-browser-artifacts/traces/trace.json"
}
```

## Output minimo

```json
{
  "traceFile": ".mcp-browser-artifacts/traces/trace-20260525-103000.json",
  "durationMs": 4200,
  "summary": {
    "navigationUrl": "http://localhost/app/report.cfm",
    "requestCount": 84,
    "longTaskCount": 3
  }
}
```

## Test di verifica Step 7

### Test A - trace base

1. Selezionare una tab con pagina locale.
2. Chiamare:

```json
{
  "action": "performance_start_trace",
  "reload": true
}
```

3. Dopo caricamento, chiamare:

```json
{
  "action": "performance_stop_trace"
}
```

Verifica:

- creato file trace sotto `.mcp-browser-artifacts/traces/`;
- output contiene path;
- file non è vuoto.

### Test B - summary

```json
{
  "action": "performance_summary"
}
```

Verifica:

- restituisce almeno durata, URL, conteggio richieste;
- se non esiste trace attivo o completato, errore controllato.

## Reinstall MCP

Non necessario se implementato tramite CDP/Playwright già presenti.

Se si decide di integrare Lighthouse o librerie esterne per analisi avanzate, sarà necessario modificare `package.json`, rieseguire `setup.ps1` o `setup.sh` e riavviare MCP.

---

# Step 8 - Allineamento documentazione, routing hook e guidance conversazionale

## Priorità

Finale obbligatorio.

## Obiettivo

Aggiornare documentazione esistente e skill operative in modo che Codex, Copilot e Antigravity usino correttamente le nuove feature.

## File da aggiornare

### README principale

File:

```text
README.md
```

Aggiungere sezione:

```text
Playwright MCP con Chrome CDP attach
```

Contenuti: indicare il comando ufficiale di avvio Chrome con profilo dedicato `"C:\Program Files\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9222 --remote-debugging-address=127.0.0.1 --user-data-dir="%LOCALAPPDATA%\ChromeMcpProfile"` e specificare che l'uso corretto prevede un prompt in cui l'utente dichiara di avere Chrome già aperto.

### Skill browser automation

File:

```text
skills/mcp-browser-automation/SKILL.md
```

Aggiornare workflow consigliato:

```text
1. Se l'utente dice che Chrome è già aperto sulla pagina in errore, non navigare da capo.
2. Verificare le capability con get_capabilities o get_browser_status.
3. Se attach_cdp è disponibile, chiamare browser_session action=attach_cdp.
4. Usare list_pages o select_page_by_url/title.
5. Usare debug_bundle come prima chiamata diagnostica.
6. Solo se serve interagire, usare snapshot e UID.
7. Per errori backend/AJAX, usare network_list/network_get/network_save_body.
8. Per errori JS, usare console_list/console_get.
9. Per tornare al browser interno, chiamare use_launch_browser.
```

Integrare inoltre una sezione di guidance conversazionale con esempi di prompt naturali e comportamento atteso dell'agente:

- se il prompt dice solo che Chrome è già aperto o che non bisogna ripartire da zero, l'agente deve preferire il percorso CDP attach;
- se l'utente fa un primo prompt per il collegamento e un secondo prompt dopo un'azione manuale, l'agente deve continuare a lavorare sulla stessa tab/sessione e leggere gli eventi post-attach;
- se il prompt non menziona Chrome già aperto, resta valido il flusso Playwright standard con browser lanciato da zero.

### Riferimenti skill

File suggerito:

```text
skills/mcp-browser-automation/references/test-patterns.md
```

Aggiungere pattern:

- debug pagina già aperta;
- attach CDP on-demand da prompt utente;
- workflow conversazionale in due prompt (collegamento, azione utente, debug successivo);
- debug AJAX 500;
- debug form legacy con UID;
- debug iframe;
- gestione artifact di rete.

### Hook di routing conversazionale

File:

```text
scripts/hooks/sophia-user-prompt-submit.mjs
```

Aggiornare euristiche e hint per intercettare in modo più affidabile prompt naturali come:

- "ho Chrome già aperto";
- "non navigare da capo";
- "pagina già aperta";
- "adesso mostrami console/network/debug".

Vincoli per il potenziamento hook:

- gli hook devono restare solo suggerimenti e non eseguire operazioni;
- non devono imporre sempre il percorso CDP: se il prompt non parla di Chrome già aperto, il flusso `launch` resta il default;
- gli hint devono orientare l'agente a usare `mcp-browser-automation` e a preservare il contesto della tab già aperta, non a elencare istruzioni MCP troppo verbose all'utente finale.

### Capability matrix

File:

```text
docs/server-capability-matrix.md
```

Aggiornare riga `playwright-node` con:

- CDP attach;
- tab selection;
- debug bundle;
- network body artifact;
- console stack;
- snapshot UID;
- performance trace base.

## Esito atteso Step 8

Al termine di questo step, la piattaforma deve avere una guidance coerente su tre livelli:

- documentazione sintetica per l'utente che spiega quando dichiarare che Chrome è già aperto;
- skill browser che descrive il workflow corretto senza richiedere prompt eccessivamente tecnici;
- hook di routing che riconoscono meglio i prompt naturali e suggeriscono il percorso CDP attach solo quando il contesto lo richiede.

## Test di verifica Step 8

Questi test sono **manuali lato utente/developer** (non automatici lato server):

### Copertura smoke vs manuale

Coperti già da smoke automatici (non serve rifarli a mano salvo regressioni sospette):

- attach CDP + selezione tab base (`attach_cdp`, `list_pages`, `select_page_by_url`);
- `debug_bundle` base;
- `console_list`/`console_clear` con eventi generati in test;
- `network_list`/`network_get`/`network_clear` base;
- `snapshot` + `fill_uid` + `click_uid`;
- `performance_start_trace`/`performance_stop_trace`/`performance_summary` base.

Punti non coperti completamente da smoke (da testare manualmente):

- raccolta `network_list`/`console_list` su pagina reale già aperta dall'utente (eventi post-attach reali);
- `network_save_body` su chiamata reale (file artifact e contenuto);
- `console_get` con `include_stack=true` su errore reale pagina;
- `hover_uid` e `press_uid` su UI reale.

### Test A - Setup locale Chrome CDP

Azioni da fare:

1. Avviare Chrome con comando ufficiale:
   `"C:\Program Files\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9222 --remote-debugging-address=127.0.0.1 --user-data-dir="%LOCALAPPDATA%\ChromeMcpProfile"`.
2. Verificare endpoint CDP:
   `curl http://127.0.0.1:9222/json/version`
3. Aprire manualmente una pagina applicativa reale in Chrome.

Esito atteso:

- endpoint CDP risponde HTTP 200;
- risposta contiene campi JSON tipo `Browser` / `webSocketDebuggerUrl`;
- pagina applicativa resta aperta e pronta per attach MCP.

### Test B - Prompt operativo attach on-demand

Prompt di test (da usare con Codex/Copilot):

```text
Ho Chrome MCP già aperto sulla pagina in errore. Non navigare da capo.
Seleziona la tab con URL che contiene {test} e raccogli debug bundle, console e network.

Verifiche da effettuare:

- l'agente non chiama `navigate` come prima azione;
- l'agente chiama `browser_session action=attach_cdp`;
- l'agente chiama `list_pages` o `select_page_by_url/title`;
- l'agente seleziona la tab corretta;
- l'agente chiama `debug_bundle`;
- l'agente usa `network_list` o `console_list` per effettuare test.

Esito atteso:

- workflow rispettato senza navigazione forzata;
- diagnosi iniziale raccolta da tab già aperta;
- output coerente con lo stato reale della pagina già autenticata.
```

### Test B2 - Routing naturale "Chrome già aperto"

Questo test serve a verificare che la guidance aggiornata funzioni anche con un prompt non tecnico, senza nominare `attach_cdp`.

Prompt di test:

```text
Ho Chrome già aperto sulla pagina in errore. Non ripartire da zero e usa la tab che sto già guardando.

Verifiche da effettuare:

- l'hook/routing suggerisce il percorso browser corretto senza chiedere all'utente termini tecnici MCP;
- l'agente interpreta il prompt come riuso di Chrome esistente, non come avvio di nuova navigazione;
- la guidance skill resta coerente con il comportamento osservato.

Esito atteso:

- riconoscimento corretto del caso "Chrome già aperto" a partire da prompt naturale;
- nessuna regressione del flusso Playwright standard quando questo indizio non è presente.
```

### Test C - Eventi post-attach per console/network

Questo test è **manuale lato utente/developer** e serve a verificare che `console_list` e `network_list` raccolgano eventi generati **dopo** `attach_cdp`.

Nota operativa importante: per avere traccia utile di network/console su Chrome già aperto, il flusso corretto è:

1. prompt iniziale di attach/selezione tab;
2. azione utente o azione agente che genera gli eventi (click, submit, fetch, console.error);
3. nuovo prompt/chiamata per leggere `network_list` e `console_list`.

Se non si genera nessun evento dopo attach, è normale ottenere liste vuote.

Azioni da fare:

1. Aprire Chrome con comando ufficiale e portarsi sulla pagina target.
2. Chiedere all'agente di collegarsi a Chrome già aperto, selezionare la tab corretta e poi:
   - generare un evento console (esempio: `evaluate_js` con `console.error("mcp test console")`);
   - generare una chiamata di rete (esempio: click su azione che fa XHR/fetch, oppure `evaluate_js` con una `fetch` verso endpoint applicativo noto).
3. Chiamare `console_list` e `network_list`.

Prompt di test:

```text
Ho Chrome MCP già aperto sulla pagina in errore. Non navigare da capo.
Collegati alla tab con URL che contiene {test}.
Poi genera un console.error di test e una chiamata fetch di test, quindi mostrami console_list e network_list.

Verifiche da effettuare:

- `console_list.total` > 0 e presenza della entry con testo test (`mcp test console`);
- `network_list.total` > 0 con almeno una entry XHR/fetch o document coerente con l'azione eseguita;
- i timestamp degli eventi sono successivi all'`attachedAt`/`capturedSince`.

Esito atteso:

- `console_list` e `network_list` non vuoti dopo la generazione esplicita di eventi;
- output coerente con eventi realmente prodotti in tab dopo attach.
```

### Test C2 - Workflow conversazionale a due prompt

Questo test verifica il caso d'uso reale più probabile: primo prompt di collegamento, azione utente manuale, secondo prompt di debug.

Prompt 1:

```text
Ho Chrome già aperto sulla pagina in errore. Non navigare da capo e collegati alla tab corretta.
```

Azione intermedia:

- l'utente esegue manualmente il click o il submit che genera errore, network o console.

Prompt 2:

```text
Ho appena eseguito l'azione nella stessa tab. Adesso mostrami debug bundle, console e network relativi a quello che è appena successo.

Verifiche da effettuare:

- il secondo prompt non fa ripartire il flusso da zero su browser nuovo;
- l'agente continua a usare la sessione/tab già selezionata;
- `console_list` e `network_list` vengono letti come eventi post-attach coerenti con l'azione appena eseguita.

Esito atteso:

- guidance conversazionale sufficiente anche senza prompt tecnici dettagliati;
- corretto riuso della sessione Chrome già aperta nel flusso in due tempi.
```

### Test D - Salvataggio body reale (`network_save_body`)

Azioni da fare:

1. Dopo il Test C, identificare un `request_id` valido in `network_list`.
2. Chiedere all'agente di eseguire `network_save_body` sul `request_id` scelto (`body=request` o `body=response`).
3. Verificare il file artifact restituito.

Prompt di test:

```text
Usa l'ultima chiamata presente in network_list e salva il body response su file con network_save_body.
Mostrami request_id usato, path file e dimensione.

Verifiche da effettuare:

- `network_save_body` ritorna `filePath` valorizzato;
- il file esiste sotto `.mcp-browser-artifacts/network` (o path esplicito);
- il contenuto è coerente con la richiesta/risposta selezionata.

Esito atteso:

- body salvato correttamente su file senza dump completo inline.
```

### Test E - Stack reale (`console_get include_stack=true`)

Azioni da fare:

1. Generare un errore JS runtime reale in pagina (o `pageerror` via script test).
2. Chiamare `console_list` filtrando `types=["pageerror"]`.
3. Scegliere un `id` e chiamare `console_get` con `include_stack=true`.

Prompt di test:

```text
Genera un errore JavaScript di test nella tab corrente, poi mostrami console_list filtrato pageerror.
Prendi l'id trovato e chiamami console_get con include_stack=true.

Verifiche da effettuare:

- `console_list` contiene almeno una entry `pageerror`;
- `console_get` su quell'id restituisce `stack` non nullo (quando disponibile dal browser);
- testo errore e stack sono coerenti tra loro.

Esito atteso:

- diagnostica JS completa disponibile con dettaglio stack.
```

### Test F - Interazioni UID non coperte da smoke (`hover_uid`, `press_uid`)

Azioni da fare:

1. Eseguire `snapshot` e identificare UID adatti (campo/focus target, bottone/menu).
2. Eseguire `hover_uid` su un elemento che mostra stato hover/tooltip/menu.
3. Eseguire `press_uid` con tasto significativo (es. `Enter`, `Escape`, `ArrowDown`) su elemento focusabile.

Prompt di test:

```text
Fai snapshot della tab corrente, individua un elemento adatto e prova hover_uid.
Poi usa press_uid con Enter (o tasto coerente) e mostrami l'effetto osservato.

Verifiche da effettuare:

- `hover_uid` eseguito senza errore su UID valido;
- `press_uid` eseguito senza errore su UID valido;
- stato UI coerente con l'azione (focus, apertura menu, submit, chiusura modal, ecc.).

Esito atteso:

- comandi UID avanzati utilizzabili in scenari reali oltre a `click_uid`/`fill_uid`.
```

## Reinstall MCP

Non necessario per aggiornamento documentazione e skill.

Se vengono modificati generatori di config MCP o script di setup, rieseguire `setup.ps1` o `setup.sh`.

---

# Checklist finale di accettazione

## Funzionale

- [ ] MCP si collega a Chrome già aperto su `http://127.0.0.1:9222`.
- [ ] L'attach avviene on-demand tramite `browser_session action=attach_cdp`, non automaticamente solo perché `PLAYWRIGHT_CDP_URL` è configurato.
- [ ] La skill/browser workflow istruisce l'agente a usare `attach_cdp` quando il prompt parla di Chrome già aperto.
- [ ] Gli hook, se presenti, restano reminder non esecutivi.
- [ ] Gli hook riconoscono anche prompt naturali tipo "Chrome già aperto" e "non navigare da capo".
- [ ] La guidance conversazionale copre esplicitamente il workflow in due prompt: collegamento iniziale, azione utente, debug successivo.
- [ ] MCP non naviga da capo quando l'utente chiede debug della tab esistente.
- [ ] In modalità CDP, MCP non chiude automaticamente tab aperte dall'utente.
- [ ] `list_pages` mostra più tab.
- [ ] `select_page_by_url` e `select_page_by_title` gestiscono match singolo, nessun match e match multiplo.
- [ ] `debug_bundle` produce diagnosi sintetica senza output enorme.
- [ ] `debug_bundle` dichiara `capturedSince` e non promette eventi precedenti all'attach.
- [ ] `network_list` mostra XHR/fetch/document e HTTP error.
- [ ] `network_save_body` salva request/response body su file.
- [ ] `console_list` mostra errori con location e stack dove disponibile.
- [ ] `snapshot` produce UID usabili per click/fill.
- [ ] artifact locali sono ignorati da Git.

## Sicurezza

- [ ] Default CDP limitato a `127.0.0.1`.
- [ ] Documentazione sconsiglia profilo Chrome personale.
- [ ] La modalità persistente usa un profilo Chrome dedicato, non quello personale.
- [ ] Header sensibili redatti in network output.
- [ ] Cookie e authorization non stampati inline.
- [ ] Body grandi salvati su file, non restituiti interamente nei token.

## Compatibilità

- [ ] Workflow Playwright headless esistente continua a funzionare senza `PLAYWRIGHT_CDP_URL`.
- [ ] `browser_session` e `browser_interact` restano compatibili con le azioni esistenti.
- [ ] Le nuove action restituiscono `content` breve e `structuredContent` stabile dove utile.
- [ ] Iframe workflow non regredisce.
- [ ] Download workflow non regredisce.

## Setup

- [ ] Nessuna nuova dipendenza salvo decisione esplicita.
- [ ] Se `package.json` cambia, documentare reinstall e rieseguire setup.
- [ ] README aggiornato.
- [ ] Skill aggiornata.
- [ ] Capability matrix aggiornata.

---

# Riferimenti tecnici

- ChromeDevTools MCP README: https://github.com/ChromeDevTools/chrome-devtools-mcp
- ChromeDevTools MCP tool reference: https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/docs/tool-reference.md
- Chrome remote debugging security change: https://developer.chrome.com/blog/remote-debugging-port
- Playwright `connectOverCDP`: https://playwright.dev/docs/api/class-browsertype#browser-type-connect-over-cdp
