MCP Server per WordPress: Controlla il Tuo Sito con l’AI

MCP Server per WordPress: Controlla il Tuo Sito con l’AI

Perché un MCP Server per WordPress nel 2026

Il Model Context Protocol (MCP) ha cambiato il modo in cui i developer integrano l’AI nei loro strumenti. Se hai già letto la guida completa a MCP e sai come funziona il protocollo, è il momento di applicarlo al caso d’uso più concreto che puoi avere: il tuo sito WordPress.

WordPress alimenta oltre il 43% del web. È la piattaforma che la maggior parte dei developer freelance gestisce per i propri clienti, e spesso significa passare ore davanti alla dashboard per operazioni ripetitive: controllare bozze, pubblicare post, aggiornare categorie, rispondere ai commenti. Con un MCP server dedicato, puoi delegare tutto questo a Claude, che diventa il tuo operatore WordPress vocale e testuale.

L’incrocio tra competenze MCP e conoscenza delle REST API di WordPress è ancora quasi inesplorato. Chi sa costruire tool AI e conosce WordPress a fondo è una figura rara nel mercato italiano del 2026. Questo articolo ti porta dall’idea all’implementazione completa, con codice funzionante, casi d’uso reali e consigli per distribuire il server ai tuoi clienti.

Come Funziona: MCP + REST API WordPress

Prima di scrivere codice, chiarire l’architettura. Un MCP server per WordPress è un processo Node.js che espone strumenti (tools) al client MCP (Claude Desktop, Claude Code, o qualsiasi client compatibile). Ogni tool corrisponde a un’operazione sulla REST API di WordPress.

Il flusso è questo: l’utente chiede a Claude “mostrami le ultime 5 bozze”, Claude invoca il tool list_posts con parametri appropriati, il MCP server traduce la richiesta in una chiamata HTTP alla REST API del sito, riceve la risposta JSON e la ritorna a Claude formattata. Claude elabora e risponde in linguaggio naturale.

Il prerequisito tecnico è avere accesso alle REST API di WordPress. Sul tuo sito devi creare una Application Password (disponibile nativamente da WordPress 5.6): vai su Utenti › Profilo › Application Passwords, inserisci un nome (es. “MCP Server”) e salva la password generata. Questo è il metodo più sicuro perché non espone la password principale e puoi revocarlo in qualsiasi momento.

Setup del Progetto: Struttura e Dipendenze

Come punto di partenza, se hai già seguito il tutorial su come creare un MCP server in Node.js, il setup ti sarà familiare. Creiamo il progetto:

mkdir mcp-wordpress-server
cd mcp-wordpress-server
npm init -y
npm install @modelcontextprotocol/sdk node-fetch dotenv

# Aggiorna package.json per ESM
# "type": "module" nel package.json

Crea il file .env con le credenziali del sito WordPress:

WP_SITE_URL=https://tuosito.it
WP_USERNAME=tuousername
WP_APP_PASSWORD=xxxx xxxx xxxx xxxx xxxx xxxx

La struttura del progetto finale sarà:

  • index.js — entry point del server MCP
  • tools/posts.js — tool per gestire i post
  • tools/media.js — tool per i media
  • tools/categories.js — tool per categorie e tag
  • lib/wp-client.js — client HTTP riutilizzabile
  • .env — credenziali (non committare mai!)

Il Client WordPress: lib/wp-client.js

Il primo file da costruire è il client che astrae le chiamate HTTP alla REST API. Questo modulo gestisce l’autenticazione Basic Auth, il base URL e la serializzazione dei parametri:

// lib/wp-client.js
import 'dotenv/config';
import fetch from 'node-fetch';

const BASE_URL = process.env.WP_SITE_URL;
const USERNAME = process.env.WP_USERNAME;
const APP_PASS = process.env.WP_APP_PASSWORD;

const AUTH_HEADER = 'Basic ' + Buffer.from(`${USERNAME}:${APP_PASS}`).toString('base64');

export async function wpRequest(endpoint, options = {}) {
  const url = `${BASE_URL}/wp-json/wp/v2/${endpoint}`;

  const response = await fetch(url, {
    ...options,
    headers: {
      'Authorization': AUTH_HEADER,
      'Content-Type': 'application/json',
      ...options.headers,
    },
  });

  if (!response.ok) {
    const errorBody = await response.text();
    throw new Error(`WP API error ${response.status}: ${errorBody}`);
  }

  return response.json();
}

export async function wpGet(endpoint, params = {}) {
  const query = new URLSearchParams(params).toString();
  const path = query ? `${endpoint}?${query}` : endpoint;
  return wpRequest(path, { method: 'GET' });
}

export async function wpPost(endpoint, body = {}) {
  return wpRequest(endpoint, {
    method: 'POST',
    body: JSON.stringify(body),
  });
}

