MCPTypeScriptGitHub CopilotVS CodeAI

Como criar um servidor MCP personalizado em TypeScript para estender o GitHub Copilot

Sloth255
Sloth255
·12 min read·2,525 words

Introdução

MCP (Model Context Protocol) é um protocolo criado pela Anthropic para conectar modelos de IA a ferramentas e fontes de dados externas. GitHub Copilot, Claude, Cursor e outras ferramentas de IA já oferecem suporte ao protocolo.

Um servidor MCP próprio atende a necessidades como chamar uma API interna pelo Copilot ou permitir que a IA execute comandos específicos de um projeto.

Este artigo mostra como implementar um servidor MCP em TypeScript e conectá-lo ao GitHub Copilot no VS Code. O conteúdo vai da configuração inicial ao desenho das ferramentas e às práticas de operação.


Conceitos básicos do MCP

flowchart TD
  A[Agente do Copilot no VS Code] -->|MCP| B[Servidor MCP]
  B --> C[Ferramentas externas]
  B --> D[API]
  B --> E[Banco de dados]

Um servidor MCP pode disponibilizar três tipos principais de capacidades.

Tipo Descrição Exemplos
Ferramentas Funções que o LLM pode invocar Chamadas de API, operações em arquivos
Recursos Dados disponíveis para leitura Documentos, arquivos de configuração
Prompts Modelos de prompt reutilizáveis Revisão de código, investigação de incidentes

A comunicação ocorre por JSON-RPC sobre stdio ou Streamable HTTP.


Arquitetura e princípios de design do MCP

Por que o MCP foi criado

Antes do MCP, cada cliente de IA implementava sua própria integração com sistemas externos. Para usar a mesma API interna em várias ferramentas de IA, era preciso criar uma integração separada para cada cliente.

[Antes do MCP]
GitHub Copilot -> Integração personalizada A -> API interna
Claude         -> Integração personalizada B -> API interna (duplicada)
Cursor         -> Integração personalizada C -> API interna (duplicada)

[Com MCP]
GitHub Copilot -+
Claude         +-> Servidor MCP -> API interna (uma implementação)
Cursor         -+

O MCP resolve essa fragmentação por meio de um protocolo padrão. Depois de implementar um servidor, não é necessário reimplementar a mesma capacidade para cada cliente. Na prática, os recursos disponíveis ainda dependem da versão do MCP, do transporte e dos métodos de autenticação compatíveis com cada cliente de IA.

O princípio central: LLMs raciocinam; servidores executam

O princípio central do MCP é a separação de responsabilidades.

  • LLM (como o Copilot): interpreta o contexto e decide qual ferramenta chamar e quais argumentos fornecer
  • Servidor MCP: executa a ferramenta e retorna os resultados

Um LLM não acessa sistemas externos diretamente. Cada chamada passa por uma interface explícita de ferramenta, o que permite centralizar autenticação, validação e registros no servidor.

sequenceDiagram
  participant U as Usuário
  participant L as LLM (Copilot)
  participant S as Servidor MCP
  participant A as API externa

  U->>L: "Verifique as issues abertas"
  L->>S: tools/call search_github_issues
  S->>A: GET /search/issues
  A-->>S: JSON de resultado
  S-->>L: Resultado da execução da ferramenta
  L-->>U: Resposta formatada

Comunicação com JSON-RPC 2.0

A comunicação usa JSON-RPC 2.0. Após a conexão, o fluxo de mensagens segue estas três etapas.

Etapa Método Descrição
1 initialize Cliente e servidor trocam versões e capacidades compatíveis
2 tools/list O cliente obtém a lista de ferramentas, com nomes e esquemas
3 tools/call O cliente pede a execução de uma ferramenta

Veja um exemplo de chamada e resposta para search_github_issues:

// Chamada
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "search_github_issues",
    "arguments": {
      "query": "authentication",
      "repo": "owner/my-repo",
      "state": "open"
    }
  }
}
// Resposta
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "[{\"number\": 42, \"title\": \"Fix OAuth token refresh\", ...}]"
      }
    ]
  }
}

O LLM recebe esse content, transforma-o em uma resposta em linguagem natural e a devolve ao usuário.

O papel dos transportes

O MCP define dois transportes padrão: stdio e Streamable HTTP. O transporte HTTP+SSE mais antigo permanece por compatibilidade com versões anteriores, mas novas implementações devem usar Streamable HTTP.

