Skip to main content
Glama
README.md
# push_contempt_mcp

Server MCP in Python (SDK `mcp` 2.x, classe `MCPServer`) per gestire un
catalogo WooCommerce da un assistente AI (Claude Code e altri client MCP),
in coppia con il plugin WordPress `push_contempt` (repo separato). La
maggior parte dei tool sono client sottili della REST API di quel plugin
(nessun accesso diretto al DB, funziona anche su hosting che blocca
connessioni MySQL esterne); un numero ridotto di operazioni passa invece
da automazione browser (Playwright) su wp-admin, quando serve poter
rivedere una modifica prima che diventi effettiva.

Per l'installazione completa (plugin + server + collegamento con
l'assistente AI, in ordine) vedi [`HOWTO.md`](HOWTO.md).

## Configurazione

Copia `.env.example` in `.env` e compila:

- `WP_SITE_URL` — URL del sito WordPress.
- `WP_APP_USER` / `WP_APP_PASSWORD` — Application Password di WordPress
  (wp-admin → Utenti → Profilo → Password per le applicazioni), usata dalla
  maggior parte dei tool per chiamare la REST API del plugin
  `push_contempt`. Il plugin la genera in automatico dal tab
  "Impostazioni" (un click, non serve WP-CLI). Le Application Password
  native di WordPress non scadono da sole.
- `WP_ADMIN_USER` / `WP_ADMIN_PASSWORD` — credenziali di login wp-admin,
  usate solo dai tool che passano da automazione browser (modifica prezzo
  singolo con anteprima, SEO categoria). Il browser Playwright gira in
  modalità **headed** (non headless): su alcuni host la build Chromium
  "fallback" (OS non ufficialmente supportato) ha problemi di rendering in
  headless, vedi i commenti in `browser_tools.py`. Serve quindi un display
  disponibile (`DISPLAY`).

  **Sessione e captcha:** il login passa da una sessione salvata in
  `.auth/wp_admin_state.json` (permessi 600, in `.gitignore`): finche' il
  cookie e' valido non si rifa' il login, nemmeno dopo un riavvio del server.
  Se serve un nuovo login, la somma Jetpack Protect viene risolta da sola,
  mentre il reCAPTCHA "Non sono un robot" va spuntato a mano nella finestra
  del browser (si aspetta fino a 2 minuti).

## Versioni

