Vue d'ensemble
- Espace de noms
Websource\Mcp, classes danssrc/(chargeur PSR-4 maison). Fichier principalwebsource-mcp.php:WEBSOURCE_MCP_VERSION,WEBSOURCE_MCP_FILE. - Aucune table. Options (non chargées automatiquement) :
wsmcp_key_hash(empreinte SHA-256 de la clé, vide = connecteur fermé),wsmcp_level(read,draft,publish),wsmcp_last_used(horodatage, réécrit au plus toutes les 10 minutes),wsmcp_log(100 dernières lignes). - Aucun appel sortant, aucun JavaScript, aucun CSS public.
| Classe | Rôle |
|---|---|
Plugin | Clé, niveau, construction du serveur avec les familles d'outils disponibles. |
Rest\Endpoint | Route REST, authentification par clé, forme des réponses et des erreurs, CORS, journalisation des écritures. |
Protocol\McpServer | Serveur JSON-RPC 2.0 sans état (message ou lot), méthodes MCP, filtrage des outils par niveau. |
Auth\AccessKey | Génération (wsmcp_ + 48 hexadécimaux), empreinte, lecture dans la requête. |
Tool\AccessLevel | Niveaux READ = 1, DRAFT = 2, PUBLISH = 3. |
Tool\ThemeTools, Tool\BuilderTools | Définitions et exécution des outils ; Tool\ToolException = erreur lisible renvoyée à Claude. |
Gateway\WordPressThemeGateway, Gateway\WordPressBuilderGateway | Accès à Websource Theme Config (Plugin::instance()->settings()) et à Websource Builder (websource_builder()). |
Admin\SettingsPage, Log | Page Réglages > Websource MCP, journal. |
Point d'accès
POST https://exemple.fr/wp-json/websource-mcp/v1/server
Content-Type: application/json
Accept: application/json
Authorization: Bearer wsmcp_…
| Élément | Détail |
|---|---|
| Authentification | Authorization: Bearer <clé> (recommandé) ou paramètre d'adresse ?key=<clé> (connecteur personnalisé de Claude, qui n'envoie pas d'en-tête). Aucun cookie, nonce ni compte WordPress n'est pris en compte. |
| Clé | ^wsmcp_[A-Za-z0-9]{40,64}$, comparée en temps constant à l'empreinte SHA-256 enregistrée. |
| Transport | « Streamable HTTP » réduit à une réponse JSON par requête : pas de session (Mcp-Session-Id ignoré), pas de flux SSE. |
| Corps | Un message JSON-RPC 2.0 ou un lot (tableau, 20 messages au plus), 1 Mo au plus. |
| Réponses | 200 JSON (JSON_UNESCAPED_UNICODE, JSON_UNESCAPED_SLASHES) ; 202 sans corps pour une notification (message sans id) ou un lot de notifications ; 400-32700 JSON invalide ; 401{"error":"unauthorized","error_description":"…"} + WWW-Authenticate: Bearer realm="Websource MCP" ; 405 + Allow: POST, OPTIONS sur GET / PUT / PATCH / DELETE ; 413 au-delà de 1 Mo. |
| En-têtes | Cache-Control: no-store, X-Robots-Tag: noindex, Access-Control-Allow-Origin: *, Access-Control-Allow-Headers: Authorization, Content-Type, Accept, Mcp-Protocol-Version, Mcp-Session-Id ; OPTIONS → 204. Pas d'Allow-Credentials : l'accès repose sur la clé, jamais sur un cookie. |
Configuration Claude Code :
claude mcp add --transport http mon-site "https://exemple.fr/wp-json/websource-mcp/v1/server" --header "Authorization: Bearer wsmcp_…"
Claude (web, ordinateur, mobile) : Paramètres > Connecteurs > Ajouter un connecteur personnalisé, avec l'adresse complète …/server?key=wsmcp_….
Méthodes JSON-RPC
| Méthode | Résultat |
|---|---|
initialize | protocolVersion (la version demandée si elle est parmi 2025-11-25, 2025-06-18, 2025-03-26, 2024-11-05, sinon 2025-11-25), capabilities.tools.listChanged = false, serverInfo (name = websource-wordpress, title = « Websource — version), instructions (consignes adaptées au niveau d'accès). |
ping | {} |
tools/list | Outils permis par le niveau d'accès (les autres ne sont pas annoncés). |
tools/call | content[0].text (JSON lisible), structuredContent (même donnée), isError. Une erreur d'outil (argument refusé, niveau insuffisant) est un résultat isError: true avec un message en français ; une méthode ou un outil inconnu est une erreur JSON-RPC. |
resources/list, prompts/list | Listes vides. |
notifications/* | Aucune réponse (202). |
Codes d'erreur JSON-RPC : -32700 JSON invalide, -32600 requête invalide (identifiant ou enveloppe), -32601 méthode inconnue, -32602 outil inconnu ou nom manquant.
Exemple réel (site de test) :
{"jsonrpc": "2.0", "id": 1, "method": "initialize",
"params": {"protocolVersion": "2025-06-18", "capabilities": {}, "clientInfo": {"name": "http-test", "version": "1"}}}
{"jsonrpc": "2.0", "id": 1, "result": {
"protocolVersion": "2025-06-18",
"capabilities": {"tools": {"listChanged": false}},
"serverInfo": {"name": "websource-wordpress", "title": "Websource — Boutique de test", "version": "1.0.0"},
"instructions": "Connecteur du site WordPress « Boutique de test » (thème et blocs Websource). Les outils theme_* règlent l'apparence (couleurs, polices, en-tête, pied de page) ; les outils builder_* composent les pages par blocs. Avant d'écrire des blocs, appeler builder_lister_blocs pour connaître les types et leurs champs. Un contenu du builder (une « composition ») a un brouillon et une version en ligne : enregistrer un brouillon ne change rien sur le site. Un bloc de type « gutenberg » est un bloc natif de l'éditeur de WordPress : le renvoyer tel quel (avec son data.ref) pour le conserver. Ce connecteur est en lecture seule."
}}
{"jsonrpc": "2.0", "id": 1, "method": "tools/list"}
{"jsonrpc": "2.0", "id": 1, "result": {"tools": [
{"name": "theme_lire_reglages", "title": "Lire les réglages du thème"},
{"name": "theme_modifier_reglages", "title": "Modifier les réglages du thème"},
{"name": "builder_lister_blocs", "title": "Types de blocs du builder"},
{"name": "builder_lister_emplacements", "title": "Emplacements possibles"},
{"name": "builder_lister_contenus", "title": "Contenus du builder"},
{"name": "builder_lire_contenu", "title": "Lire un contenu"},
{"name": "builder_creer_contenu", "title": "Créer un contenu (brouillon)"},
{"name": "builder_enregistrer_brouillon", "title": "Enregistrer le brouillon d'un contenu"},
{"name": "builder_publier", "title": "Publier un contenu"},
{"name": "builder_mettre_hors_ligne", "title": "Mettre un contenu hors ligne"}
]}}
(Liste au niveau « publication » ; chaque entrée réelle porte aussi description, inputSchema et annotations.)
Lot : [{"jsonrpc":"2.0","id":"a","method":"ping"},{"jsonrpc":"2.0","method":"notifications/initialized"},{"jsonrpc":"2.0","id":"b","method":"tools/list"}] → tableau de deux réponses (a, b).
Niveaux d'accès
| Outil | Niveau requis | Annotations |
|---|---|---|
theme_lire_reglages, builder_lister_blocs, builder_lister_emplacements, builder_lister_contenus, builder_lire_contenu | lecture (read) | readOnlyHint |
builder_creer_contenu, builder_enregistrer_brouillon | brouillons (draft) | destructiveHint: false / true, idempotentHint |
builder_publier, builder_mettre_hors_ligne, theme_modifier_reglages | publication (publish) | destructiveHint, idempotentHint |
Un outil appelé sous son niveau renvoie isError: true : « Cette action n'est pas permise par le niveau d'accès choisi pour ce connecteur… ». Les outils theme_* n'existent que si Websource Theme Config est actif, les outils builder_* que si Websource Builder est actif.
Outils thème
theme_lire_reglages
Sans argument. Résultat : couleurs (primary, secondary, accent, ink, line, page_bg, surface, footer_bg : valeur, role), palette_reconnue, palettes_disponibles (blue, navy, coral, forest, amber, violet, raspberry, mint, slate), polices (font_body, font_head : mode = system | google | fichier envoyé, famille), bordures (borders, shadow_when_no_border), reglages, valeurs_possibles, avertissements (contraste WCAG < 4,5:1).
{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "theme_lire_reglages", "arguments": {}}}
{"jsonrpc": "2.0", "id": 1, "result": {"structuredContent": {
"couleurs": {"primary": {"valeur": "#0369a1", "role": "couleur principale (boutons, liens)"}, "secondary": {"valeur": "#0b3a5c", "role": "couleur secondaire"}, "accent": {"valeur": "#f5b83d", "role": "couleur d'accent (promotions, mises en avant)"}, "ink": {"valeur": "#0f172a", "role": "couleur du texte"}, "line": {"valeur": "#dbe4ee", "role": "couleur des bordures et séparateurs"}, "page_bg": {"valeur": "#ffffff", "role": "fond de page"}, "surface": {"valeur": "#ffffff", "role": "fond des cartes et encarts"}, "footer_bg": {"valeur": "#0c2a43", "role": "fond du pied de page"}},
"palette_reconnue": "blue",
"palettes_disponibles": {"blue": {"primary": "#0369a1", "secondary": "#0b3a5c", "accent": "#f5b83d"}, "…": {}},
"polices": {"font_body": {"mode": "system", "famille": null}, "font_head": {"mode": "system", "famille": null}},
"bordures": {"borders": true, "shadow_when_no_border": true},
"reglages": {"radius": "xl", "color_intensity": "balanced", "desktop_header": "classic", "mobile_header": "icons_right", "mobile_nav": "bottom", "footer_variant": "dark", "search_width": "md", "search_align": "center", "header_actions": "main", "checkout_header": "full", "checkout_footer": "full"},
"valeurs_possibles": {"radius": {"valeurs": ["none", "sm", "md", "lg", "xl"], "role": "Arrondi des boutons, cartes et champs (none = angles droits, xl = très arrondi)."}, "…": {}},
"avertissements": []
}, "content": [{"type": "text", "text": "{ … même contenu … }"}], "isError": false}}
theme_modifier_reglages
Niveau publication, effet immédiat. Arguments (au moins un, additionalProperties: false) :
| Argument | Type | Valeurs |
|---|---|---|
palette | string | clé de palettes_disponibles ; applique les 8 couleurs, puis couleurs par-dessus |
couleurs | object | primary, secondary, accent, ink, line, page_bg, surface, footer_bg au format #rrggbb |
font_body, font_head | string | system ou le nom exact d'une famille Google Fonts (lettres, chiffres, espaces ; jamais une adresse) |
borders, shadow_when_no_border | boolean | |
radius | enum | none, sm, md, lg, xl |
color_intensity | enum | sober, balanced, vivid |
desktop_header | enum | classic, centered, compact, split, band, dark, large, sticky |
mobile_header | enum | icons_right, centered, stacked |
mobile_nav | enum | bottom, top |
footer_variant | enum | dark, light, brand, centered, minimal, accent, twotone, contrast |
search_width | enum | sm, md, lg, full |
search_align | enum | left, center, right |
header_actions | enum | top, main |
checkout_header, checkout_footer | enum | full, minimal, none |
Toute valeur hors liste est refusée avec la liste des valeurs permises ; un réglage inconnu (par exemple le code libre du <head>, volontairement non exposé) est refusé.
{"jsonrpc": "2.0", "id": 1, "method": "tools/call",
"params": {"name": "theme_modifier_reglages", "arguments": {"couleurs": {"primary": "#123456"}}}}
{"jsonrpc": "2.0", "id": 1, "result": {"structuredContent": {
"modifie": true,
"modifications": {"primary": {"avant": "#0369a1", "apres": "#123456"}},
"avertissements": [],
"message": "Réglages enregistrés et visibles sur la boutique. Pour annuler, rappeler cet outil avec les valeurs « avant »."
}, "content": [{"type": "text", "text": "…"}], "isError": false}}
Sans changement effectif : {"modifie": false, "message": "Aucun changement : ces réglages ont déjà ces valeurs."}.
Outils builder
Modèle d'un bloc : {"type": "<type>", "data": {<champs>}, "style": {<apparence>}} (voir la référence de Websource Builder). Les validations sont celles de l'éditeur : champ absent → valeur par défaut, valeur hors liste → valeur par défaut, type inconnu → retiré (listé dans blocs_ignores), HTML filtré, couleurs invalides retirées, liens javascript: neutralisés. Bloc columns : data.cols = [{"w": 1–6, "blocks": […]}], un seul niveau. Bloc {"type": "gutenberg", "data": {"ref": "…"}} : bloc natif déjà présent dans le contenu, conservé tel quel ; une référence inconnue est ignorée.
builder_lister_blocs
Arguments : types (tableau de chaînes, facultatif). Sans types : liste courte (type, nom, categorie, description, champs) et suite. Avec types : pour chaque type, data (champs : champ, nature, libelle, valeurs, min/max, elements, aide, defaut), variantes (layouts), plus style (champs d'apparence communs) et format (règle d'écriture).
{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "builder_lister_blocs", "arguments": {"types": ["hero"]}}}
{"jsonrpc": "2.0", "id": 1, "result": {"structuredContent": {
"blocs": [{"type": "hero", "nom": "Bannière d'accueil", "description": "Grand titre, texte, bouton et image de fond.",
"data": [
{"champ": "title", "nature": "text", "libelle": "Titre", "defaut": "Un titre qui donne envie"},
{"champ": "subtitle", "nature": "textarea", "libelle": "Sous-titre", "defaut": "Expliquez en une phrase ce que vous proposez."},
{"champ": "button_label", "nature": "text", "libelle": "Texte du bouton", "defaut": "Découvrir"},
{"champ": "button_url", "nature": "url", "libelle": "Lien du bouton", "aide": "Adresse complète, ou {link:home}, {link:contact}, {link:cms:ID}, {link:category:ID}, {link:product:ID} : adaptée automatiquement à la langue et à la boutique.", "defaut": "{link:home}"},
{"champ": "image", "nature": "image", "libelle": "Image de fond", "aide": "Facultatif. Une image large (1600 px) fonctionne bien."},
{"champ": "height", "nature": "select", "libelle": "Hauteur", "valeurs": {"m": "Moyenne", "l": "Grande"}, "defaut": "m"}
],
"variantes": {"classic": "Classique, fond doux du thème", "brand-gradient": "Dégradé de marque centré", "dark": "Sombre à fort contraste", "floating-card": "Carte flottante avec ombre", "accent": "Accent chaleureux", "veil": "Image avec voile sombre translucide", "minimal": "Minimal centré, sans fond", "two-tone": "Dégradé marque vers accent"}}],
"style": [
{"champ": "bg", "nature": "select", "libelle": "Fond", "valeurs": {"none": "Aucun", "soft": "Teinte douce", "surface": "Couleur des cartes", "brand": "Couleur principale", "dark": "Sombre"}, "defaut": "none"},
{"champ": "pad", "nature": "select", "libelle": "Espace autour", "valeurs": {"s": "Petite", "m": "Moyenne", "l": "Grande"}, "defaut": "m"},
{"champ": "width", "nature": "select", "libelle": "Largeur", "valeurs": {"container": "Dans le cadre du site (boxed)", "wide": "Fond pleine largeur, contenu centré (wide)", "full": "Bord à bord (aucune marge)"}, "defaut": "container"},
{"champ": "align", "nature": "select", "libelle": "Alignement du texte", "valeurs": {"left": "…", "center": "…"}, "defaut": "left"}
],
"format": "Un bloc s'écrit {\"type\": \"...\", \"data\": {champ: valeur}, \"style\": {champ: valeur}}. …"
}, "isError": false}}
Type inconnu → isError: true, « Type de bloc inconnu : … Types disponibles : … ».
builder_lister_emplacements
Sans argument. Résultat : pages (clé => libellé), positions (position, libelle, pages_permises = liste ou "toutes"), langues, note.
{"structuredContent": {
"pages": {"all": "Toutes les pages du site", "home": "Page d'accueil", "shop": "Page boutique (catalogue)", "category": "Pages catégories", "product": "Fiches produit", "cms": "Pages de contenu (CMS)", "post": "Articles du blog", "blog": "Blog (liste des articles)", "login": "Page de connexion", "registration": "Page d'inscription", "password": "Mot de passe oublié", "account": "Mon compte (tableau de bord)", "orders": "Mes commandes", "cart": "Panier", "checkout": "Commande (tunnel de paiement)", "contact": "Page de contact", "search": "Résultats de recherche", "notfound": "Page introuvable (404)"},
"positions": [{"position": "top", "libelle": "Tout en haut, sous l'en-tête (pleine largeur)", "pages_permises": "toutes"},
{"position": "product_info", "libelle": "Dans la fiche produit, sous le bouton d'achat", "pages_permises": ["product"]}, "…"],
"langues": ["fr"],
"note": "La position « cms_page » (remplacer le texte d'une page) et les hooks personnalisés se règlent dans l'administration de WordPress."
}}
Les positions cms_page et custom sont refusées à la création par le connecteur (réglage dans l'éditeur WordPress).
builder_lister_contenus
Sans argument. contenus[] : contenu_id, nom, langue (toutes ou code), en_ligne, brouillon_non_publie, emplacements[] (page, position, cms_id, hook si présents), modifie_le. Corbeille exclue.
{"structuredContent": {"contenus": [
{"contenu_id": 83, "nom": "Fiche produit : atouts sous le bouton", "langue": "toutes", "en_ligne": true, "brouillon_non_publie": false,
"emplacements": [{"page": "product", "position": "product_info"}], "modifie_le": "2026-10-08 18:30:04"},
{"contenu_id": 81, "nom": "Bandeau de promotion", "langue": "toutes", "en_ligne": true, "brouillon_non_publie": false,
"emplacements": [{"page": "all", "position": "top"}], "modifie_le": "2026-10-08 18:14:26"}
]}}
builder_lire_contenu
Arguments : contenu_id (entier, requis), version (brouillon par défaut, ou en_ligne). Résultat : le résumé du contenu + version + blocs.
{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "builder_lire_contenu", "arguments": {"contenu_id": 83}}}
{"jsonrpc": "2.0", "id": 1, "result": {"structuredContent": {
"contenu_id": 83, "nom": "Fiche produit : atouts sous le bouton", "langue": "toutes", "en_ligne": true, "brouillon_non_publie": false,
"emplacements": [{"page": "product", "position": "product_info"}], "modifie_le": "2026-10-08 18:30:04",
"version": "brouillon",
"blocs": [{"type": "features",
"data": {"columns": 2, "items": [{"icon": "icon:truck", "title": "Expédié sous 24 h", "text": "Depuis notre atelier."},
{"icon": "icon:return", "title": "Retour sous 14 jours", "text": "Échange ou remboursement."}]},
"style": {"bg": "none", "pad": "s", "width": "container", "align": "left"}}]
}, "isError": false}}
Identifiant inconnu → isError: true, « Aucun contenu avec cet identifiant sur ce site. ».
builder_creer_contenu
Niveau brouillons. Arguments : nom (requis, 150 caractères), page et position (requis, clés de builder_lister_emplacements), cms_id (entier, pages cms, post, product, category ; 0 = tous), langue (code ou toutes), blocs (requis). Crée une composition WordPress en statut brouillon. Une position incompatible avec la page (« product_info » sur home) ou custom / cms_page est refusée ; si aucun bloc n'est valide, rien n'est créé.
{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "builder_creer_contenu", "arguments": {
"nom": "Test MCP draft", "page": "cms", "position": "before_content",
"blocs": [
{"type": "hero", "data": {"title": "Test MCP & accents « é »", "subtitle": "Créé par une requête JSON-RPC.", "button_label": "Voir", "button_url": "javascript:alert(1)", "height": "géante"}, "style": {"bg": "soft", "c_bg": "red;background:url(x)"}},
{"type": "text", "data": {"content": "<p>Bonjour <strong>à tous</strong><script>alert(1)</script></p>"}},
{"type": "html", "data": {"code": "<p class=\"wsmcp-html\">Code libre</p><script>alert(2)</script>"}},
{"type": "bloc_inconnu", "data": {}},
{"type": "columns", "data": {"cols": [{"w": 1, "blocks": [{"type": "heading", "data": {"title": "Colonne A"}}]},
{"w": 9, "blocks": [{"type": "button", "data": {"label": "Colonne B", "style": "outline"}}]}]}}
]}}}
{"jsonrpc": "2.0", "id": 1, "result": {"structuredContent": {
"contenu_id": 113, "etat": "brouillon, hors ligne", "blocs_enregistres": 4, "blocs_ignores": ["bloc_inconnu"],
"message": "Contenu créé hors ligne. Il n'apparaîtra sur le site qu'après publication (builder_publier, ou bouton Publier de l'éditeur de WordPress)."
}, "isError": false}}
À la relecture : height revenu à m, c_bg retiré, <script> supprimé, lien javascript: neutralisé, poids de colonne ramené à 6.
builder_enregistrer_brouillon
Niveau brouillons. Arguments : contenu_id, blocs (liste complète), nom (facultatif). Composition publiée : le brouillon est mis en attente (_wsb_pending_content), la version en ligne ne change pas, et l'éditeur WordPress propose de le charger. Composition non publiée : le contenu est remplacé.
{"structuredContent": {"contenu_id": 113, "blocs_enregistres": 4, "blocs_ignores": [], "en_ligne_inchange": true,
"message": "Brouillon enregistré. Le site affiche toujours la version en ligne ; publier pour appliquer."}}
builder_publier
Niveau publication. Argument : contenu_id. Le brouillon (en attente ou non) devient la version en ligne. Brouillon vide → erreur.
{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "builder_publier", "arguments": {"contenu_id": 115}}}
{"structuredContent": {"contenu_id": 115, "etat": "en ligne", "message": "Contenu publié : il est visible sur le site."}}
builder_mettre_hors_ligne
Niveau publication. Argument : contenu_id. Repasse la composition en brouillon WordPress ; rien n'est supprimé.
{"structuredContent": {"contenu_id": 115, "etat": "hors ligne", "message": "Contenu retiré du site. Il est conservé : builder_publier le remet en ligne."}}
Journal
Log::add($tool, $type, $detail) enregistre chaque tools/call dont le nom ne contient ni _lire_ ni _lister_ (type = call), et chaque exception inattendue d'un outil (type = error, message tronqué à 300 caractères). 100 lignes au plus, option wsmcp_log, affichées dans Réglages > Websource MCP.
Hooks
| Hook | Paramètres | Rôle |
|---|---|---|
Filtre websource_mcp_tool_providers | ToolProviderInterface[] $providers | Ajouter une famille d'outils. |
Action websource_mcp_logged | string $tool, string $type, string $detail | Après chaque écriture dans le journal (journal externe). |
Ajouter une famille d'outils :
add_filter('websource_mcp_tool_providers', function (array $providers) {
$providers[] = new class implements Websource\Mcp\Tool\ToolProviderInterface {
public function definitions(): array
{
return [[
'name' => 'stock_lire_alertes', 'title' => 'Alertes de stock',
'description' => 'Produits sous le seuil de stock.',
'inputSchema' => ['type' => 'object', 'properties' => new stdClass(), 'additionalProperties' => false],
'annotations' => ['readOnlyHint' => true, 'openWorldHint' => false],
'level' => Websource\Mcp\Tool\AccessLevel::READ,
]];
}
public function call(string $name, array $arguments): array
{
return ['alertes' => []]; // ou lever Websource\Mcp\Tool\ToolException('message pour Claude')
}
};
return $providers;
});
definitions() renvoie des définitions MCP (name, title, description, inputSchema, annotations) plus level (retiré avant l'envoi). call() renvoie un tableau sérialisé en JSON ; une ToolException devient un résultat isError: true ; toute autre exception est journalisée et masquée (« Erreur interne du connecteur »).
API PHP
| Méthode | Rôle |
|---|---|
Plugin::hasKey(): bool, Plugin::isKeyValid(string): bool | État de la clé. |
Plugin::generateKey(): string | Nouvelle clé en clair (à afficher une fois) ; l'ancienne est invalidée. |
Plugin::revokeKey(): void | Ferme le connecteur. |
Plugin::levelKey(): string, Plugin::setLevel(string): bool | Niveau read / draft / publish. |
Plugin::endpointUrl(string $key = ''): string | Adresse du serveur (avec ?key= si fournie). |
Plugin::createServer(): McpServer | Serveur prêt, avec les familles d'outils actives ; handlePayload(array) traite un message ou un lot décodé. |
Plugin::themeReady(): bool, Plugin::builderReady(): bool | Disponibilité des familles d'outils. |
WP-CLI : wp option update wsmcp_level publish change le niveau ; wp eval 'echo Websource\Mcp\Plugin::generateKey();' génère une clé.
Tests
php tests/http-test.php https://exemple.fr/wp-json/websource-mcp/v1/server wsmcp_… draft [exemples.json]
Vraies requêtes HTTP, sans charger WordPress : transport (401, 405, 204 CORS, 400, 202, 413), protocole (initialize, lot, méthodes et outils inconnus), outils de lecture, garde-fous du niveau, création / brouillon / publication / mise hors ligne avec contrôle du HTML public, outils du thème avec retour à la valeur d'origine. Le niveau se règle avant chaque passe (read, draft, publish). Le 4ᵉ paramètre enregistre les exemples de requêtes et réponses (ceux de cette page en viennent). Dernière exécution sur le site de test : 26/26, 37/37 et 49/49 vérifications réussies.
Désinstallation
uninstall.php supprime wsmcp_key_hash, wsmcp_level, wsmcp_last_used et wsmcp_log. Rien d'autre n'est touché.