Se você gerencia muitos sites WordPress, está sempre procurando a próxima maneira fácil de reduzir o tempo que gasta acessando painéis e clicando em uma série de botões.
Os MCPs (Model Context Protocols) têm causado bastante impacto recentemente, e decidimos explorar como os MCPs, em conjunto com a API da Kinsta, poderiam ajudar uma agência que gerencia tantos sites.
Neste artigo, vamos te mostrar um exemplo prático de como criar um servidor MCP que conecta assistentes de IA, como o Claude, à API da Kinsta para gerenciar tarefas de hospedagem para WordPress que as agências já realizam diariamente.
O que estamos construindo
Estamos construindo um servidor MCP que disponibiliza um conjunto de ferramentas, para que assistentes de IA possam realizar ações como:
- Listar todos os sites WordPress da tua conta
- Mostrar ambientes para um site específico
- Limpar o cache em um determinado ambiente
- Clonar um site existente para criar um novo
- Verificar quais plugins e temas estão desatualizados ou vulneráveis
- Habilitar atualizações de plugins em ambientes específicos
Depois que o servidor estiver configurado, ele é conectado a um host/cliente MCP (neste caso, o Claude para Desktop):

Observe como ele chama várias ferramentas e, em seguida, retorna a seguinte resposta:

Introdução
Antes de mais nada, é bom entender alguns conceitos básicos sobre como o MCP se encaixa nessa configuração.
Um servidor MCP fica entre um assistente de IA e uma API existente. Ele não substitui a API nem altera seu funcionamento. Em vez disso, ele expõe um conjunto de ferramentas que a IA pode chamar quando necessário. Cada ferramenta corresponde a uma ação específica, como listar sites ou limpar o cache do seu site.
Quando você faz uma pergunta a um assistente de IA, ele decide se uma dessas ferramentas é relevante. Se for, o assistente chama a ferramenta por meio do servidor MCP, o servidor se comunica com a API e o resultado é retornado como uma resposta simples. Nada é executado automaticamente sem a sua aprovação, e apenas as ferramentas que você expõe ficam disponíveis.
Neste exemplo, o servidor MCP se comunica com a API da Kinsta e expõe um conjunto limitado de ações de hospedagem para WordPress. Não é necessária nenhuma interface personalizada, automação em segundo plano nem configuração especial de IA.
Pré-requisitos
Para acompanhar, você precisa ter algumas coisas instaladas:
- Uma instalação estável do Node.js
- Conhecimento básico de TypeScript
- Uma conta Kinsta com acesso à API habilitado
- Sua chave de API da Kinsta e ID da empresa
Você não precisa de nenhuma experiência prévia com MCP, nem precisa construir ou treinar um modelo de IA. Nosso foco é apenas conectar as ferramentas existentes.
Configurando o projeto
Comece criando um novo diretório para o projeto e inicializando um aplicativo Node.js:
mkdir kinsta-mcp
cd kinsta-mcp
npm init -y
Em seguida, instale o SDK do MCP e o pequeno conjunto de dependências que usamos:
npm install @modelcontextprotocol/sdk zod@3
npm install -D typescript @types/node
Crie uma estrutura básica de projeto:
mkdir src
touch src/index.ts
Depois, atualiza o seu package.json para que o Node possa executar o servidor compilado:
{
"name": "kinsta-mcp-server",
"version": "1.0.0",
"description": "Servidor MCP para gerenciar sites WordPress via a API da 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 fim, adiciona um arquivo tsconfig.json na raiz do projeto:
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./build",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
Com isso pronto, você está pronto para começar a construir o próprio servidor MCP.
Construindo o servidor MCP
Agora que o projeto está configurado, é hora de construir o próprio servidor MCP.
Começamos importando os pacotes necessários e criando a instância do servidor. Depois, adicionamos um pequeno auxiliar para interagir com a API. Em seguida, registramos ferramentas que mapeiam diretamente para as ações de hospedagem do WordPress.
Importando pacotes e criando o servidor
Abra o src/index.ts e adicione as seguintes importações no topo do arquivo:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
Elas fazem três coisas:
McpServeré o servidor principal que registra as ferramentas e lida com as requisições do cliente de IAStdioServerTransportpermite que o servidor se comunique por entrada/saída padrão, que é como a maioria dos clientes de IA para desktop se conectazodé usado para definir e validar o input que cada ferramenta aceita
Em seguida, defina algumas constantes para a API e as credenciais:
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;
Agora cria a instância do servidor MCP:
const server = new McpServer({
name: "kinsta",
version: "1.0.0",
});
O nome é como o servidor aparece dentro de um cliente MCP. A versão é opcional, mas útil quando você começar a iterar.
Adicionando um helper para solicitações de API
A maioria das ferramentas que criamos precisa fazer solicitações HTTP para a API. Em vez de repetir essa lógica em todos os lugares, cria uma única função auxiliar. Adiciona isso abaixo da configuração do 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(`Erro na API da Kinsta (${response.status}): ${errorText}`);
}
return response.json() as Promise;
}
Implementando a execução de ferramentas
As ferramentas são o principal recurso que um servidor MCP expõe. Cada ferramenta é uma função que um assistente de IA pode chamar, com a tua aprovação, para realizar uma tarefa específica.
Neste servidor, cada ferramenta segue a mesma estrutura:
- Um nome de ferramenta (como
list_sites) - Uma descrição curta (que ajuda o assistente a saber quando usá-la)
- Um schema de input (para que a ferramenta só seja executada com um input válido)
- Uma função handler (onde chamamos a API e formatamos o output)
Formatamos as respostas como texto simples de forma intencional. Os assistentes de IA funcionam melhor quando as ferramentas retornam um output claro e legível em vez de despejar JSON bruto.
Ferramenta 1: Listar sites
Esta ferramenta recupera todos os sites WordPress da sua conta da empresa. Geralmente, é a primeira coisa que você deseja quando trabalha com vários sites, já que a maioria das outras ações começa com um ID de site.
A resposta da API inclui informações básicas sobre cada site, então definimos uma estrutura simples para trabalhar com eles:
interface Site {
id: string;
name: string;
display_name: string;
status: string;
site_labels: Array;
}
interface ListSitesResponse {
company: {
sites: Site[];
};
}
Com isso pronto, podemos registrar a ferramenta:
server.registerTool(
"list_sites",
{
description:
"Pega todos os sites WordPress da tua empresa. Retorna IDs, nomes e status dos sites.",
inputSchema: {},
},
async () => {
const data = await kinstaRequest(
`/sites?company=${KINSTA_COMPANY_ID}`
);
const sites = data.company.sites;
if (!sites || sites.length === 0) {
return {
content: [
{ type: "text", text: "Nenhum site encontrado para esta empresa." }
],
};
}
const siteList = sites
.map((site) => {
const labels =
site.site_labels?.map((l) => l.name).join(", ") || "nenhum";
return `• ${site.display_name} (${site.name})
ID: ${site.id}
Status: ${site.status}
Etiquetas: ${labels}`;
})
.join("\n\n");
return {
content: [
{
type: "text",
text: `Encontrados ${sites.length} site(s):\n\n${siteList}`,
},
],
};
}
);
Esta ferramenta não exige nenhum input, portanto o schema de input está vazio. Dentro do handler, chamamos a API, verificamos se o resultado está vazio e, em seguida, formatamos a resposta como texto legível.
Em vez de retornar JSON bruto, retornamos um resumo curto que funciona bem em uma interface de chat. Isso facilita para o assistente de IA responder perguntas como “Quais sites eu tenho?” ou “Mostre todos os meus sites WordPress” sem nenhum processamento adicional.
Ferramenta 2: Obter ambientes
Após ter um ID do site, o próximo passo comum é verificar seus ambientes. Esta ferramenta retorna todos os ambientes de um determinado site, incluindo os ambientes de produção, de teste e de teste 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[];
};
}
Alguns campos são opcionais, como o domínio principal ou a versão do PHP, então eles estão marcados como tal. A ferramenta em si aceita apenas o ID do site:
server.registerTool(
"get_environments",
{
description:
"Obtém ambientes (produção, staging) para um site específico. Requer o ID do site.",
inputSchema: {
site_id: z.string().describe("O ID do site para o qual se deseja obter os ambientes"),
},
},
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: "Nenhum ambiente encontrado para este site." }
],
};
}
const envList = envs
.map((env) => {
const domain = env.primaryDomain?.name || "Sem domínio";
const php = env.container_info?.php_engine_version || "Desconhecido";
const type = env.is_premium
? "Staging Premium"
: env.name === "live"
? "Live"
: "Staging";
return `• ${env.display_name} (${type})
ID: ${env.id}
Domínio: ${domain}
PHP: ${php}`;
})
.join("\n\n");
return {
content: [
{
type: "text",
text: `Encontrados ${envs.length} ambientes:\n\n${envList}`,
},
],
};
}
);
Esse geralmente é o próximo passo antes de ações como limpar o cache, clonar um site ou atualizar plugins.
Ferramenta 3: Limpar o cache do site
Limpar o cache é uma tarefa de rotina, mas também é uma operação assíncrona. Quando você a aciona, a API responde imediatamente com um ID de operação, enquanto a limpeza do cache continua em segundo plano.
A seguir, a definição de tipo e a função da ferramenta:
interface OperationResponse {
operation_id: string;
message: string;
status: number;
}
server.registerTool(
"clear_site_cache",
{
description:
"Limpa o cache de um ambiente do site. Requer o ID do ambiente.",
inputSchema: {
environment_id: z
.string()
.describe("O ID do ambiente para o qual limpar o cache"),
},
},
async ({ environment_id }) => {
const data = await kinstaRequest(
"/sites/tools/clear-cache",
{
method: "POST",
body: JSON.stringify({ environment_id }),
}
);
return {
content: [
{
type: "text",
text: `Limpeza do cache iniciada!
ID da operação: ${data.operation_id}
Mensagem: ${data.message}
Use get_operation_status para verificar o progresso.`,
},
],
};
}
);
Em vez de aguardar a conclusão da operação, a ferramenta retorna imediatamente o ID da operação. Isso mantém a interação ágil e permite que o assistente de IA faça um acompanhamento posterior, se necessário.
Ferramenta 4: Clonar site
Clonar um site é uma daquelas ações que as agências usam o tempo todo, especialmente quando trabalham com templates ou criam novos sites para clientes. Em vez de começar do zero, você usa um ambiente existente e cria um novo site com base nele.
A resposta usa a mesma estrutura de operação que vimos anteriormente, então não é necessário defini-la novamente. A ferramenta exige um nome de exibição para o novo site e o ID do ambiente a ser clonado:
server.registerTool(
"clone_site",
{
description:
"Clona um ambiente de site existente para criar um novo site. Ótimo para criar novos sites de clientes a partir de um modelo.",
inputSchema: {
display_name: z
.string()
.describe("Nome do novo site clonado"),
source_env_id: z
.string()
.describe("O ID do ambiente a partir do qual 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: `Clonagem do site iniciada!
Novo site: ${display_name}
ID da operação: ${data.operation_id}
Mensagem: ${data.message}
Use get_operation_status para verificar o progresso.`,
},
],
};
}
);
Esta ferramenta é especialmente útil quando combinada com outras ferramentas. Por exemplo, um assistente de IA pode clonar um site e, em seguida, listar os ambientes imediatamente ou verificar o status dos plugins assim que a operação for concluída.
Ferramenta 5: Obter o status da operação
Como algumas ações são executadas de forma assíncrona, precisamos de uma maneira de verificar o progresso delas. É exatamente para isso que esta ferramenta serve.
interface OperationStatusResponse {
status?: number;
message?: string;
}
server.registerTool(
"get_operation_status",
{
description:
"Verificar o status de uma operação assíncrona (limpar cache, clonar site, etc.)",
inputSchema: {
operation_id: z
.string()
.describe("O ID da operação a ser verificada"),
},
},
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: `Operação concluída com sucesso!
Mensagem: ${data.message || "Operação concluída"}`,
},
],
};
}
if (response.status === 202) {
return {
content: [
{
type: "text",
text: `Operação ainda em andamento...
Mensagem: ${data.message || "Processando"}`,
},
],
};
}
return {
content: [
{
type: "text",
text: `Status da operação: ${response.status}
Mensagem: ${data.message || "Status desconhecido"}`,
},
],
};
}
);
Ferramenta 6: Obter plugins em todos os sites
Quando você gerencia muitos sites WordPress, os plugins geralmente são onde as coisas começam a divergir. Esta ferramenta resolve isso verificando os plugins em toda a conta da empresa, e não um site por vez.
A API retorna muitas informações, incluindo em quais ambientes cada plugin está instalado, se há atualizações disponíveis e se uma versão está marcada como vulnerável. Para trabalhar com esses dados, definimos as seguintes estruturas:
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;
}
interface Plugin {
name: string;
title: string;
latest_version: string | null;
is_latest_version_vulnerable: boolean;
environment_count: number;
update_count: number;
environments: PluginEnvironment[];
}
interface GetPluginsResponse {
company: {
plugins: {
total: number;
items: Plugin[];
};
};
}
A ferramenta em si não recebe nenhuma entrada:
server.registerTool(
"get_plugins",
{
description:
"Pega todos os plugins do WordPress em todos os sites. Mostra quais plugins têm atualizações disponíveis ou vulnerabilidades de segurança.",
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: "Nenhum plugin encontrado." }
],
};
}
const sorted = [...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} site(s) precisam de atualização`
: "✅ Em dia";
const vulnerável =
plugin.is_latest_version_vulnerable ? " 🔴 VULNERÁVEL" : "";
return `• ${plugin.title} (${plugin.name})${vulnerável}
Versão mais recente: ${plugin.latest_version || "desconhecida"}
Instalado em: ${plugin.environment_count} ambiente(s)
${status}`;
}).join("\n\n");
const outdatedCount = plugins.filter(
(p) => p.update_count > 0
).length;
return {
content: [
{
type: "text",
text: `Encontrados ${data.company.plugins.total} plugins (${outdatedCount} têm atualizações disponíveis):\n\n${pluginList}`,
},
],
};
}
);
Ferramenta 7: Verificar temas em todos os sites
Os temas têm problemas semelhantes aos dos plugins, mas costumam ser verificados com ainda menos frequência. Esta ferramenta funciona da mesma forma que a ferramenta de plugins, mas foca nos temas WordPress.
A estrutura da resposta espelha o endpoint de plugins, apenas com campos específicos de temas:
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;
contagem_de_ambientes: número;
contagem_de_atualizações: número;
ambientes: ThemeEnvironment[];
}
interface GetThemesResponse {
empresa: {
temas: {
total: número;
itens: Theme[];
};
};
}
Ferramenta 8: Atualizar plugin
Listar problemas é útil, mas, eventualmente, você precisa corrigi-los. Essa ferramenta permite que você atualize um plugin específico em um ambiente específico.
O endpoint de atualização retorna a mesma forma de operação assíncrona usada anteriormente, então podemos pular essa parte. Aqui está a definição da ferramenta:
server.registerTool(
"update_plugin",
{
description:
"Atualiza um plugin específico para uma nova versão em um ambiente do site.",
inputSchema: {
environment_id: z
.string()
.describe("O ID do ambiente onde o plugin está instalado"),
plugin_name: z
.string()
.describe("O nome/slug do plugin (por exemplo, 'akismet', 'elementor')"),
update_version: z
.string()
.describe("A versão para a qual atualizar (por exemplo, '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: `Atualização do plugin iniciada!
Plugin: ${plugin_name}
Versão de destino: ${update_version}
ID da operação: ${data.operation_id}
Mensagem: ${data.message}
Use get_operation_status para verificar o progresso.`,
},
],
};
}
);
Assim como a limpeza do cache e a clonagem de sites, as atualizações são executadas de forma assíncrona. Retornar o ID da operação permite que o assistente de IA acompanhe o progresso, em vez de presumir que a atualização terminou instantaneamente.
Executando o servidor
Com todas as ferramentas registradas, o último passo é iniciar o servidor MCP e disponibilizá-lo para um cliente de IA.
No final do seu arquivo, adicione a função main que conecta o servidor usando o transporte STDIO:
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Servidor Kinsta MCP em execução no stdio");
}
main().catch((error) => {
console.error("Erro fatal:", error);
process.exit(1);
});
Isso instrui o servidor MCP a aguardar requisições por entrada e saída padrão. Isso torna o servidor detectável por clientes desktop compatíveis com MCP.
Um detalhe importante aqui é o logging. Como este servidor se comunica por STDIO, todos os logs devem ser direcionados para stderr. Escrever no stdout pode interferir nas mensagens MCP e interromper a conexão.
Em seguida, faça o build do projeto:
npm run build
Isso compila os arquivos TypeScript no diretório build e torna o ponto de entrada executável.
Quando a compilação terminar, o servidor estará pronto para ser iniciado por um cliente MCP. Você pode acessar o código completo no GitHub.
Testando seu servidor com o Claude for Desktop
Para usar seu servidor MCP, o Claude for Desktop precisa saber como iniciá-lo. Abra o arquivo de configuração do Claude Desktop:
~/Library/Application Support/Claude/claude_desktop_config.json
Crie o arquivo se ele ainda não existir. Se você estiver usando o VS Code, pode abri-lo diretamente pelo terminal:
code ~/Library/Application\ Support/Claude/claude_desktop_config.json
Dentro do arquivo, adicione o seu servidor MCP na chave mcpServers. Por exemplo:
{
"mcpServers": {
"kinsta": {
"command": "node",
"args": ["/ABSOLUTE/PATH/TO/mcp-server-demo-kinsta-api/build/index.js"],
"env": {
"KINSTA_API_KEY": "tua-chave-api-aqui",
"KINSTA_COMPANY_ID": "tua-id-da-empresa-aqui"
}
}
}
}
Esta configuração informa ao Claude for Desktop que há um servidor MCP chamado kinsta, que ele deve ser iniciado usando Node.js e que o ponto de entrada é o arquivo index.js compilado.
Certifique-se de que o caminho aponta para o arquivo compilado no diretório de build, não para o código-fonte TypeScript. Salve o arquivo e reinicie o Claude for Desktop.
Verificando a conexão
Após o reinício do Claude, abra um novo chat. Clique no ícone + ao lado do campo de input e, em seguida, passe o cursor sobre Connectors. Você deverá ver o seu servidor MCP listado.

Com o servidor conectado, você já pode começar a usá-lo. O Claude decide qual ferramenta usar, passa o input necessário e retorna o resultado como texto simples.

Uma forma diferente de trabalhar com as ferramentas que você já tem
O que está mudando agora não são as ferramentas em si. As APIs ainda são APIs. As plataformas de hospedagem ainda funcionam da mesma forma. O que está mudando é a maneira como interagimos com elas.
As ferramentas de IA estão começando a parecer menos caixas de chat e mais interfaces. Este servidor MCP é um pequeno exemplo dessa mudança. Ele não introduz novas funcionalidades. Ele expõe as existentes de uma forma que se adapta à maneira como as pessoas realmente trabalham.
Para onde isso vai a seguir depende de você. Você pode manter as coisas simples e somente leitura. Pode adicionar mais automação com aprovações e guardrails. Ou pode conectar o mesmo servidor a outras ferramentas no seu fluxo de trabalho.
À medida que você explora novas ferramentas e fluxos de trabalho como este, ter uma base sólida de hospedagem é fundamental. A última coisa que você quer é perder tempo lidando com quedas ou problemas de desempenho em vez de criar e melhorar os seus sites.
A Kinsta oferece hospedagem gerenciada para WordPress que mantém seus sites funcionando de forma confiável, mesmo quando você está off-line. Você pode explorar nossos planos de hospedagem ou falar com nossa equipe de vendas para encontrar o plano ideal para você.