stdio Streamable HTTP
Mecanismo Inicia o servidor como subprocesso e se comunica pela entrada e saída padrão Envia e recebe JSON-RPC por HTTP POST/GET, usando SSE para streaming quando necessário
Indicado para Desenvolvimento local e uso individual Compartilhamento em equipe, gestão centralizada e vários clientes
Custo inicial Baixo (pode iniciar a cada chamada) Pressupõe um serviço em execução contínua

Para uso local no VS Code, stdio costuma ser a escolha adequada. A seção “Escolhendo entre stdio e Streamable HTTP” retoma essa decisão mais adiante.


Quando criar um MCP em vez de usar habilidades

O GitHub Copilot no VS Code tem dois mecanismos para estender seu comportamento com arquivos Markdown. Arquivos de instruções personalizados (.github/copilot-instructions.md, .instructions.md e outros) definem orientações enviadas à IA, enquanto habilidades do agente (SKILL.md) descrevem fluxos de trabalho reutilizáveis que podem incluir procedimentos, scripts e recursos de apoio. Neste artigo, ambos são chamados de “personalização baseada em instruções”. Antes de criar um servidor MCP, avalie se esses mecanismos são suficientes.

Casos atendidos por personalização baseada em instruções

  • Fornecer à IA informações estáticas, como convenções de código ou estrutura do projeto
  • Reutilizar um procedimento conhecido como modelo de prompt
  • Definir um fluxo de trabalho que não exija acesso a sistemas externos

A personalização baseada em instruções não exige um servidor dedicado. Scripts incluídos em uma Skill podem ser executados por um agente com ferramentas existentes, como o terminal, mas a disponibilidade da execução, as aprovações e o formato da saída dependem dos recursos do ambiente hospedeiro. Esse deve ser o primeiro caminho a avaliar.

Habilidades também podem ensinar a chamar uma API externa

Quando o requisito é “usar uma API externa”, você pode documentar a chamada em Skills e fazer com que o Copilot gere código ou um comando curl.

<!-- Exemplo de SKILL.md -->
O endpoint da API interna de status é https://api.internal/status
Para autenticação, especifique um token Bearer no cabeçalho Authorization.
Exemplo de chamada com curl: curl -H "Authorization: Bearer $TOKEN" https://api.internal/status

Essa abordagem funciona quando o objetivo é que o Copilot gere código. Em ambientes nos quais um agente pode chamar uma API pelo terminal ou por uma extensão existente, o procedimento de execução, aprovação e tratamento dos resultados continua dependente do host.

O ponto em que o MCP se torna necessário

As habilidades deixam de ser suficientes quando uma das situações abaixo se aplica.

Situação Personalização baseada em instruções MCP
Ensinar o Copilot a chamar uma API Sim
Definir um procedimento para executar uma API com ferramentas fornecidas pelo host Sim
Expor uma API como ferramenta tipada e dedicada Não Sim
Padronizar autenticação, validação de entrada e formato de resultado no servidor Não Sim
Expor a mesma capacidade a vários clientes MCP Não Sim

As habilidades ensinam ao agente o que fazer e em qual ordem. O MCP, por sua vez, fornece um contrato de ferramenta tipada e dedicada que os clientes podem chamar. Com o MCP, o servidor executa as chamadas e devolve resultados em um formato definido para o contexto do Copilot. Esse controle e essa portabilidade orientam a decisão.

Casos que exigem um servidor MCP

Crie um servidor MCP quando requisitos como os abaixo forem difíceis de controlar apenas com personalização baseada em instruções.

Requisito Razão
Usar os resultados de uma API no raciocínio do Copilot Os resultados da execução entram diretamente no contexto, permitindo perguntas complementares e transformações
Acessar recursos autenticados Tokens e credenciais podem permanecer em variáveis de ambiente, sem serem expostos aos usuários
Executar operações de escrita ou atualização Operações com efeitos colaterais, como criar issues, atualizar rótulos ou alterar registros
Obter dados em tempo real Recupera o estado atual do CI ou valores recentes do banco de dados que não cabem em arquivos estáticos
Tratar dados grandes ou dinâmicos Documentos grandes demais para prompts ou dados que mudam com frequência
Executar processamento ou conversões complexas Análise de arquivos, agregação e conversão de formato que devem ocorrer no código
Compartilhar entre projetos ou equipes Centraliza a gestão de ferramentas chamadas por vários desenvolvedores e repositórios

Fluxo de decisão

