Si gestionas muchos sitios de WordPress, siempre estás buscando la forma más fácil de reducir el tiempo que pasas accediendo a los paneles de control y haciendo clic en una serie de botones.
Los MCP (Model Context Protocols) han dado mucho que hablar últimamente, y hemos decidido explorar cómo los MCP, junto con la API de Kinsta, podrían ayudar a una agencia que gestiona tantos sitios web.
En este artículo, te mostramos un ejemplo práctico de cómo crear un servidor MCP que conecte asistentes de IA como Claude con la API de Kinsta para gestionar las tareas de alojamiento para WordPress que las agencias ya realizan a diario.
Qué estamos creando
Estamos creando un servidor MCP que ofrece un conjunto de herramientas para que los asistentes de IA puedan realizar acciones como:
- Mostrar todos los sitios de WordPress de tu cuenta
- Mostrar los entornos de un sitio específico
- Borrar la caché en un entorno determinado
- Clonar un sitio existente para crear uno nuevo
- Ver qué plugins y temas están desactualizados o son vulnerables
- Activar actualizaciones de plugins en entornos específicos
Una vez configurado el servidor, se conecta a un host/cliente MCP (en este caso, Claude para escritorio):

Fíjate en cómo llama a varias herramientas y luego devuelve la siguiente respuesta:

Primeros pasos
Antes de ponerte con el código, conviene entender algunos conceptos básicos sobre cómo encaja MCP en esta configuración.
Un servidor MCP se sitúa entre un asistente de IA y una API ya existente. No sustituye a la API ni cambia su funcionamiento. En cambio, pone a disposición un conjunto de herramientas a las que la IA puede recurrir cuando sea necesario. Cada herramienta corresponde a una acción concreta, como listar sitios o borrar la caché de tu sitio.
Cuando haces una pregunta a un asistente de IA, este decide si alguna de esas herramientas es relevante. Si lo es, el asistente invoca la herramienta a través del servidor MCP, el servidor se comunica con la API y el resultado se devuelve como una simple respuesta. Nada se ejecuta automáticamente sin tu aprobación, y solo están disponibles las herramientas que tú expongas.
En este ejemplo, el servidor MCP se comunica con la API de Kinsta y ofrece un conjunto limitado de acciones de Alojamiento para WordPress. No hace falta ninguna interfaz de usuario personalizada, automatización en segundo plano ni configuración especial de IA.
Requisitos previos
Para seguir el tutorial, necesitas tener algunas cosas preparadas:
- Una instalación estable de Node.js
- Conocimientos básicos de TypeScript
- Una cuenta de Kinsta con acceso a la API habilitado
- Tu clave API de Kinsta y tu ID de empresa
No necesitas ninguna experiencia previa con MCP, ni tienes que crear o entrenar un modelo de IA. Nos centramos únicamente en conectar herramientas ya existentes.
Configuración del proyecto
Empieza creando un nuevo directorio para el proyecto e inicializando una aplicación Node.js:
mkdir kinsta-mcp
cd kinsta-mcp
npm init -y
A continuación, instala el SDK de MCP y el pequeño conjunto de dependencias que usamos:
npm install @modelcontextprotocol/sdk zod@3
npm install -D typescript @types/node
Crea una estructura básica del proyecto:
mkdir src
touch src/index.ts
A continuación, actualiza tu package.json para que Node pueda ejecutar el servidor construido:
{
"name": "kinsta-mcp-server",
"version": "1.0.0",
"description": "Servidor MCP para gestionar sitios de WordPress a través de la API de Kinsta",
"type": "module",
"scripts": {
"build": "tsc"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.0.0",
"zod": "^3.24.0"
},
"devDependencies": {
"@types/node": "^22.0.0",
"typescript": "^5.0.0"
}
}
Por último, añade un archivo tsconfig.json en la raíz del proyecto:
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./build",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
Una vez hecho esto, ya estás listo para empezar a construir el servidor MCP.
Construcción del servidor MCP
Ahora que el proyecto ya está configurado, es hora de construir el servidor MCP propiamente dicho.
Empezamos importando los paquetes necesarios y creando la instancia del servidor. A continuación, añadimos una pequeña ayuda para comunicarnos con la API. Después de eso, registramos herramientas que se corresponden directamente con las acciones del Alojamiento para WordPress.
Importar paquetes y crear el servidor
Abre src/index.ts y añade las siguientes importaciones al principio del archivo:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
Esto hace tres cosas:
McpServeres el servidor principal que registra las herramientas y gestiona las solicitudes del cliente de IAStdioServerTransportpermite que el servidor se comunique a través de la entrada/salida estándar, que es como se conectan la mayoría de los clientes de IA de escritoriozodse usa para definir y validar la entrada que acepta cada herramienta
A continuación, define algunas constantes para la API y las credenciales:
const KINSTA_API_BASE = "https://api.kinsta.com/v2";
const KINSTA_API_KEY = process.env.KINSTA_API_KEY;
const KINSTA_COMPANY_ID = process.env.KINSTA_COMPANY_ID;
Ahora crea la instancia del servidor MCP:
const server = new McpServer({
name: "kinsta",
version: "1.0.0",
});
El nombre es cómo aparece el servidor dentro de un cliente MCP. La versión es opcional, pero resulta útil una vez que empiezas a iterar.
Añadir un helper para las solicitudes a la API
La mayoría de las herramientas que desarrollamos necesitan realizar peticiones HTTP a la API. En lugar de repetir esa lógica en todas partes, crea una única función auxiliar (helper). Añádela debajo de la configuración del servidor:
async function kinstaRequest(
endpoint: string,
options: RequestInit = {}
): Promise {
const url = `${KINSTA_API_BASE}${endpoint}`;
const headers = {
Authorization: `Bearer ${KINSTA_API_KEY}`,
"Content-Type": "application/json",
...options.headers,
};
const response = await fetch(url, { ...options, headers });
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Error de la API de Kinsta (${response.status}): ${errorText}`);
}
return response.json() as Promise;
}
Ejecución de herramientas de implementación
Las herramientas son lo principal que ofrece un servidor MCP. Cada herramienta es una función que un asistente de IA puede invocar, con tu permiso, para realizar una tarea específica.
En este servidor, cada herramienta sigue la misma estructura:
- Un nombre de herramienta (como
list_sites) - Una breve descripción (esto ayuda al asistente a saber cuándo usarla)
- Un esquema de entrada (para que la herramienta solo se ejecute con entradas válidas)
- Una función de gestión (donde llamamos a la API y formateamos la salida)
Formateamos las respuestas como texto sin formato a propósito. Los asistentes de IA funcionan mejor cuando las herramientas devuelven resultados claros y legibles, en lugar de volcar datos JSON sin procesar.
Herramienta 1: Listar sitios
Esta herramienta muestra todos los sitios de WordPress asociados a tu cuenta de empresa. Suele ser lo primero que necesitas cuando trabajas con varios sitios, como la mayoría del resto de acciones requieren un ID de sitio.
La respuesta de la API incluye información básica sobre cada sitio, así que definimos una estructura sencilla con la que trabajar:
interface Site {
id: string;
name: string;
display_name: string;
status: string;
site_labels: Array;
}
interface ListSitesResponse {
company: {
sites: Site[];
};
}
Con eso listo, podemos registrar la herramienta:
server.registerTool(
"list_sites",
{
description:
"Obtén todos los sitios de WordPress de tu empresa. Devuelve los ID, los nombres y el estado de los sitios.",
inputSchema: {},
},
async () => {
const data = await kinstaRequest(
`/sites?company=${KINSTA_COMPANY_ID}`
);
const sitios = data.company.sites;
if (!sitios || sitios.length === 0) {
return {
contenido: [
{ tipo: "text", texto: "No se han encontrado sitios para esta empresa." }
],
};
}
const siteList = sites
.map((site) => {
const labels =
site.site_labels?.map((l) => l.name).join(", ") || "none";
return `• ${site.display_name} (${site.name})
ID: ${site.id}
Estado: ${site.status}
Etiquetas: ${labels}`;
})
.join("\n\n");
return {
content: [
{
type: "text",
text: `Se han encontrado ${sites.length} sitios:\n\n${siteList}`,
},
],
};
}
);
Esta herramienta no requiere ninguna entrada, así que el esquema de entrada está vacío. Dentro del controlador, llamamos a la API, comprobamos si el resultado está vacío y, a continuación, formateamos la respuesta como texto legible.
En lugar de devolver JSON sin procesar, devolvemos un breve resumen que funciona bien en una interfaz de chat. Esto facilita que un asistente de IA responda a preguntas como «¿Qué sitios tengo?» o «Muéstrame todos mis sitios de WordPress» sin necesidad de ningún análisis adicional.
Herramienta 2: Obtener entornos
Una vez que tengas un ID de sitio, el siguiente paso habitual es comprobar sus entornos. Esta herramienta muestra todos los entornos de un sitio determinado, incluidos los entornos de producción, staging y staging premium.
interface Environment {
id: string;
name: string;
display_name: string;
is_premium: boolean;
primaryDomain?: {
id: string;
name: string;
};
container_info?: {
php_engine_version: string;
};
}
interface GetEnvironmentsResponse {
site: {
environments: Environment[];
};
}
Algunos campos son opcionales, como el dominio principal o la versión de PHP, así que están marcados como tales. La herramienta en sí solo necesita el ID del sitio:
server.registerTool(
"get_environments",
{
description:
"Obtiene los entornos (productivo, staging) de un sitio específico. Requiere el ID del sitio.",
inputSchema: {
site_id: z.string().describe("El ID del sitio para el que se quieren obtener los entornos"),
},
},
async ({ site_id }) => {
const data = await kinstaRequest(
`/sites/${site_id}/environments`
);
const envs = data.site.environments;
if (!envs || envs.length === 0) {
return {
content: [
{ type: "text", text: "No se han encontrado entornos para este sitio." }
],
};
}
const envList = envs
.map((env) => {
const domain = env.primaryDomain?.name || "Sin dominio";
const php = env.container_info?.php_engine_version || "Desconocido";
const type = env.is_premium
? "Entorno de pruebas premium"
: env.name === "live"
? "Live"
: "Staging";
return `• ${env.display_name} (${type})
ID: ${env.id}
Dominio: ${domain}
PHP: ${php}`;
})
.join("\n\n");
return {
content: [
{
type: "text",
text: `Se han encontrado ${envs.length} entornos:\n\n${envList}`,
},
],
};
}
);
Este suele ser el siguiente paso antes de acciones como borrar la caché, clonar un sitio o actualizar plugins.
Herramienta 3: Borrar la caché del sitio
Borrar la caché es una tarea rutinaria, pero también es una operación asíncrona. Cuando la inicias, la API responde de inmediato con un ID de operación, mientras que el borrado de la caché continúa en segundo plano.
Aquí tienes la definición del tipo y la función de la herramienta:
interface OperationResponse {
operation_id: string;
message: string;
status: number;
}
server.registerTool(
"clear_site_cache",
{
description:
"Borra la caché de un entorno del sitio. Requiere el ID del entorno.",
inputSchema: {
environment_id: z
.string()
.describe("El ID del entorno para el que se va a borrar la caché"),
},
},
async ({ environment_id }) => {
const data = await kinstaRequest(
"/sites/tools/clear-cache",
{
method: "POST",
body: JSON.stringify({ environment_id }),
}
);
return {
content: [
{
type: "text",
text: `¡Se ha iniciado el borrado de la caché!
ID de la operación: ${data.operation_id}
Mensaje: ${data.message}
Usa get_operation_status para comprobar el progreso.`,
},
],
};
}
);
En lugar de esperar a que termine la operación, la herramienta devuelve inmediatamente el ID de la operación. Esto mantiene la interacción rápida y permite que el asistente de IA haga un seguimiento más tarde si es necesario.
Herramienta 4: Clonar sitio
Clonar un sitio es una de esas acciones que las agencias realizan constantemente, especialmente cuando trabajan con plantillas o crean nuevos sitios para clientes. En lugar de empezar desde cero, se toma un entorno existente y se crea un nuevo sitio basado en él.
La respuesta utiliza la misma estructura de operación que vimos antes, así que no hace falta volver a definirla. La herramienta requiere un nombre de visualización para el nuevo sitio y el ID del entorno desde el que clonar:
server.registerTool(
"clone_site",
{
description:
"Clona un entorno de sitio existente para crear un nuevo sitio. Ideal para crear nuevos sitios de clientes a partir de una plantilla.",
inputSchema: {
display_name: z
.string()
.describe("Nombre del nuevo sitio clonado"),
source_env_id: z
.string()
.describe("El ID del entorno desde el que clonar"),
},
},
async ({ display_name, source_env_id }) => {
const data = await kinstaRequest(
"/sites/clone",
{
method: "POST",
body: JSON.stringify({
company: KINSTA_COMPANY_ID,
display_name,
source_env_id,
}),
}
);
return {
content: [
{
type: "text",
text: `¡Clonación del sitio iniciada!
Nuevo sitio: ${display_name}
ID de la operación: ${data.operation_id}
Mensaje: ${data.message}
Usa get_operation_status para comprobar el progreso.`,
},
],
};
}
);
Esta herramienta es especialmente útil cuando se combina con otras herramientas. Por ejemplo, un asistente de IA puede clonar un sitio y, una vez finalizada la operación, mostrar inmediatamente los entornos o comprobar el estado de los plugins.
Herramienta 5: Obtener el estado de la operación
Como algunas acciones se ejecutan de forma asíncrona, necesitamos una forma de comprobar su progreso. Para eso sirve esta herramienta.
interface OperationStatusResponse {
status?: number;
message?: string;
}
server.registerTool(
"get_operation_status",
{
description:
"Comprobar el estado de una operación asíncrona (borrado de caché, clonación del sitio, etc.)",
inputSchema: {
operation_id: z
.string()
.describe("El ID de la operación que se va a comprobar"),
},
},
async ({ operation_id }) => {
const response = await fetch(
`${KINSTA_API_BASE}/operations/${encodeURIComponent(operation_id)}`,
{
headers: {
Authorization: `Bearer ${KINSTA_API_KEY}`,
},
}
);
const data: OperationStatusResponse = await response.json();
if (response.status === 200) {
return {
content: [
{
type: "text",
text: `¡Operación completada con éxito!
Message: ${data.message || "Operación finalizada"}`,
},
],
};
}
if (response.status === 202) {
return {
content: [
{
type: "text",
text: `Operación aún en curso...
Mensaje: ${data.message || "Procesando"}`,
},
],
};
}
return {
content: [
{
type: "text",
text: `Estado de la operación: ${response.status}
Mensaje: ${data.message || "Estado desconocido"}`,
},
],
};
}
);
Herramienta 6: Obtén los plugins de todos los sitios
Cuando gestionas muchos sitios de WordPress, los plugins suelen ser el punto donde las cosas empiezan a descontrolarse. Esta herramienta lo soluciona analizando los plugins de toda la cuenta de la empresa, en lugar de un sitio cada vez.
La API devuelve mucha información, incluyendo en qué entornos está instalado cada plugin, si hay actualizaciones disponibles y si una versión está marcada como vulnerable. Para trabajar con esos datos, definimos las siguientes estructuras:
interface PluginEnvironment {
id: string;
site_display_name: string;
display_name: string;
plugin_status: string;
plugin_update: string | null;
plugin_version: string;
is_plugin_version_vulnerable: boolean;
plugin_update_version: string | null;
}
interfaz Plugin {
name: string;
title: string;
latest_version: string | null;
is_latest_version_vulnerable: boolean;
environment_count: number;
update_count: number;
environments: PluginEnvironment[];
}
interfaz GetPluginsResponse {
company: {
plugins: {
total: número;
items: Plugin[];
};
};
}
La herramienta en sí no requiere ninguna entrada:
server.registerTool(
"get_plugins",
{
description:
"Obtiene todos los plugins de WordPress de todos los sitios. Muestra qué plugins tienen actualizaciones disponibles o vulnerabilidades de seguridad.",
inputSchema: {},
},
async () => {
const data = await kinstaRequest(
`/company/${KINSTA_COMPANY_ID}/wp-plugins`
);
const plugins = data.company.plugins.items;
if (!plugins || plugins.length === 0) {
return {
content: [
{ type: "text", text: "No se han encontrado plugins." }
],
};
}
const ordenados = [...plugins].sort(
(a, b) => b.update_count - a.update_count
);
const pluginList = sorted.slice(0, 20).map((plugin) => {
const status =
plugin.update_count > 0
? `⚠️ ${plugin.update_count} sitio(s) necesitan actualizarse`
: "✅ Al día";
const vulnerable =
plugin.is_latest_version_vulnerable ? " 🔴 VULNERABLE" : "";
return `• ${plugin.title} (${plugin.name})${vulnerable}
Última versión: ${plugin.latest_version || "desconocida"}
Instalado en: ${plugin.environment_count} entornos\
${status}`;\
}).join("\n\n");\
\
const outdatedCount = plugins.filter(\
(p) => p.update_count > 0\
).length;
return {
content: [
{
type: "text",
text: `Se han encontrado ${data.company.plugins.total} plugins (${outdatedCount} tienen actualizaciones disponibles):\n\n${pluginList}`,
},
],
};
}
);
Herramienta 7: Busca temas en todos los sitios
Los temas tienen problemas similares a los de los plugins, pero a menudo se revisan con menos frecuencia. Esta herramienta funciona igual que la de los plugins, pero se centra en los temas de WordPress.
La estructura de respuesta es igual que el endpoint del plugin, pero con campos específicos del tema:
interface ThemeEnvironment {
id: string;
site_display_name: string;
display_name: string;
theme_status: string;
theme_update: string | null;
theme_version: string;
is_theme_version_vulnerable: boolean;
theme_update_version: string | null;
}
interface Theme {
name: string;
title: string;
latest_version: string | null;
is_latest_version_vulnerable: boolean;
número_de_entornos: número;
número_de_actualizaciones: número;
entornos: ThemeEnvironment[];
}
interfaz GetThemesResponse {
empresa: {
temas: {
total: número;
elementos: Theme[];
};
};
}
Herramienta 8: Actualizar plugin
Enumerar los problemas es útil, pero al final hay que solucionarlos. Esta herramienta te permite actualizar un plugin específico en un entorno concreto.
El endpoint de actualización devuelve la misma estructura de operación asíncrona que se utilizó antes, así que podemos saltárnoslo. Aquí tienes la definición de la herramienta:
server.registerTool(
"update_plugin",
{
description:
"Actualiza un plugin específico a una nueva versión en un entorno de sitio.",
inputSchema: {
environment_id: z
.string()
.describe("El ID del entorno donde está instalado el plugin"),
plugin_name: z
.string()
.describe("El nombre/slug del plugin (p. ej., 'akismet', 'elementor')"),
update_version: z
.string()
.describe("La versión a la que actualizar (p. ej., '5.3')"),
},
},
async ({ environment_id, plugin_name, update_version }) => {
const data = await kinstaRequest(
`/sites/environments/${environment_id}/plugins`,
{
method: "PUT",
body: JSON.stringify({
name: plugin_name,
update_version,
}),
}
);
return {
content: [
{
type: "text",
text: `¡Actualización del plugin iniciada!
Plugin: ${plugin_name}
Versión de destino: ${update_version}
ID de la operación: ${data.operation_id}
Mensaje: ${data.message}
Usa get_operation_status para comprobar el progreso.`,
},
],
};
}
);
Al igual que el borrado de la caché y los clones de sitios, las actualizaciones se ejecutan de forma asíncrona. Al devolver el ID de la operación, el asistente de IA puede seguir el progreso en lugar de dar por hecho que la actualización ha terminado instantáneamente.
Ejecutar el servidor
Una vez registradas todas las herramientas, el último paso es iniciar el servidor MCP y ponerlo a disposición de un cliente de IA.
Al final de tu archivo, añade la función principal que conecta el servidor utilizando el transporte STDIO:
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Servidor MCP de Kinsta ejecutándose en stdio");
}
main().catch((error) => {
console.error("Error fatal:", error);
process.exit(1);
});
Esto le indica al servidor MCP que escuche las solicitudes a través de la entrada y salida estándar. Hace que el servidor sea detectable por los clientes de escritorio compatibles con MCP.
Un detalle importante aquí es el registro. Como este servidor se comunica a través de STDIO, todos los registros deben ir a stderr. Escribir en stdout puede interferir con los mensajes de MCP y romper la conexión.
A continuación, construye el proyecto:
npm run build
Esto compila los archivos de TypeScript en el directorio de construcción y hace que el punto de entrada sea ejecutable.
Una vez finalizada la construcción, el servidor está listo para que lo inicie un cliente MCP. Puedes acceder al código completo en GitHub.
Probar tu servidor con Claude for Desktop
Para utilizar tu servidor MCP, Claude for Desktop necesita saber cómo iniciarlo. Abre el archivo de configuración de Claude Desktop:
~/Library/Application Support/Claude/claude_desktop_config.json
Crea el archivo si aún no existe. Si utilizas VS Code, puedes abrirlo directamente desde el terminal:
code ~/Library/Application\ Support/Claude/claude_desktop_config.json
Dentro del archivo, añade tu servidor MCP bajo la clave mcpServers. Por ejemplo:
{
"mcpServers": {
"kinsta": {
"command": "node",
"args": ["/RUTA/ABSOLUTA/A/mcp-server-demo-kinsta-api/build/index.js"],
"env": {
"KINSTA_API_KEY": "tu-clave-api-aquí",
"KINSTA_COMPANY_ID": "tu-id-de-empresa-aquí"
}
}
}
}
Esta configuración le indica a Claude for Desktop que hay un servidor MCP llamado kinsta, que debe ejecutarse con Node.js y que el punto de entrada es el archivo index.js construido.
Asegúrate de que la ruta apunte al archivo compilado en el directorio de construcción, no al código fuente de TypeScript. Guarda el archivo y reinicia Claude for Desktop.
Comprobación de la conexión
Una vez que Claude se haya reiniciado, abre un nuevo chat. Haz clic en el icono junto al campo de entrada y, a continuación, pasa el cursor por encima de Connectors. Deberías ver tu servidor MCP en la lista.

Una vez conectado el servidor, puedes empezar a utilizarlo de inmediato. Claude decide qué herramienta usar, envía los datos necesarios y devuelve el resultado como texto sin formato.

Una forma diferente de trabajar con las herramientas que ya tienes
Lo que está cambiando ahora mismo no son las herramientas en sí. Las APIs siguen siendo las APIs. Las plataformas de alojamiento siguen funcionando igual. Lo que está cambiando es cómo interactuamos con ellas.
Las herramientas de IA empiezan a parecer menos chatbots y más interfaces. Este servidor MCP es un pequeño ejemplo de ese cambio. No introduce nuevas funciones, sino que pone a tu disposición las que ya existen de una forma que se adapta a cómo trabajas realmente.
Lo que hagas a partir de aquí depende de ti. Puedes mantenerlo sencillo y en modo de solo lectura. Puedes añadir más automatización con aprobaciones y controles de seguridad. O puedes conectar ese mismo servidor a otras herramientas de tu flujo de trabajo.
A medida que exploras nuevas herramientas y flujos de trabajo como este, es importante contar con una base de alojamiento sólida. Lo último que quieres es perder tiempo lidiando con problemas de inactividad o de rendimiento en lugar de crear y mejorar tus sitios.
Kinsta ofrece alojamiento administrado para WordPress que mantiene tus sitios funcionando de forma fiable, incluso cuando estás desconectado. Puedes explorar nuestros planes de alojamiento o hablar con nuestro equipo de ventas para encontrar el plan adecuado para ti.