# Audit → agent : prompt de correction + tool MCP `run_site_audit`

**Date :** 2026-07-07 · **Suite de :** ELEVA-93 (v1.1 différée) et du resync du même jour. Demande : « il manque la partie MCP pour que les gens puissent faire leurs changements — un copy MCP / copy-paste depuis l'audit ».

## Problème

`/flight-checker` diagnostique (fail/warn/manual/N/A) mais n'aide pas à **corriger**. L'utilisateur doit deviner quoi demander à son agent (Claude Code, Cursor…) et comment le brancher au MCP. Précédent produit : le générateur de prompt keywords (`src/lib/keywords/prompt/build-prompt.ts` + carte copy-paste) — même philosophie, appliquée à l'audit.

## Décisions

### 1. Tool MCP `run_site_audit(siteId)` — première mutation du serveur
- Enregistré dans `src/lib/mcp/server.ts`, implémentation org-scopée dans `src/lib/mcp/tools.ts` : le site doit appartenir à l'org de la clé (même jointure que `get_site_audit`), sinon « Site introuvable dans cette organisation. »
- Réutilise `fetchSiteSnapshot` + `runSiteAudit` + l'**upsert** une-ligne-par-site (invariant du 2026-07-07). L'upsert vit aujourd'hui dans `actions.ts` (`"use server"`, non importable par le MCP) → extraire un helper partagé `persistSiteAudit(siteId, result)` (module serveur simple, ex. `src/lib/spec/audit/persist.ts`) utilisé par l'action ET le tool. Site sans URL → message d'erreur clair, pas de throw transport.
- **Cooldown anti-boucle 60 s** : si `ranAt` du dernier audit < 60 s, renvoyer le résultat existant préfixé d'une note (« Audit récent (<60 s), résultat existant renvoyé ») — un agent qui boucle ne martèle ni le site cible ni la base. Pas de nouveau fetch dans ce cas.
- Sortie texte = même format que `get_site_audit` (score + verdicts par item). L'appel passe par le chokepoint auth + quota mensuel existant, rien de spécifique.
- Pas de `revalidatePath` (on n'est pas dans une Server Action) : l'UI voit le nouvel audit au prochain chargement de la page — acceptable.

### 2. Générateur de prompt — `src/lib/spec/audit/fix-prompt.ts` (pur, testé)
`buildAuditFixPrompt({ siteName, siteUrl, appUrl, audit, items })` → string. Sections :
- **Contexte** : site, URL, score, date du dernier audit.
- **Connexion MCP** : bloc `mcpServers` JSON avec `${appUrl}/api/mcp`, clé **placeholder** (`eli_xxxxxxxx…`) + rappel « créez votre clé dans Réglages → Clés API ». Jamais de clé réelle dans le prompt.
- **Points à corriger** : items **fail + warn** embarqués en détail (id, titre, statut spec, verdict, détail FR) + consigne `get_spec_topic(category, slug)` pour la marche à suivre.
- **À vérifier manuellement** : PAS d'embed des ~117 items (prompt illisible) — consigne d'appeler `get_site_audit(siteId)` puis `get_spec_topic` pour traiter les items `manual` applicables au code du site. Les **N/A** : ids seuls, « à évaluer si applicable ».
- **Boucle de vérification** : corriger → déployer → `run_site_audit(siteId)` → répéter jusqu'à zéro fail/warn (noter le cooldown 60 s).
- **Garde-fous** : modifications minimales, ne rien casser, un commit par point corrigé.
- Le `siteId` réel est embarqué (nécessaire aux tools) ; les données proviennent de ce que la page a déjà (audit + CHECKLIST), **pas de nouvelle server action**.

### 3. UI — bouton « Copier le prompt agent » sur `/flight-checker`
- Dans la carte résumé d'audit, à côté de « Relancer l'audit » (composant `src/components/flight-check/checklist.tsx` ou sous-composant dédié).
- Construction 100 % client à partir des props existantes (`initialAudit`/dernier résultat + items) ; `navigator.clipboard.writeText` + feedback (toast/état « Copié ») — même UX que la carte prompt keywords.
- Désactivé tant qu'aucun audit n'a tourné. Hint discret vers `/settings/api-keys` (« nécessite une clé API »).
- `appUrl` : même source que la page settings API keys (env/URL publique), passée en prop depuis le RSC.

## Hors périmètre v1
Bouton de copie par item, variantes de prompt par agent (un seul générique), embed de clé réelle, i18n du prompt (français, comme le prompt keywords), déclenchement d'audit programmé.

## Tests
- `fix-prompt.test.ts` : prompt contient config MCP + siteId + chaque item fail/warn avec son détail ; items manual non embarqués (consigne présente) ; N/A en ids ; snapshot stable.
- `tools.test.ts` (DB) : `run_site_audit` — tenancy cross-org refusée, site sans URL → message, upsert (1 ligne), **cooldown** (2e appel < 60 s → résultat existant, pas de nouveau fetch — vérifiable via un fetchImpl espionné/mocké).
- `route.test.ts` : liste exacte des tools + `run_site_audit` (9 tools).
- Vérif live : prompt copié depuis l'UI ; `run_site_audit` appelé via le endpoint avec une clé dev (smoke script existant).