export async function wpPut(endpoint, body = {}) {
  return wpRequest(endpoint, {
    method: 'PUT',
    body: JSON.stringify(body),
  });
}

Nota: l’Application Password di WordPress include spazi nel formato visualizzato (es. xxxx xxxx xxxx). Non rimuoverli: sono parte della password e devono essere inclusi nell’header Base64 così com’è.

I Tool MCP: Operazioni sui Post

Ora definiamo i tool MCP veri e propri. Ogni tool ha un nome, una descrizione (che Claude usa per decidere quando invocarlo) e uno schema degli input. Il file tools/posts.js espone le operazioni più comuni:

// tools/posts.js
import { wpGet, wpPost, wpPut } from '../lib/wp-client.js';

export const postTools = [
  {
    name: 'list_posts',
    description: 'Elenca i post WordPress. Filtra per status (publish, draft, future), categoria, numero di risultati.',
    inputSchema: {
      type: 'object',
      properties: {
        status:     { type: 'string', enum: ['publish', 'draft', 'future', 'any'], default: 'publish' },
        per_page:   { type: 'number', description: 'Numero di post (max 100)', default: 10 },
        category:   { type: 'number', description: 'ID categoria (opzionale)' },
        search:     { type: 'string', description: 'Testo da cercare nel titolo/contenuto' },
      },
    },
  },
  {
    name: 'get_post',
    description: 'Ottieni il dettaglio completo di un post WordPress tramite il suo ID.',
    inputSchema: {
      type: 'object',
      required: ['post_id'],
      properties: {
        post_id: { type: 'number', description: 'ID del post WordPress' },
      },
    },
  },
  {
    name: 'create_post',
    description: 'Crea un nuovo post o bozza in WordPress.',
    inputSchema: {
      type: 'object',
      required: ['title', 'content'],
      properties: {
        title:      { type: 'string', description: 'Titolo del post' },
        content:    { type: 'string', description: 'Contenuto HTML o Gutenberg blocks' },
        status:     { type: 'string', enum: ['draft', 'publish', 'future'], default: 'draft' },
        excerpt:    { type: 'string', description: 'Estratto breve (opzionale)' },
        categories: { type: 'array', items: { type: 'number' }, description: 'Array di ID categoria' },
        date:       { type: 'string', description: 'Data pubblicazione ISO 8601 (per status future)' },
      },
    },
  },
  {
    name: 'update_post',
    description: 'Aggiorna un post esistente: titolo, contenuto, status, categorie.',
    inputSchema: {
      type: 'object',
      required: ['post_id'],
      properties: {
        post_id:    { type: 'number', description: 'ID del post da aggiornare' },
        title:      { type: 'string' },
        content:    { type: 'string' },
        status:     { type: 'string', enum: ['publish', 'draft', 'future', 'trash'] },
        excerpt:    { type: 'string' },
      },
    },
  },
];

export async function handlePostTool(name, args) {
  switch (name) {
    case 'list_posts': {
      const params = {
        status:   args.status   || 'publish',
        per_page: args.per_page || 10,
        _fields:  'id,title,status,date,link,categories',
      };
      if (args.category) params.categories = args.category;
      if (args.search)   params.search = args.search;
      const posts = await wpGet('posts', params);
      return posts.map(p => ({
        id:         p.id,
        title:      p.title.rendered,
        status:     p.status,
        date:       p.date,
        link:       p.link,
        categories: p.categories,
      }));
    }

    case 'get_post': {
      const post = await wpGet(`posts/${args.post_id}`);
      return {
        id:       post.id,
        title:    post.title.rendered,
        content:  post.content.rendered,
        status:   post.status,
        date:     post.date,
        link:     post.link,
        excerpt:  post.excerpt.rendered,
      };
    }

    case 'create_post': {
      const body = {
        title:      args.title,
        content:    args.content,
        status:     args.status || 'draft',
        excerpt:    args.excerpt || '',
        categories: args.categories || [],
      };
      if (args.date) body.date = args.date;
      const created = await wpPost('posts', body);
      return { id: created.id, link: created.link, status: created.status };
    }

    case 'update_post': {
      const body = {};
      if (args.title)   body.title   = args.title;
      if (args.content) body.content = args.content;
      if (args.status)  body.status  = args.status;
      if (args.excerpt) body.excerpt = args.excerpt;
      const updated = await wpPut(`posts/${args.post_id}`, body);
      return { id: updated.id, status: updated.status, link: updated.link };
    }

    default:
      throw new Error(`Tool post sconosciuto: ${name}`);
  }
}

Il Server MCP Principale: index.js

Il file principale assembla tutto e registra il server MCP usando l’SDK ufficiale. Questo è il punto di ingresso che Claude invoca tramite stdio:

// index.js
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';

import { postTools, handlePostTool } from './tools/posts.js';