flowchart TD
  A[Há algo que você quer fazer] --> B{É preciso acessar um<br/>sistema externo?}
  B -->|Não| C[A personalização baseada em instruções pode resolver]
  B -->|Sim| D{O Copilot precisa receber<br/>o resultado e raciocinar sobre ele?}
  D -->|Não: gerar código basta| C
  D -->|Sim| E{Há autenticação, efeitos colaterais<br/>ou dados dinâmicos?}
  E -->|Não| F[Verifique se um servidor MCP existente<br/>pode atender ao caso]
  E -->|Sim| G[Crie um servidor MCP]
  F -->|Se não houver um| G

O principal limite entre a personalização baseada em instruções e o MCP é a necessidade de controlar, no servidor, um contrato de ferramenta dedicado, a autenticação e a validação de entrada.


Configuração

mkdir my-mcp-server
cd my-mcp-server
npm init -y
npm install @modelcontextprotocol/server zod
npm install -D typescript @types/node tsx
// tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "Node16",
    "moduleResolution": "Node16",
    "outDir": "./dist",
    "strict": true
  },
  "include": ["src/**/*"]
}
// package.json (trecho)
{
  "type": "module",
  "scripts": {
    "dev": "tsx src/index.ts",
    "build": "tsc",
    "start": "node dist/index.js"
  }
}

Implementação de um servidor MCP básico

// src/index.ts
import { McpServer } from '@modelcontextprotocol/server'
import { serveStdio } from '@modelcontextprotocol/server/stdio'
import * as z from 'zod/v4'
import { fetchWeather } from './tools/weather.js'

interface GithubIssueSearchResponse {
  items: Array<{
    number: number
    title: string
    state: string
    html_url: string
  }>
}

function createServer(): McpServer {
  const server = new McpServer({
    name: 'my-custom-tools',
    version: '1.0.0',
  })

  server.registerTool(
    'get_weather',
    {
      description: 'Obtém o clima atual da cidade especificada',
      inputSchema: z.object({
        city: z.string().describe('Nome da cidade para consultar o clima (ex.: Tokyo, Osaka)'),
      }),
    },
    async ({ city }) => {
      const weather = await fetchWeather(city)
      return {
        content: [{
          type: 'text',
          text: `Clima atual em ${city}: ${weather.description}, temperatura: ${weather.temp}°C`,
        }],
      }
    }
  )

  server.registerTool(
    'search_github_issues',
    {
      description: 'Pesquisa issues em um repositório do GitHub',
      inputSchema: z.object({
        query: z.string().describe('Consulta de pesquisa'),
        repo: z.string().regex(/^[^/]+\/[^/]+$/).describe('Nome do repositório (formato owner/repo)'),
        state: z.enum(['open', 'closed', 'all']).default('open').describe('Estado da issue'),
      }),
    },
    async ({ query, repo, state }) => {
      const token = process.env.GITHUB_TOKEN
      if (!token) {
        return {
          content: [{ type: 'text', text: 'GITHUB_TOKEN não está configurado' }],
          isError: true,
        }
      }

      const stateQualifier = state === 'all' ? '' : ` state:${state}`
      const url = new URL('https://api.github.com/search/issues')
      url.searchParams.set(
        'q',
        `${query} is:issue repo:${repo}${stateQualifier}`
      )

      const response = await fetch(url.toString(), {
        headers: {
          Authorization: `Bearer ${token}`,
          Accept: 'application/vnd.github.v3+json',
        },
      })

      if (!response.ok) {
        return {
          content: [{ type: 'text', text: `GitHub API error: ${response.status}` }],
          isError: true,
        }
      }

      const data = await response.json() as GithubIssueSearchResponse
      const issues = data.items.slice(0, 5).map((issue) => ({
        number: issue.number,
        title: issue.title,
        state: issue.state,
        url: issue.html_url,
      }))

      return {
        content: [{ type: 'text', text: JSON.stringify(issues, null, 2) }],
      }
    }
  )

  return server
}

void serveStdio(createServer)
console.error('O servidor MCP foi iniciado')

Exemplos práticos de implementação de ferramentas

API de clima (chamada de API externa)

// src/tools/weather.ts
interface WeatherData {
  description: string
  temp: number
  humidity: number
}

export async function fetchWeather(city: string): Promise<WeatherData> {
  const apiKey = process.env.OPENWEATHER_API_KEY
  if (!apiKey) throw new Error('OPENWEATHER_API_KEY não está configurada')

  const url = `https://api.openweathermap.org/data/2.5/weather?q=${encodeURIComponent(city)}&appid=${apiKey}&units=metric&lang=ja`

  const response = await fetch(url)
  if (!response.ok) {
    throw new Error(`Falha ao obter dados meteorológicos: ${response.statusText}`)
  }

  const data = await response.json()
  return {
    description: data.weather[0].description,
    temp: Math.round(data.main.temp),
    humidity: data.main.humidity,
  }
}

