Crie um servidor MCP para Claude Code e GitHub Copilot
Crie um único servidor MCP em TypeScript e registre-o no Claude Code e no GitHub Copilot. Você leva o código funcional da ferramenta, a configuração exata de cada cliente, uma rotina de depuração com o MCP Inspector e uma ferramenta de imagem que chama a API do PicassoIA.
Você escreveu um script que economiza dez minutos por dia, e agora quer que seu assistente de IA o execute sem ter que copiar e colar. Construa-o uma vez como um servidor Model Context Protocol e tanto o Claude Code quanto o GitHub Copilot podem chamar as mesmas ferramentas, porque o MCP é a linguagem comum que eles usam para se comunicar com qualquer coisa fora do editor. Este passo a passo cria um pequeno servidor em TypeScript, registra-o no Claude Code, registra-o no Copilot dentro do VS Code e termina com uma ferramenta de imagem real que chama a API do PicassoIA. Reserve cerca de 40 minutos e cerca de 100 linhas de código.
Por que um servidor é melhor que dois
Antes do MCP, cada assistente exigia seu próprio formato de plugin, seu próprio manifesto e suas próprias regras de empacotamento. Um servidor MCP substitui todo esse acúmulo por um único processo que anuncia o que pode fazer. O cliente inicia o processo, pede a lista de capacidades e entrega essa lista ao modelo. O modelo então decide, no meio da conversa, quando uma chamada vale a pena.
Mesmo protocolo, dois clientes
Um servidor pode expor três tipos de capacidade:
Ferramentas: funções que o modelo pode chamar, como add_note ou generate_image.
Recursos: dados somente leitura que o cliente pode anexar a uma conversa, como um arquivo de log ou um schema.
Prompts: modelos reutilizáveis que o usuário aciona de propósito.
As ferramentas concentram quase todo o valor hoje, então este artigo se concentra nelas. Tanto o Claude Code quanto o Copilot falam as mesmas mensagens JSON-RPC pelos mesmos transportes, o que significa que um servidor que funciona com um cliente vai funcionar com o outro praticamente sem mudanças.
Onde as configurações diferem
O servidor é idêntico. O registro não é. Aqui está toda a diferença em uma tabela:
Configuração
Claude Code
GitHub Copilot no VS Code
Arquivo de configuração
.mcp.json no projeto, ou ~/.claude.json
.vscode/mcp.json, ou seu perfil de usuário
Propriedade raiz
mcpServers
servers
Adicionar pelo terminal
claude mcp add
Paleta de Comandos: MCP: Add Server
Campo de transporte
type (stdio, http, sse)
type é obrigatório (stdio ou http)
Segredos
Flag --env ou expansão de ${VAR}
Bloco inputs com ${input:id}
Onde as ferramentas rodam
Qualquer sessão
modo Agent apenas
💡 Dica: A propriedade raiz é a armadilha clássica. Cole uma configuração do Claude Code no VS Code sem alterações e nada é carregado, porque o Copilot procura servers, não mcpServers.
Preparar o projeto
Escolha uma pasta fora do seu repositório principal para que o servidor possa atender vários projetos depois. Você precisa do Node.js 20 ou mais recente e de um terminal.
Os exemplos usam a API 1.x do @modelcontextprotocol/sdk com McpServer e registerTool. Se uma versão principal mais nova alterar um caminho de importação, os conceitos abaixo continuam os mesmos.
Começar com stdio
O MCP define dois transportes principais. stdio significa que o cliente inicia seu servidor como um processo filho e troca mensagens pela entrada e saída padrão. HTTP em streaming significa que o servidor roda por conta própria e os clientes se conectam por URL. Comece com stdio. Ele não precisa de porta, de camada de autenticação nem de hospedagem, e os dois clientes o suportam de imediato. Passe para HTTP apenas quando várias pessoas precisarem compartilhar uma mesma instância em execução.
Escrever o servidor
Nosso exemplo é um pequeno servidor de notas de equipe com duas ferramentas: uma salva uma nota, a outra pesquisa nelas. Ele é pequeno o bastante para ler em um minuto e real o bastante para ser útil.
Registrar uma ferramenta
Crie src/index.ts:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { promises as fs } from "node:fs";
import path from "node:path";
const NOTES_FILE = path.join(process.env.NOTES_DIR ?? process.cwd(), "notes.json");
type Note = { id: number; text: string; tags: string[]; createdAt: string };
async function readNotes(): Promise<Note[]> {
try {
return JSON.parse(await fs.readFile(NOTES_FILE, "utf8"));
} catch {
return [];
}
}
const server = new McpServer({ name: "team-notes", version: "1.0.0" });
server.registerTool(
"add_note",
{
title: "Add note",
description:
"Save a short engineering note with optional tags. Use it when the user asks to remember a decision, a command or a bug.",
inputSchema: {
text: z.string().min(3).max(2000),
tags: z.array(z.string()).default([]),
},
},
async ({ text, tags }) => {
const notes = await readNotes();
const note: Note = {
id: notes.length + 1,
text,
tags,
createdAt: new Date().toISOString(),
};
await fs.writeFile(NOTES_FILE, JSON.stringify([...notes, note], null, 2));
return { content: [{ type: "text", text: `Saved note #${note.id}` }] };
}
);
server.registerTool(
"search_notes",
{
title: "Search notes",
description: "Find saved notes whose text or tags contain the query.",
inputSchema: { query: z.string().min(1) },
},
async ({ query }) => {
const q = query.toLowerCase();
const hits = (await readNotes()).filter(
(n) =>
n.text.toLowerCase().includes(q) ||
n.tags.some((t) => t.toLowerCase().includes(q))
);
const text = hits.length
? hits.map((n) => `#${n.id} [${n.tags.join(", ")}] ${n.text}`).join("\n")
: "No notes matched.";
return { content: [{ type: "text", text }] };
}
);
await server.connect(new StdioServerTransport());
console.error("team-notes MCP server running on stdio");
Execute npm run build. Agora você tem dist/index.js, e esse arquivo é a única coisa que os dois clientes precisam saber.
Retornar resultados limpos
O modelo lê tudo o que você retorna, então trate o valor de retorno como uma interface. Mantenha os resultados curtos, estruturados e exatos. Quando algo falhar, não lance uma exceção que morra na camada de transporte. Retorne um erro que o modelo possa ler e reagir a ele:
return {
isError: true,
content: [{ type: "text", text: "notes.json is not valid JSON. Fix or delete it." }],
};
Um resultado isError permite que o assistente explique o problema para você ou tente de novo com outra entrada. Uma falha só mostra um vago aviso de "servidor desconectado".
Mantenha o stdout em silêncio
Este é o motivo mais comum de um primeiro servidor falhar. Com stdio, a saída padrão pertence ao protocolo. Um único console.log perdido injeta texto simples no fluxo JSON-RPC e o cliente derruba a conexão. Registre logs em console.error, que escreve no stderr, e os dois clientes vão capturá-lo como saída de diagnóstico.
💡 Dica: Escreva as descrições das ferramentas como instruções para o modelo, não como documentação para humanos. "Use quando o usuário pedir para lembrar de uma decisão" faz a ferramenta ser escolhida no momento certo. "Utilitário de notas" não faz.
Conectar o Claude Code
Adicionar com a CLI
Um comando registra o servidor. As opções vêm antes do nome, e um duplo hífen separa o nome do comando que o Claude Code vai iniciar:
claude mcp add --transport stdio --scope user \
--env NOTES_DIR=/home/dev/notes \
team-notes -- node /absolute/path/to/team-notes-mcp/dist/index.js
Use um caminho absoluto. O Claude Code inicia o processo a partir do diretório que sua sessão usa, então caminhos relativos quebram assim que você abre outro projeto. Depois confirme:
claude mcp list
claude mcp get team-notes
Dentro de uma sessão, digite /mcp para ver o status da conexão e a lista de ferramentas. Faça uma pergunta natural, como "Lembre que fazemos deploy às quintas, marque como release", e observe o Claude Code pedir permissão para chamar add_note.
Compartilhar pelo .mcp.json
A flag de escopo decide quem recebe o servidor. local mantém ele privado para você em um projeto, user o torna disponível em todos os projetos, e project grava um arquivo .mcp.json que você pode versionar para que a equipe inteira o receba. Aqui está uma configuração compartilhada que evita caminhos fixos no código:
Cada colega define TEAM_NOTES_PATH uma vez no shell. A forma ${NOTES_DIR:-.notes} fornece um valor padrão quando a variável não existe. O Claude Code pede aprovação na primeira vez que vê um servidor com escopo de projeto, o que é uma salvaguarda sensata para qualquer coisa baixada de um repositório.
Conectar o GitHub Copilot
Escrever o .vscode/mcp.json
Crie .vscode/mcp.json no seu workspace. Lembre-se da propriedade raiz diferente e do type obrigatório:
${workspaceFolder} torna o arquivo portável, para que você possa versioná-lo. Para segredos, adicione um array inputs. O VS Code pede uma vez, armazena o valor com segurança e o injeta:
O Copilot Chat abre no modo Ask por padrão, e as ferramentas MCP só disparam no modo Agent. Troque o modo no painel de chat, abra o seletor de ferramentas e confirme que team-notes aparece com as duas ferramentas marcadas. Se não aparecer, execute MCP: List Servers na Paleta de Comandos, escolha o servidor e leia a saída dele. Reinicie-o pelo mesmo menu após cada recompilação.
O Copilot também permite escolher entre os modelos que seu plano oferece, então o mesmo servidor é testado por modelos diferentes. É uma forma barata de verificar se as descrições das suas ferramentas são claras o bastante para todos eles.
Testar e depurar antes de publicar
Executar o MCP Inspector
Antes de culpar qualquer um dos clientes, teste o servidor sozinho. O Inspector oficial abre uma página web local onde você pode listar ferramentas, preencher argumentos e ver as respostas brutas:
Chame add_note com um text vazio. Seu schema Zod deve rejeitá-lo com uma mensagem de validação legível. Depois chame search_notes com uma tag que você acabou de salvar. Se os dois se comportarem aqui, qualquer problema restante está na configuração do cliente, não no seu código.
Corrigir as falhas mais comuns
Sintoma
Causa provável
Correção
Servidor nunca conecta
Um console.log escreveu no stdout
Troque por console.error
"Command not found"
Caminho relativo ou build ausente
Use um caminho absoluto e execute npm run build
Ferramentas ausentes no Copilot
O chat está no modo Ask
Mude para o modo Agent
Ferramenta existe, mas nunca é escolhida
Descrição vaga
Reescreva com frases de gatilho
Variável de ambiente vazia
Não declarada na configuração
Adicione-a ao bloco env
Chamadas de imagem falham sob carga
Mais de 5 jobs ao mesmo tempo
Enfileire as chamadas dentro da ferramenta
Dê ao seu servidor uma ferramenta de imagem
Notas são úteis, mas a melhor demonstração do MCP é uma ferramenta que faz algo que o assistente não consegue fazer sozinho. A geração de imagens é uma boa escolha: o modelo escreve um prompt preciso, seu servidor o transforma em um arquivo, e a URL volta direto para a conversa.
Chamar a API do PicassoIA
A API para desenvolvedores do PicassoIA fica em https://api.picassoia.com/v1 e usa um token Bearer que começa com pia_sk_. As previsões são assíncronas, no estilo Replicate: você cria uma e depois consulta até que o status mostre succeeded. O modelo PicassoIA Image aceita um prompt de até 4.000 caracteres, um aspect_ratio e retorna uma lista de URLs de imagens. Adicione esta ferramenta antes da linha server.connect:
Uma conta permite 5 previsões simultâneas, compartilhadas entre todos os tokens e conexões, então um loop que dispara dez imagens de uma vez vai bater nesse teto. Gere-as uma após a outra dentro da ferramenta, ou mantenha uma pequena fila.
💡 Dica: Verifique os preços e os requisitos de plano atuais na página da API do PicassoIA antes de publicar um servidor para outras pessoas. A documentação e a página de preços descrevem o acesso de formas diferentes, então confirme o que o seu próprio plano inclui.
Como usar o Sonnet 5 no PicassoIA
Suas descrições de ferramentas são prompts, e um modelo de linguagem é o melhor editor para elas. O Claude Sonnet 5 é uma ótima escolha para essa tarefa, e você pode executá-lo no PicassoIA sem sair do navegador.
Abra a página do Claude Sonnet 5 na coleção de modelos de linguagem.
Cole as definições das suas ferramentas, incluindo nomes, descrições e schemas, no prompt.
Peça para reescrever cada descrição como uma instrução curta que diga quando chamar a ferramenta e o que ela retorna.
Peça dez entradas de casos extremos por ferramenta, como strings vazias, textos muito longos e tags incomuns.
Execute essas entradas no MCP Inspector, corrija todas as falhas e cole as descrições melhoradas de volta no seu código.
Para uma segunda opinião sobre lógica complicada, o Claude Fable 5 e o GPT 5.6 Sol estão ambos listados para tarefas de programação. Comparar as reescritas deles de uma mesma descrição costuma revelar qual formulação é ambígua.
Experimente você mesmo no PicassoIA
Agora você tem um servidor rodando em dois assistentes: notas para memória, uma ferramenta de imagem para saída e uma rotina de testes que mantém os dois sob controle. O mesmo padrão escala para qualquer coisa que você possa encapsular em uma função, de scripts de deploy a consultas em banco de dados.
Comece pela ferramenta de imagem, porque ela dá retorno instantâneo. Escreva um prompt, chame generate_image a partir do Claude Code ou do Copilot e veja o resultado em segundos. Depois abra a página PicassoIA Image e teste proporções e estilos de prompt diretamente, ou navegue por todos os modelos disponíveis em picassoia.com/en/all-models. Sua primeira imagem está a um prompt de distância.