// Aggiungi qui altri moduli di tool quando li crei
const ALL_TOOLS = [...postTools];

const server = new Server(
  { name: 'mcp-wordpress', version: '1.0.0' },
  { capabilities: { tools: {} } }
);

// Handler: lista tool disponibili
server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: ALL_TOOLS,
}));

// Handler: esecuzione tool
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const { name, arguments: args } = request.params;

  try {
    let result;

    // Routing per modulo
    if (postTools.some(t => t.name === name)) {
      result = await handlePostTool(name, args);
    } else {
      throw new Error(`Tool non trovato: ${name}`);
    }

    return {
      content: [
        {
          type: 'text',
          text: JSON.stringify(result, null, 2),
        },
      ],
    };
  } catch (err) {
    return {
      content: [{ type: 'text', text: `Errore: ${err.message}` }],
      isError: true,
    };
  }
});

// Avvio con trasporto stdio
const transport = new StdioServerTransport();
await server.connect(transport);

Ora collega il server a Claude Desktop aggiungendo questa configurazione al file claude_desktop_config.json (su Mac si trova in ~/Library/Application Support/Claude/):

{
  "mcpServers": {
    "wordpress": {
      "command": "node",
      "args": ["/percorso/assoluto/mcp-wordpress-server/index.js"],
      "env": {
        "WP_SITE_URL": "https://tuosito.it",
        "WP_USERNAME": "tuousername",
        "WP_APP_PASSWORD": "xxxx xxxx xxxx xxxx xxxx xxxx"
      }
    }
  }
}

Riavvia Claude Desktop e troverai i tool WordPress disponibili nella barra degli strumenti. Puoi chiedere direttamente: “Elenca le ultime 10 bozze del mio sito” oppure “Crea una bozza con titolo X e questo contenuto”.

Se preferisci una soluzione cloud senza installazione locale, dai un’occhiata a come funziona MCP Connector via API per server remoti: con quella architettura puoi ospitare il server su un VPS e condividerlo con più utenti o clienti.

Casi d’Uso Avanzati: Workflow Editoriale con AI

Avere Claude collegato a WordPress apre scenari che prima richiedevano strumenti separati, automazioni complesse o molto tempo manuale. Ecco i workflow più potenti che puoi costruire.

Revisione editoriale automatica: chiedi a Claude di leggere tutte le bozze correnti e per ognuna suggerire miglioramenti al titolo SEO, alla meta description e all’introduzione. Claude invoca list_posts con status: "draft", poi get_post per ognuna, analizza il contenuto e produce un report strutturato. Lo stesso approccio funziona per audit SEO tecnica — argomento che ho approfondito nella checklist SEO tecnica per developer.

Pianificazione calendario editoriale: dai a Claude una lista di topic e chiedigli di creare le bozze con titoli ottimizzati, excerpt e categorie corrette. Con i tool create_post può creare 10 bozze in meno di un minuto, ciascuna con status: "draft" pronta per la tua revisione.

Aggiornamento bulk: se devi aggiornare il footer o una call-to-action presente in molti post, puoi chiedere a Claude di trovare tutti i post che contengono una certa stringa e aggiornarli. Con il parametro search in list_posts e il tool update_post, l’operazione che prima richiedeva un plugin diventa una conversazione.

Questo tipo di automazione si integra perfettamente con il pattern degli agenti con Tool Use di Claude API, dove l’AI decide autonomamente quale tool invocare in base all’obiettivo fornito.

Generazione contenuti contestuale: il vantaggio vero di avere Claude connesso al sito è il contesto. Puoi chiedere “analizza i miei ultimi 20 post pubblicati e suggerisci i prossimi 5 argomenti coerenti con il mio stile”. Claude legge i contenuti reali, non generici esempi, e produce suggerimenti calibrati sulla tua voice.

Sicurezza e Considerazioni di Produzione

Prima di usare il server MCP su un sito di produzione o condividerlo con clienti, ci sono alcune considerazioni di sicurezza non trascurabili.

Scope minimo delle credenziali: crea un utente WordPress dedicato con ruolo Editor (non Administrator) esclusivamente per il MCP server. In questo modo l’AI non ha accesso alle impostazioni del sito, ai plugin o ai file del tema. Se il token viene compromesso, il danno è limitato.

Rate limiting lato server: le REST API di WordPress non hanno rate limiting nativo. Se Claude lancia richieste in loop per un bug, puoi saturare il server. Aggiungi un semplice throttle nel client:

// lib/throttle.js — semplice rate limiter (max N req/sec)
let lastCall = 0;
const MIN_INTERVAL_MS = 200; // max 5 req/sec

export async function throttle() {
  const now = Date.now();
  const elapsed = now - lastCall;
  if (elapsed < MIN_INTERVAL_MS) {
    await new Promise(r => setTimeout(r, MIN_INTERVAL_MS - elapsed));
  }
  lastCall = Date.now();
}