Il server e il [plugin push_contempt](https://github.com/ness1-contempt/push_contempt) hanno **lo stesso numero di
versione** e lo cambiano insieme a ogni pubblicazione (stesso tag, per esempio `v0.1.14` su entrambi). Per
funzionare bene usa le due parti con lo stesso numero: se un tool risponde che una rotta non esiste, il plugin
sul sito è più vecchio del server. Il server si aggiorna con `git pull`, il plugin reinstallando lo zip della
pagina Releases.

## Tool disponibili

**Catalogo**
- `lista_prodotti` — ricerca testuale nel catalogo: titolo, sku, scorte, tipo
  (simple, variable, grouped, external) e prezzo. Un prodotto variabile o
  raggruppato non ha un prezzo solo: `prezzo` è null e si leggono `prezzo_min` e
  `prezzo_max` (le varianti in vendita), più `numero_varianti`. Gli stessi campi sono
  in `leggi_seo_prodotto`.
- `controllo_salute_catalogo` — prodotti pubblicati ma incompleti (nessuna
  immagine in evidenza, nessuna categoria, prezzo non valido, descrizione
  troppo corta).
- `prodotti_sotto_scorta` — prodotti esauriti o sotto una soglia di scorte. Di un prodotto
  variabile compaiono anche le singole varianti esaurite o sotto soglia (`variante`,
  `variante_id`); quelle che ereditano le scorte dal padre non si ripetono.
- `report_vendite_prodotti` — più venduti e mai venduti in un periodo.

**Ordini** (sola lettura, richiedono il plugin ≥ della versione che include
`/ordini`)
- `lista_ordini` — ordini filtrabili per stato WooCommerce (`pending`,
  `processing`, `on-hold`, `completed`, `cancelled`, `refunded`, `failed`; non
  esiste uno stato "evaso") e per finestra temporale (`ultime_ore`,
  `piu_vecchi_di_ore`, `da`/`a`). L'elenco non contiene dati personali oltre
  alla città di spedizione.
- `dettaglio_ordine` — un singolo ordine con righe, importi, date e dati del
  cliente (nome, email, telefono, indirizzi).

**Creazione prodotti**
- `scansiona_cartella_prodotti` — confronta una cartella locale con il catalogo e separa i
  prodotti nuovi da quelli già presenti. Le cartelle sono lo **specchio dell'albero delle
  categorie** di WooCommerce, a qualsiasi profondità (fino a 5 livelli): una cartella con foto o
  un `.txt` è un prodotto, le cartelle sopra di lei sono il suo `percorso_categoria`
  (`Indumenti/Magliette/Maglietta rossa/` = prodotto «Maglietta rossa» nella categoria Magliette,
  dentro Indumenti). Confronta i percorsi con le categorie del sito (sola lettura) e restituisce
  per ogni prodotto `piano_categorie` (per ogni livello: esiste / da_creare / conflitto), più
  `categorie_da_creare`, `conflitti_categorie` e `da_controllare` (cartelle dubbie).
- `crea_prodotto` — crea un prodotto WooCommerce **in bozza** (titolo,
  descrizione, prezzo, categoria o `percorso_categoria`, immagini: la prima diventa quella in
  evidenza, le altre vanno in galleria), con SEO e ALT delle immagini già
  impostati nella stessa chiamata. Non viene mai pubblicato in automatico:
  resta da rivedere e pubblicare da wp-admin (o con `pubblica_prodotti`, su
  richiesta e con la conferma dell'utente). La risposta include
  `avvisi_seo` (densità frase chiave, sottotitoli H2/H3, link interni) da
  controllare prima di considerare il prodotto pronto.
- `crea_prodotto_variabile` — come `crea_prodotto` ma per un prodotto **con varianti**
  (colori, memoria, taglie): una variante per ogni combinazione degli attributi, tutte
  allo stesso prezzo e senza scorte, sempre in bozza. Parte dall'**anteprima**
  (`anteprima=True`, predefinita): non scrive niente, nemmeno immagini o categoria, e
  mostra le varianti che nascerebbero. Solo con `anteprima=False` crea davvero. Due modi
  per dare gli attributi, vedi sotto.
- `prodotti_in_bozza` — elenco dei prodotti creati e non ancora pubblicati.
- **Blog** (articoli scritti dall'assistente, sempre in bozza): `leggi_brief_blog` e `salva_brief_blog`
  (argomento, pubblico, tono, lingua, lunghezza, categorie di prodotti, note e `ottimizza_aeo_geo`: attivo, il
  brief include `regole_aeo_geo`, le regole AEO/GEO contenute nel server), `contesto_blog` (negozio,
  prodotti con indirizzo e immagine, articoli già presenti), `lista_articoli_blog`, `leggi_articolo_blog`,
  `carica_immagine_blog` (immagine dell'utente con ALT), `crea_articolo_bozza` e `aggiorna_articolo_bozza`.
  Le due ultime restituiscono `avvisi` sui controlli on-page (frase chiave, H2, titolo SEO e meta description,
  link interni ed esterni, lunghezza rispetto al brief, segnaposto). Un titolo già usato viene rifiutato e un
  articolo pubblicato non si modifica da qui. Il server istruisce l'assistente a usare solo informazioni
  verificabili e a non pubblicare mai.
- `pubblica_prodotti` — pubblica bozze (max 50 per volta). **Azione visibile a tutti, sempre in due
  tempi:** l'anteprima (predefinita) non pubblica niente, elenca cosa verrebbe pubblicato con gli
  avvisi (niente immagine, SEO vuota...) e cosa no con i motivi (niente prezzo, varianti sbagliate),
  e restituisce un `codice_conferma`; la pubblicazione vera lo richiede e il plugin la rifiuta se
  anche un solo prodotto è cambiato dopo l'anteprima.
- `lista_categorie_prodotti` — l'albero delle categorie con id, percorso, livello, prodotti e indirizzo.
- `crea_categoria_prodotti` — crea una categoria da sola (senza prodotti), alla radice o sotto
  un'altra: serve quando la categoria deve esistere prima (per esempio «Indumenti» prima di
  spostarci sotto «Magliette»). Anteprima di default. Un nome esiste una volta sola nell'albero (se
  c'è già, anche in un altro ramo, rifiuta e dice dove), al massimo 5 livelli. Una categoria vuota non
  è visibile a nessuno, quindi non serve il codice di conferma.
- `sposta_categoria_prodotti` — sposta una categoria (con le sottocategorie) sotto un'altra o alla
  radice. Stessa regola: anteprima con percorso e indirizzo prima/dopo, prodotti coinvolti e avvisi
  (WordPress non crea il redirect 301: il vecchio indirizzo resta raggiungibile con canonical sul nuovo),
  poi l'azione vera solo con il `codice_conferma`.

**Categorie: l'albero delle cartelle**
- Con `percorso_categoria` (es. `["Indumenti", "Magliette"]`) le categorie che mancano si creano
  **una dentro l'altra**, quelle che esistono già si riusano (il confronto ignora maiuscole e
  spazi) e il prodotto va **solo nella categoria più in basso**: quelle sopra lo mostrano
  comunque. Senza `percorso_categoria` vale la `categoria` singola di sempre.
- **Un nome di categoria esiste una volta sola in tutto l'albero.** Se sul sito c'è già una
  categoria con quel nome in un altro punto (la cartella vuole `Indumenti > Magliette`, il sito ha
  `Abbigliamento > Magliette`), o ce n'è più di una, o lo stesso nome compare in due rami delle
  cartelle, **non si crea niente** (né prodotto né categorie): il plugin risponde con un errore 409
  che dice dove sta la categoria e dove la vorrebbe la cartella, e la scansione lo segnala già in
  `problemi_categorie`. Va sistemato a mano (spostando la cartella o la categoria), non aggirato.
- Cartelle dubbie, non caricate e riportate in `da_controllare`: una cartella con foto o testo **e**
  sottocartelle con altri prodotti, e un prodotto fuori da una cartella di categoria.

**Prodotti variabili: i due modi**
- **Preciso** (`riga_varianti`): nel file di testo del prodotto una riga sola che comincia
  con `varianti:`, con gli attributi separati da `|` e i valori da virgola:
  `varianti: Colore: Nero, Bianco | Memoria: 256 GB, 512 GB` (4 varianti). La legge il
  server senza interpretarla (`scansiona_cartella_prodotti` la restituisce già letta in
  `varianti`); se è scritta male dà un errore chiaro. I nomi e i valori non possono
  contenere `|` né virgole.
- **Automatico** (`attributi`): se la riga non c'è, l'assistente ricava gli attributi dal testo
  libero. È comodo ma **può sbagliare**: leggere male un valore, inventarne uno, scambiare una
  frase descrittiva per una variante. Per questo l'assistente deve usare solo valori scritti
  davvero, chiedere se il testo è ambiguo e **mostrarti l'anteprima aspettando il tuo ok** prima
  di creare. Controllala sempre.
- Regole comuni, controllate dal plugin prima di scrivere: al massimo 4 attributi e 50 varianti,
  niente valori doppi o vuoti, prezzo numerico maggiore di zero con il punto come decimale. Se
  qualcosa fallisce a metà, il plugin toglie quello che aveva già creato.
- Prezzi o scorte diversi per variante e foto per variante non sono gestiti: si cambiano dopo
  da wp-admin.

**Prezzi**
- `calcola_aggiornamento_prezzi` / `applica_aggiornamento_prezzi` —
  aggiornamento prezzi in blocco per categoria (percentuale o delta fisso),
  con anteprima da far approvare prima di scrivere.
- `prepara_modifica_prezzo_prodotto` / `conferma_modifica_in_corso` /
  `annulla_modifica_in_corso` — modifica prezzo di un singolo prodotto in
  due fasi via browser (compila e mostra uno screenshot da controllare,
  poi salva o scarta).

**SEO**
- `lista_stato_seo_prodotti` — vista d'insieme: titolo SEO e meta
  description impostati o mancanti su tutto il catalogo.
- `leggi_seo_prodotto` / `leggi_seo_prodotti_selezionati` /
  `aggiorna_seo_prodotto` — lettura/scrittura dei campi Yoast (titolo SEO,
  meta description, parola chiave focus) di uno o più prodotti. Solo I/O:
  l'ottimizzazione dei testi la fa chi chiama i tool. Il plugin tiene
  base e precedente di ogni prodotto (`origine` dice chi ha scritto).
- `ripristina_seo_prodotti` — rimette i campi Yoast di uno o più prodotti come nella
  **base** (punto di partenza, di norma lo stato originale di Yoast) o nel **precedente**
  (la versione sostituita dall'ultima scrittura); la versione lasciata diventa il precedente,
  quindi si può tornare indietro. I prodotti mai modificati non si toccano. Ridefinire la base
  non è un tool: lo fa solo l'utente dalla tab SEO.
- `leggi_descrizione_prodotto` / `aggiorna_descrizione_prodotto` — descrizione lunga e
  breve di un prodotto (HTML filtrato come in un articolo WordPress). Restituisce
  prima/dopo; il plugin tiene base e precedente come per la SEO.
- `ripristina_descrizione_prodotti` — rimette descrizione e descrizione breve come nella
  **base** o nel **precedente**; la versione lasciata diventa il precedente. I prodotti mai
  modificati con questi tool non si toccano.
- `leggi_alt_immagini_prodotto` / `aggiorna_alt_immagini_prodotto` — testo ALT
  dell'immagine in evidenza e della galleria, con la parola chiave focus Yoast a
  portata di mano. In scrittura si indicano gli `allegato_id` da cambiare; gli
  allegati che non sono del prodotto vengono rifiutati.
- `leggi_seo_varianti` / `aggiorna_seo_varianti` — SEO dei prodotti **variabili**: attributi,
  varianti (prezzo, SKU, immagine, descrizione) e i problemi trovati, dal più grave; in
  scrittura descrizione e/o SKU delle varianti (rifiuta varianti non del prodotto e SKU già
  in uso, con `solo_anteprima` non salva). Le immagini delle varianti si trattano con gli
  strumenti ALT (ruolo «variante»). Le regole per l'assistente e i dati strutturati
  `ProductGroup` che genera il plugin sono nella wiki, pagina «SEO dei prodotti variabili».
- `analizza_link_prodotto` — i link nella descrizione di un prodotto, divisi in interni, esterni
  e altri (ancore, mailto, tel), con `ha_link_in_uscita`: la verifica che Yoast segnala più
  spesso come mancante. Sola lettura.
- `analizza_link_prodotti` — la stessa verifica su tutti i prodotti in una chiamata: per ciascuno
  i totali di link interni ed esterni, più i conteggi complessivi (quanti senza link in uscita,
  quanti senza link interni); con `solo_senza_link_in_uscita` l'elenco è solo di quelli a cui
  manca. Per gli indirizzi di un prodotto si usa `analizza_link_prodotto`.
- `aggiorna_seo_categoria` — stessa cosa per le categorie prodotto
  (tassonomia `product_cat`), via browser: per i termini di tassonomia
  Yoast non rilegge i campi scritti via REST/DB diretto (vedi Note).
- `analizza_seo_sito` — analisi a campione del sito di un altro negozio per
  capire come è fatta la sua SEO e come descrive i prodotti (home, sitemap,
  qualche categoria, una decina di prodotti). Oltre ai campi SEO (titoli,
  meta description, H1/H2, dati strutturati) analizza il testo: vocabolario
  (parole e coppie di parole ricorrenti, unità di misura), segnali di stile
  e intenzione ogni 1000 parole (inviti all'acquisto, fiducia e servizio,
  offerte, confronto e guida, tono "tu"/"noi", tecnicismo, lunghezza delle
  frasi), struttura delle descrizioni e un breve estratto per pagina
  (`caratteri_per_pagina`, 600 di default: è quello che pesa sui token), così
  l'assistente giudica intenzione e tono. Le formule ripetute su tutto il sito
  (spedizioni, resi, "aggiungi al carrello") sono separate dai contenuti. Gira
  sul tuo PC, non sul sito WordPress; rispetta `robots.txt`, fa una pausa fra le
  richieste. Se l'indirizzo punta a una sezione (es. `https://sito.it/telefonia/`)
  l'analisi resta dentro quella sezione (`perimetro`); se punta a una pagina di categoria
  (`.../smartphone-1001.html`) il campione viene dai link di quella pagina, senza sitemap. Metodi, dal più leggero: HTTP; se la home risponde 403/429 o è
  costruita con JavaScript e il browser è attivo (`SEO_ANALISI_USA_BROWSER=1` o
  `usa_browser`), dopo aver aspettato il `Retry-After` si passa a un Chromium vero in
  locale; se non basta, `analizza_pagine_salvate`. Con `browser_visibile` (o
  `SEO_ANALISI_BROWSER_VISIBILE=1`) il browser si apre con la finestra visibile: alcuni siti
  rifiutano quello senza finestra. È un tentativo in più dopo un rifiuto, da usare con il
  consenso dell'utente. Il report dice il `metodo` usato.
  Con `usa_curl` (scelta esplicita dell'utente) fa
  le richieste con il programma `curl`, sempre con il nostro User-Agent dichiarato; se
  il sito risponde ancora 429 si ferma, non aggira i blocchi. Rifiuta reti private; per provarlo su un
  sito locale `SEO_ANALISI_CONSENTI_RETE_PRIVATA=1`. Vocabolario ed estratti
  servono ad analizzare: i testi nostri vanno scritti con parole proprie.
- `analizza_pagine_salvate` — la stessa analisi su pagine `.html` salvate
  dall'utente dal proprio browser (file o cartelle), senza nessuna richiesta
  di rete: per i siti che bloccano gli strumenti automatici o si costruiscono
  con JavaScript.
- `controlla_somiglianza` — misura quanto un testo nostro somiglia ai testi di
  un sito analizzato con `analizza_seo_sito` (salvati in locale dopo ogni
  analisi) o a testi dati a mano, su una scala da 0 (nessuna somiglianza) a 9
  (copia). Si basa su sequenze di almeno 5 parole consecutive in comune e
  restituisce punteggio, significato, passaggi coincidenti e fonti più simili.
  La `soglia` (0-9) è il massimo che l'utilizzatore accetta: con essa il
  risultato dice anche se il testo è accettabile. Specifiche tecniche uguali
  alzano un po' il punteggio anche in testi originali, per questo i passaggi
  sono elencati e il giudizio finale resta all'utilizzatore. Il punteggio è solo
  un riferimento, non vincolante: va mostrato all'utente e il livello accettato
  scelto da lui prima di applicare qualsiasi modifica ai prodotti.
- `rimuovi_analisi_sito` — toglie un sito dalla tabella «Concorrenti e confronto» della tab SEO
  (con il suo storico) e, di norma, ne cancella anche i testi salvati in locale; si usa solo su
  richiesta dell'utente, per esempio quando un report è inutile. Dopo, `controlla_somiglianza` non
  confronta più con quel sito finché non lo si analizza di nuovo. L'utente può anche togliere
  la riga a mano dalla tab («Togli dalla lista»), che però lascia i testi locali.
- Il riepilogo di ogni valutazione (punteggi per area con la prova dei criteri, mai i testi
  delle pagine) viene mandato al plugin (`POST /concorrenti`) e compare nella sezione
  "Concorrenti e confronto" della tab SEO; se l'indirizzo è quello di `WP_SITE_URL` è marcato
  come "Il tuo sito" e gli altri mostrano la differenza. Non blocca mai l'analisi (il report ha
  un campo `plugin`); `SEO_ANALISI_SALVA_NEL_PLUGIN=0` lo disattiva.
- La `valutazione` che `analizza_seo_sito` e `analizza_pagine_salvate` aggiungono al report
  (modulo `seo_scorecard.py`) dà alla struttura SEO del sito analizzato un punteggio da 0 a 9
  per area (titoli, meta description, intestazioni, dati strutturati, contenuto, aspetti
  tecnici) e complessivo, con la prova di ogni criterio. Dice se il sito è un buon modello
  da cui prendere spunto: è solo un riferimento, e un sito scadente può essere buono su
  un'area. Le soglie (lunghezze di titolo e meta, parole minime) sono costanti in testa al
  modulo. Alcuni difetti gravi pongono un **tetto** al voto dell'area (ALT mancante a
  scaglioni: oltre il 15% delle immagini contenuto al massimo 8, oltre il 25% 7, oltre il 35% 6,
  oltre il 50% 5, oltre il 75% 3; H3 senza H2 su più
  della metà delle pagine: intestazioni al massimo 6); il report mostra ogni tetto con la sua
  misura e il suo effetto. Regole decise con l'utente: una meta description vale in base a
  quanto aggiunge al titolo (testo proprio 1; titolo più frase generica uguale su più della
  metà delle pagine, o su almeno 3 pagine dello stesso tipo, 0,5; solo il titolo, frase fissa
  uguale su più della metà delle pagine o scheda incollata 0; con meno di 5 meta utili,
  lunghezza e unicità pesano in proporzione); gli H1 contano a livelli (uno solo 1; più H1 uguali 0,6; più H1 diversi 0,3;
  nessuno 0). Prima di proporre testi, all'utente vanno mostrati punteggio e aree, e lui decide
  se e per quali aree usare il sito come ispirazione.

**Manutenzione**
- `purga_cache_sito` — invalida manualmente la cache del sito (gli altri
  tool di scrittura la chiamano già in automatico dove serve).

## Comandi

| Cosa | Comando |
|---|---|
| Installare/aggiornare le dipendenze | `uv sync` |
| Avviare il server (stdio) | `uv run push_contempt_mcp` |
| Test | `uv run pytest` |
| Lint / format | `uv run ruff check --fix . && uv run ruff format .` |
| Provarlo con MCP Inspector (UI web) | `npx -y @modelcontextprotocol/inspector uv run push_contempt_mcp` |
| Aggiungere una dipendenza | `uv add <pacchetto>` |

## Struttura

- `src/push_contempt_mcp/server.py` — registrazione dei tool, resource e prompt MCP
- `src/push_contempt_mcp/db.py` — connessione MySQL diretta, usata solo dove serve davvero (lettura veloce, scansione cartella)
- `src/push_contempt_mcp/product_tools.py` — creazione prodotto: client REST + upload immagini su `/wp/v2/media`
- `src/push_contempt_mcp/pricing_tools.py` — aggiornamento prezzi in blocco: client REST
- `src/push_contempt_mcp/inventory_tools.py` — scorte: client REST
- `src/push_contempt_mcp/order_tools.py` — ordini: client REST
- `src/push_contempt_mcp/sales_tools.py` — report vendite: client REST
- `src/push_contempt_mcp/catalog_health.py` — salute catalogo: client REST
- `src/push_contempt_mcp/seo_tools.py` — lettura/scrittura campi SEO Yoast: client REST
- `src/push_contempt_mcp/competitor_seo.py` — scarico e campione di pagine di un sito concorrente, campi SEO (solo libreria standard + `requests`)
- `src/push_contempt_mcp/seo_scorecard.py` — valutazione 0-9 per area della struttura SEO di un sito analizzato
- `src/push_contempt_mcp/text_similarity.py` — controllo di somiglianza 0-9 fra un nostro testo e quelli analizzati (cache locale dei testi dei concorrenti)
- `src/push_contempt_mcp/competitor_text.py` — analisi del testo di quelle pagine: template, vocabolario, segnali di stile e intenzione
- `src/push_contempt_mcp/product_sync.py` — scansione cartella prodotti locale (lettura diretta dal DB, solo confronto nomi)
- `src/push_contempt_mcp/browser_tools.py` — automazione Playwright per le operazioni ancora manuali (prezzo singolo con anteprima, SEO categoria)
- `src/push_contempt_mcp/cache_tools.py` — invalidazione cache del sito dopo una scrittura
- `src/push_contempt_mcp/__init__.py` — `main()`, entry point
- `tests/` — test in-process con `mcp.Client(mcp)` per il server, test unitari a `requests` mockato per i client REST
- `.mcp.json` — registra il server in Claude Code quando apri questa cartella

## Note

- Con trasporto stdio **non usare `print()`**: stdout è riservato al protocollo. Usa `logging`.
- SDK 2.x: `FastMCP` è stato rinominato `MCPServer` (`from mcp.server.mcpserver import MCPServer`).
  Molti esempi online sono ancora per la v1.
- Per Claude Desktop aggiungi lo stesso blocco di `.mcp.json` a `~/.config/Claude/claude_desktop_config.json`.
- Per i **termini di tassonomia** (categorie prodotto) `wp yoast index --reindex`
  non basta: l'indicizzabile Yoast del termine resta vuoto anche dopo, a
  prescindere da reindex/cache flush/`wp term update`. L'unico modo affidabile
  trovato è salvare davvero dalla UI di modifica categoria (da cui
  `aggiorna_seo_categoria`, via browser) — i campi titolo/meta
  description lì sono editor Draft.js, non `<input>`, vanno riempiti
  cliccando + selezionando tutto + ridigitando, non con `.fill()`.

TDQS

B3.4/5.0

Scored across 28 tools

Disambiguation3/5

Most tools map to distinct resources/actions, but there are two overlapping price-update paths (prepara_modifica_prezzo_prodotto + conferma/annulla via browser vs calcola/applica_aggiornamento_prezzi via DB), and leggi_seo_prodotto vs leggi_seo_prodotti_selezionati differ only by batch size. The junk tools somma and saluta add further confusion with no clear domain role. Descriptions do help disambiguate the main flows.

Naming Consistency4/5

Nearly all names are Italian snake_case with a readable verb_noun/noun_qualifier pattern (leggi_seo_prodotto, aggiorna_seo_prodotto, lista_ordini, dettaglio_ordine). Minor deviations are noun-only names like prodotti_in_bozza and prodotti_sotto_scorta, plus the generic somma/saluta. Overall consistent and predictable.

Tool Count3/5

28 tools is on the heavy side for the apparent scope, and the surface spans several sub-domains (SEO, products, pricing, orders, competitor analysis). Most tools earn their place, but somma and saluta are clearly extraneous filler that inflate the count. Borderline between well-scoped and bloated.

Completeness4/5

Strong coverage of the SEO and pricing lifecycle (read, write, restore snapshots, similarity checks, cache invalidation) plus order reporting. Minor gaps: no product delete/update-general tool, no category listing, and single-product detail is only via leggi_* rather than a full get. Core workflows are well covered.

Maintenance

ActivityMaintained
ResponsivenessNo recent community issues