Pesquisa de documentos internos (arquivos locais)

// src/tools/docs-search.ts
import * as fs from 'fs/promises'
import * as path from 'path'
import { pathToFileURL } from 'url'

interface DocResult {
  title: string
  excerpt: string
  url: string
}

export async function searchDocs(
  query: string,
  limit: number
): Promise<DocResult[]> {
  const docsDir = process.env.DOCS_DIR ?? './docs'
  const files = await fs.readdir(docsDir)
  const results: DocResult[] = []

  for (const file of files.filter((f) => f.endsWith('.md'))) {
    const content = await fs.readFile(path.join(docsDir, file), 'utf-8')

    if (content.toLowerCase().includes(query.toLowerCase())) {
      const lines = content.split('\n')
      // Ignore o frontmatter (o primeiro bloco ---) e use o primeiro H1 como título
      let bodyStart = 0
      if (lines[0]?.trim() === '---') {
        const end = lines.findIndex((l, i) => i > 0 && l.trim() === '---')
        bodyStart = end >= 0 ? end + 1 : 0
      }
      const h1 = lines.slice(bodyStart).find((l) => /^#\s/.test(l))
      const title = h1 ? h1.replace(/^#\s*/, '') : path.basename(file, '.md')
      const matchIndex = content.toLowerCase().indexOf(query.toLowerCase())
      const excerpt = content.slice(
        Math.max(0, matchIndex - 50),
        matchIndex + 150
      )

      results.push({
        title,
        excerpt,
        url: pathToFileURL(path.resolve(docsDir, file)).toString(),
      })

      if (results.length >= limit) break
    }
  }

  return results
}

Implementação de recursos

Os recursos fornecem dados somente leitura que a IA pode consultar. São adequados para informações que o modelo precisa acessar de forma confiável, como políticas internas, procedimentos operacionais e documentação de API.

// Adicione no início de src/index.ts
import { readFile } from 'node:fs/promises'
import path from 'node:path'

// Adicione antes de return server em createServer
server.registerResource(
  'project-guide',
  'docs://project-guide',
  {
    title: 'Guia do projeto',
    description: 'Guia do projeto para consultar durante o desenvolvimento',
    mimeType: 'text/markdown',
  },
  async (uri) => {
  const docsDir = process.env.DOCS_DIR ?? path.join(process.cwd(), 'docs')
  const content = await readFile(
    path.join(docsDir, 'project-guide.md'),
    'utf-8'
  )

  return {
    contents: [
      {
        uri: uri.href,
        mimeType: 'text/markdown',
        text: content,
      },
    ],
  }
  }
)

Bons candidatos a recursos:

  • Guias de design e convenções de codificação
  • Runbooks operacionais
  • Resumos de especificações de API
  • Perguntas frequentes internas

O principal benefício é poder consultar essas informações sem inseri-las em cada conversa.


Uso de prompts

Além de ferramentas, o MCP pode fornecer prompts. Eles são úteis para reutilizar procedimentos e critérios de revisão.

  • Prompt para análise de segurança
  • Prompt para gerar descrições de pull requests
  • Prompt para investigação inicial de incidentes

Pense nas ferramentas como meios de execução e nos prompts como padrões de pensamento para identificar cenários em que cada um se aplica.


Conectando ao VS Code

// .vscode/mcp.json
{
  "servers": {
    "my-custom-tools": {
      "type": "stdio",
      "command": "node",
      "args": ["${workspaceFolder}/dist/index.js"],
      "env": {
        "GITHUB_TOKEN": "${env:GITHUB_TOKEN}",
        "OPENWEATHER_API_KEY": "${env:OPENWEATHER_API_KEY}"
      }
    }
  }
}

Durante o desenvolvimento, é possível executá-lo diretamente com tsx, sem uma etapa de compilação.

{
  "servers": {
    "my-custom-tools-dev": {
      "type": "stdio",
      "command": "npx",
      "args": ["tsx", "${workspaceFolder}/src/index.ts"]
    }
  }
}

Uso no Copilot Chat

Depois de reiniciar o VS Code, as ferramentas MCP ficam disponíveis no Copilot Chat.

"Como está o tempo em Tóquio?"
  -> O Copilot chama automaticamente a ferramenta get_weather

"Encontre issues abertas sobre \"memory leak\""
  -> O Copilot chama automaticamente a ferramenta search_github_issues

Também é possível filtrar ferramentas pelo nome do servidor.

#my-custom-tools Liste as issues abertas sobre "authentication" em owner/my-repo
  -> Chama a ferramenta search_github_issues por meio deste servidor

Dicas de depuração

# Teste ferramentas interativamente com o MCP Inspector
npx @modelcontextprotocol/inspector npx tsx src/index.ts

Abra no navegador o URL exibido pelo Inspector para inspecionar visualmente as chamadas de ferramenta e os valores retornados.


Boas práticas para o design de ferramentas

Comece decidindo o que não permitir

As ferramentas MCP são úteis, mas concentrar poder demais em uma única ferramenta é arriscado.

Mau exemplo Bom exemplo
manage_project list_open_issues, update_issue_labels
run_anything get_pipeline_status
execute_sql search_customers

As ferramentas são mais confiáveis quando seus nomes deixam claros os efeitos colaterais, os parâmetros de entrada são poucos e o comportamento em caso de falha é simples de explicar.

Projete inputSchema com cuidado

O esquema de entrada de uma ferramenta não serve apenas para validação: também é a interface do LLM. Descrições vagas reduzem a precisão ao preencher argumentos.

const updateIssueInput = {
  type: 'object',
  properties: {
    repo: { type: 'string', pattern: '^[^/]+/[^/]+$' },
    issueNumber: { type: 'integer', minimum: 1 },
    labels: {
      type: 'array',
      items: { type: 'string' },
      maxItems: 10,
    },
  },
  required: ['repo', 'issueNumber', 'labels'],
  additionalProperties: false,
}

Pontos principais:

  • Escreva valores de description específicos
  • Use enum sempre que possível
  • Declare required explicitamente
  • Use additionalProperties: false para evitar parâmetros não intencionais

Separe as responsabilidades de Zod e JSON Schema

inputSchema é o contrato externo, enquanto Zod faz a revalidação em tempo de execução. Em especial, não dispense a revalidação no servidor para ferramentas que chamam uma API ou um banco de dados externo.

case 'update_issue': {
  // Defina no JSON Schema a estrutura exposta ao modelo
  // e valide com Zod os valores aceitos pelo servidor
  const input = z.object({
    repo: z.string().regex(/^[^/]+\/[^/]+$/),
    issueNumber: z.number().int().positive(),
    labels: z.array(z.string()).max(10),
  }).parse(args)
  // ...
}

Princípios para o tratamento de erros

Organizar os erros em pelo menos estas três categorias facilita o tratamento.

  • Erros de entrada (falhas de validação)
  • Erros de autenticação/autorização
  • Falhas de serviço externo
function toToolErrorMessage(error: unknown): string {
  if (error instanceof z.ZodError) {
    return 'A entrada é inválida. Verifique os campos obrigatórios e os formatos dos valores.'
  }

  if (error instanceof Error) {
    return `A execução da ferramenta falhou: ${error.message}`
  }

  return 'A execução da ferramenta falhou.'
}

As mensagens retornadas não devem revelar detalhes internos em excesso e precisam indicar claramente a próxima ação.


Gestão de credenciais

Nunca passe segredos em prompts ou argumentos; leia-os de variáveis de ambiente ou de um ambiente de execução seguro.

Faça Não
Leia tokens de API do ambiente Inclua segredos nos argumentos da ferramenta
Use tokens com o menor privilégio possível Grave tokens nos logs
Separe os ambientes de produção e desenvolvimento Inclua segredos em mensagens de erro

Mantenha as permissões mínimas

  • Use um token somente leitura para pesquisar issues do GitHub
  • Use um usuário somente com SELECT em uma ferramenta de leitura de banco de dados
  • Use apenas permissão de leitura de workflows em uma ferramenta de verificação de implantação

Para uso em equipe, é mais seguro separar ferramentas somente leitura das ferramentas de atualização e isolar as ferramentas destinadas à produção.


Projeto operacional

Timeouts, retentativas e falhas parciais

Um experimento local pode funcionar bem enquanto a produção permanece instável. Evite escrever código supondo que todas as chamadas terão sucesso.

async function withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {
  return await Promise.race([
    promise,
    new Promise<T>((_, reject) => {
      setTimeout(() => reject(new Error(`Timed out after ${ms}ms`)), ms)
    }),
  ])
}

Elementos mínimos:

  • Timeouts
  • Limites de retentativas
  • Limitação de taxa
  • Normalização de mensagens de erro

Observabilidade (design de logs)

Durante a depuração, saber apenas o que foi chamado não basta. Registre:

  • Nome da ferramenta
  • Horário da chamada e duração da execução
  • Categoria de sucesso ou falha e motivo da falha

Mas não registre em excesso: mantenha dados pessoais, tokens de acesso, instruções SQL completas e respostas completas da API externa fora dos logs.

Estratégia de teste

Separar as implementações das ferramentas em funções permite testar sua lógica fora do protocolo MCP.

// Separe a lógica de negócios para torná-la testável
export async function searchGithubIssues(input: SearchIssuesInput) {
  // Processamento real
}

server.registerTool(
  'search_github_issues',
  { inputSchema: SearchIssuesInputSchema },
  async (input) => formatResult(await searchGithubIssues(input))
)

Camadas de teste recomendadas:

  1. Testes unitários da validação de entrada
  2. Testes unitários dos manipuladores de ferramentas
  3. Testes de integração que incluam o transporte
  4. Verificação manual com o MCP Inspector

Escolhendo entre stdio e Streamable HTTP

Perspectiva stdio Streamable HTTP
Configuração local Muito simples Exige um pouco mais de estrutura
Auditoria central Fraco Forte
Governança da autenticação Depende de usuários individuais Mais simples
Depuração Fácil Mais fatores de rede
Distribuição npm/binário URL do serviço

Comece validando a usabilidade com stdio. Quando o compartilhamento em equipe ou a gestão centralizada se tornarem necessários, considere Streamable HTTP. Se o servidor for exposto por HTTP, implemente validação de origem, vinculação ao host local e autenticação.


Separando servidores MCP

Concentrar tudo em um servidor tende a ampliar suas responsabilidades. Dividir por unidades como estas facilita a operação.

  • Ferramentas relacionadas ao GitHub
  • Ferramentas relacionadas a APIs internas
  • Ferramentas de leitura de banco de dados
  • Servidores de recursos dedicados à documentação

A separação também facilita a organização de permissões e variáveis de ambiente.


Padrões de falha frequentes

1. Criar uma ferramenta genérica que pode fazer qualquer coisa

Ferramentas como run_anything ou execute_sql (SQL arbitrário) tendem a reduzir a segurança e a reprodutibilidade.

2. Deixar o inputSchema permissivo demais

O modelo tem dificuldade para montar argumentos de modo confiável, o que aumenta o número de chamadas malsucedidas.

3. Receber segredos como argumentos

Eles podem vazar facilmente por logs ou pelo histórico da conversa.

4. Misturar operações de leitura e atualização com a mesma permissão

Isso aumenta a probabilidade de incidentes operacionais.

5. Escrever implementações de ferramentas diretamente nos manipuladores

Isso torna as implementações difíceis de testar e manter.


Uma sequência prática de adoção

Para o primeiro servidor MCP, esta ordem é realista:

  1. Crie um servidor exclusivamente local com stdio
  2. Crie apenas uma ou duas ferramentas somente leitura
  3. Adicione recursos e prompts
  4. Melhore os logs e o tratamento de erros
  5. Adicione ferramentas de atualização cuidadosamente, apenas quando necessário

Essa sequência facilita o equilíbrio entre segurança e utilidade.

Bons candidatos iniciais:

  • Pesquisa de configuração do projeto
  • Pesquisa de documentos internos
  • Verificações de status do CI
  • Pesquisa de pull requests e issues

Adicione ferramentas de atualização somente quando a confiança dos usuários e o desenho de auditoria estiverem estabelecidos.


Resumo

  • Servidores MCP se comunicam por JSON-RPC via stdio ou Streamable HTTP, e o atual @modelcontextprotocol/server simplifica a implementação em TypeScript
  • A combinação de ferramentas (meios de execução), recursos (dados de referência) e prompts (padrões de pensamento) amplia as capacidades do Copilot
  • Registrar um servidor em .vscode/mcp.json permite chamá-lo pelo GitHub Copilot no VS Code
  • Comece o desenho da ferramenta decidindo o que não permitir; um inputSchema bem projetado melhora a precisão do raciocínio do LLM
  • Gerencie segredos por variáveis de ambiente e mantenha as permissões mínimas
  • Comece com stdio e uma ou duas ferramentas somente leitura; expanda depois de consolidar o uso

Referências