// Uso nel wp-client.js:
// import { throttle } from './throttle.js';
// export async function wpRequest(endpoint, options = {}) {
//   await throttle();
//   // ... resto della funzione
// }

Validazione degli input: l’SDK MCP valida automaticamente gli argomenti rispetto all’inputSchema che hai definito. Assicurati che gli schemi siano precisi, soprattutto per i tool che modificano contenuti (create_post, update_post). Aggiungi maxLength sui campi stringa per prevenire contenuti giganteschi.

Audit log: in produzione, logga ogni chiamata al tool con timestamp, nome del tool e argomenti (oscura le credenziali). Un semplice console.error su stderr è sufficiente — MCP usa stdout per il protocollo e stderr per i log, quindi non si mescolano.

Se stai pensando di offrire questo come servizio ai tuoi clienti, vale la pena inquadrarlo come un’offerta premium. Hai già la base tecnica; come strutturare il pricing e il contratto per questo tipo di servizio lo trovi nella guida su come alzare le tariffe da freelance developer.

FAQ e Domande Frequenti

Posso usare questo MCP server con WordPress.com (non self-hosted)?

Dipende dal piano. WordPress.com Business e superiori abilitano le REST API custom. Per i piani inferiori le API sono limitate e non supportano Application Passwords. Il consiglio è usarlo su installazioni self-hosted dove hai pieno controllo. Se usi hosting condiviso, assicurati che il provider non blocchi le richieste autenticate alle REST API (alcuni lo fanno per sicurezza).

Quanto è sicuro dare all’AI accesso in scrittura al mio sito?

Con le precauzioni descritte (utente dedicato con ruolo Editor, Application Password revocabile, throttle), il rischio è gestibile. Claude non agisce mai in modo autonomo senza che tu lo chieda esplicitamente. Ogni tool viene invocato in risposta a una tua istruzione e puoi vedere esattamente cosa sta per fare prima che lo faccia. In Claude Desktop c’è anche la possibilità di approvare manualmente ogni tool call prima dell’esecuzione — abilitala finché non ti senti a tuo agio con il comportamento del server.

Posso aggiungere tool per WooCommerce o plugin custom?

Assolutamente sì. WooCommerce espone le proprie REST API sotto /wp-json/wc/v3/ e il pattern è identico: crei un nuovo file tools/woocommerce.js con i tool per prodotti, ordini e clienti, aggiungi le definizioni in ALL_TOOLS e implementi gli handler. Per plugin custom che non hanno REST API native puoi usare wp_register_rest_route() in un piccolo plugin PHP dedicato — crei endpoint personalizzati che il MCP server poi chiama come qualsiasi altra API.

Funziona anche con Claude Code oltre che con Claude Desktop?

Sì. Claude Code supporta server MCP locali tramite la stessa configurazione. Nel file .claude/settings.json del tuo progetto aggiungi il blocco mcpServers con le stesse impostazioni usate per Claude Desktop. Questo è utile se stai sviluppando temi o plugin WordPress e vuoi accesso rapido ai contenuti del sito senza uscire dal terminale.

Conclusione

Un MCP server per WordPress trasforma radicalmente il modo in cui interagisci con il tuo sito. Non si tratta di un semplice wrapper API: significa avere un assistente AI che conosce il tuo contenuto, capisce il contesto editoriale e può agire in modo coordinato su decine di operazioni che prima richiedevano navigazione manuale nella dashboard.

Il codice in questo articolo copre il nucleo del server — post management e configurazione di base. Da qui puoi espandere con tool per media, commenti, tassonomie, utenti e qualsiasi endpoint custom. L’architettura modulare rende ogni aggiunta indipendente e testabile.

Se sei un freelance che gestisce siti WordPress per clienti, questo server diventa un differenziatore concreto: puoi offrire un servizio di gestione contenuti assistita da AI che nessun’altro nella tua area probabilmente sta ancora facendo. Costruiscilo, usalo internamente, poi decide se venderlo come prodotto o come servizio.

Suggerimenti e Risorse

🔧 Tool consigliato: Usa il code review automatico con Claude e GitHub Actions per tenere sotto controllo il codice del tuo MCP server man mano che lo espandi.

💡 Pro tip: Testa ogni tool con @modelcontextprotocol/inspector prima di collegarlo a Claude Desktop. Il tool inspector esegue il server localmente e ti permette di invocare i tool manualmente via browser, senza dover aprire Claude ogni volta.

🎯 Strategia: Inizia con tool solo in lettura (list_posts, get_post) per qualche settimana prima di abilitare quelli in scrittura. Ti permette di capire il comportamento del server e costruire fiducia nel workflow prima di dare all’AI il potere di modificare contenuti reali.

Condividi

Articoli Recenti

Categorie popolari