Design de servidor MCP
Esta skill decide como um servidor MCP expõe um produto a agentes: onde as ferramentas ficam no código, como uma conexão se vincula a um tenant e uma pessoa, quais chamadas precisam de confirmação humana e como resultados e erros guiam o modelo para a próxima chamada correta. A sintaxe do protocolo segue a especificação MCP (revisão 2026-07-28 quando isto foi escrito) e a documentação do SDK. Escopo de consulta, catálogos de permissões e formatos de identificador vêm de tenant-isolation, authorization-rbac e dual-key-identifiers. A falha que isso previne: um modelo que escolhe o tenant errado, envia um e-mail para um cliente duas vezes ou age com base em texto plantado por um terceiro, porque o servidor deixou uma porta aberta para o modelo, o cliente ou a descrição de uma ferramenta.
Aplica-se quando / não se aplica
- Aplica-se: construir um servidor MCP para um produto, adicionar ou revisar uma ferramenta, escolher como credenciais MCP se vinculam a tenants e pessoas, ferramentas que notificam clientes, contactam pessoas, silenciam alertas ou excluem, gravações duplicadas após novas tentativas do cliente, formatos de erro e resultado, nomes e descrições de ferramentas, conjuntos de ferramentas para modelos pequenos, avaliações de uso de ferramentas.
- Não se aplica: configurar um cliente para usar o servidor MCP de outra pessoa, design de endpoints REST ou GraphQL, chatbots e comandos de barra que não são MCP, escrita de prompts para o assistente próprio de um produto, limites de taxa em rotas não-MCP.
Regras
Mais importante primeiro. Exemplos usam um sistema de reservas: reservas, clientes, técnicos.
- [invariante] O servidor impõe o que não pode dar errado; descrições e anotações apenas orientam. Cada portão abaixo é executado no código do servidor em cada chamada. Por que: anotações são dicas, e existem clientes que aprovam automaticamente ou nunca perguntam. Teste: chame cada ferramenta protegida de um cliente JSON-RPC puro que ignora dicas; todos os guardas ainda disparam. Fonte: MCP schema 2026-07-28,
ToolAnnotations: "Os clientes nunca devem tomar decisões de uso de ferramentas com base emToolAnnotationsrecebidas de servidores não confiáveis" (https://modelcontextprotocol.io/specification/2026-07-28/schema, verificado em 2026-10-31). - [invariante] Ferramentas são um segundo adaptador na camada de serviço: elas chamam os mesmos serviços de domínio que a interface do usuário e a API, nunca a própria API HTTP em loopback e nunca o ORM diretamente. As verificações que protegem dados (limites, listas de permissão de URL contra SSRF, propriedade de registros referenciados) estão no serviço, não apenas em um controlador ou solicitação de formulário. Um caminho de gravação por função. Por que: um adaptador fino herda todas as verificações que apenas seus adaptadores irmãos executaram. Teste: um teste de arquitetura falha em chamadas de persistência em classes de ferramentas; por ferramenta de gravação, uma entrada que a API rejeita é rejeitada pela ferramenta. Fonte: reproduzível com o teste acima.
- [invariante] Uma conexão, um tenant: o tenant é vinculado na credencial quando a conexão é criada, nenhum esquema de ferramenta tem um parâmetro de tenant, um argumento como
workspace_idé rejeitado como desconhecido, e cada identificador é resolvido dentro do tenant vinculado. Uma credencial que serve vários tenants carrega o tenant no transporte (caminho ou cabeçalho) e o verifica contra a credencial, nunca em um argumento da ferramenta. Por que: um parâmetro de tenant permite que o modelo escolha o tenant. Teste: por ferramenta com uma referência, passe o identificador válido de outro tenant; esperenot_founde uma linha inalterada. Fonte: especificação de ferramentas MCP, o conjunto de ferramentas "PODE variar de acordo com a autorização apresentada na solicitação" (https://modelcontextprotocol.io/specification/2026-07-28/server/tools); melhores práticas de segurança, estado chave por um ID de usuário "derivado do token verificado em vez de fornecido pelo cliente" (https://modelcontextprotocol.io/docs/2026-07-28/tutorials/security/security_best_practices); ambos verif. em 2026-10-31. - [invariante] Os direitos são cortados ao vivo: direitos efetivos = direitos do token ∩ direitos atuais da pessoa que criou a conexão, calculados em cada chamada
tools/listetools/call. Os direitos do token são limitados na criação, um criador removido ou desativado encerra a conexão, a revogação passa por um serviço que audita. Por que: direitos congelados em um token sobrevivem a uma mudança de função. Teste: remova a função do criador, chame uma ferramenta de gravação; recusado na próxima chamada. Fonte: especificação de ferramentas MCP, servidores DEVEM "Implementar controles de acesso adequados" (verif. em 2026-10-31). - [invariante] Nenhum usuário ambiente em solicitações MCP: o middleware vincula um contexto de conexão (tenant, criador, direitos ao vivo) e deixa o slot de usuário atual do framework vazio; as ferramentas leem o ator do contexto, os serviços recebem o ator como parâmetro. Por que: com o proprietário definido como usuário atual, uma verificação de política perdida em uma ferramenta passa porque os proprietários podem fazer tudo; sem usuário, falha fechada. Teste: um teste de arquitetura proíbe portão, política e auxiliares de usuário atual no código da ferramenta; uma verificação de política adicionada deliberadamente faz a chamada falhar. Fonte: reproduzível com o teste acima.
- [invariante] Cada ferramenta tem exatamente uma classe de efeito: leitura, gravação silenciosa, notificar (atinge clientes ou altera o que veem), despertar (contacta ou chama uma pessoa), alertar (silencia, pausa ou redireciona alertas), excluir. Derive a classe do caminho do código (eventos, listeners, observadores, trabalhos, e-mails), não do nome, e divida uma ferramenta que abrange duas. Por que: dicas, balde de taxa, dever de pré-visualização e troca de conexão seguem da classe. Teste: com fila, e-mail e falsificações de evento, ferramentas silenciosas não despacham notificação. Fonte: critérios de revisão de conectores, ferramentas de leitura/gravação genéricas são rejeitadas e as gravações são divididas por tipo de ação (https://claude.com/docs/connectors/building/review-criteria, verif. em 2026-10-31).
- [invariante] Notificar, despertar, alertar e excluir levam duas etapas. A ferramenta apenas prepara: armazena uma ação pendente (conexão, ferramenta, argumentos normalizados e digest, hash do conteúdo que será enviado, expiração) e retorna
PREVIEW, umconfirm_tokene umconfirm_summaryescrito pelo servidor. Apenasconfirm_actionexecuta, após verificar conexão, não usado, não expirado, não rejeitado, resumo exato, um bloqueio, direitos ao vivo e interruptores, e uma preparação nova cujo hash de conteúdo ainda corresponde (senão uma nova pré-visualização, token antigo morto). Por que: nem o modelo nem o cliente perguntam a uma pessoa de forma confiável, e o texto aprovado pode mudar entre as etapas. Teste: pré-visualização sozinha não envia nada; tokens reutilizados, expirados, estrangeiros e de resumo errado falham; uma edição entre as etapas produzchanged_since_preview. Fonte: especificação MCP tools, "DEVE haver sempre um humano no loop com a capacidade de negar invocações de ferramentas"; requisitos de servidor MRTR: estado ecoado por um cliente é "entrada controlada pelo atacante", vinculado ao principal, expiração curta e digest de parâmetros, uso único imposto no servidor (https://modelcontextprotocol.io/specification/2026-07-28/basic/patterns/mrtr); ambos verif. em 2026-10-31. - [invariante] Proteção contra repetição em cada gravação silenciosa: impressão digital = conexão + ferramenta + SHA-256 do JSON canônico dos argumentos normalizados (chaves ordenadas em todos os níveis); um acerto dentro da janela retorna o primeiro resultado com
replayed: truee não executa nada. Umaidempotency_keyopcional é vinculada ao digest do argumento. O cache contém saída do apresentador, nunca segredos de uso único. Por que: um stream quebrado perde a solicitação em andamento e os clientes DEVEM reenviá-la com um novo ID, então uma gravação que foi executada chega novamente. Teste: mesma chamada duas vezes com IDs JSON-RPC diferentes; uma linha, segundo resultadoreplayed: true. Fonte: MCP changelog 2026-07-28, mudança principal 9 (https://modelcontextprotocol.io/specification/2026-07-28/changelog); Claude Code repete uma chamada pendente para ferramentas marcadas comoidempotentHintoureadOnlyHintuma vez em uma nova conexão (https://code.claude.com/docs/en/mcp); ambos verif. em 2026-10-31. - [invariante] Toda ferramenta de criação tem uma ferramenta de atualização irmã e um guarda de duplicação em uma chave de negócio. Um acerto retorna um erro nomeando o identificador existente e a ferramenta de atualização; criar mesmo assim precisa de
separate: truee um motivo. Os guardas leem colunas síncronas (created_by,created_at), nunca um log de auditoria enfileirado. Ferramentas de edição não têm parâmetro de notificação. Por que: ao ser solicitado a "melhorar o texto", um modelo cria um segundo objeto a menos que seja impedido. Teste: crie duas vezes com a mesma chave; uma linha, um erro nomeando-a. Fonte: reproduzível com o teste acima. - [invariante] Atualizações carregam a versão que o chamador leu: objetos e mensagens nos resultados têm uma
version(hash de conteúdo), ferramentas de atualização e edição a exigem, uma desatualizada retornaconflictcom o estado atual. Por que: um agente trabalhando com uma leitura antiga sobrescreve a edição de um colega. Teste: leia, altere a linha em outro lugar, atualize com a versão antiga;conflict, linha inalterada. Fonte: reproduzível com o teste acima. - [invariante] Ferramentas em lote usam identificadores explícitos, ou um filtro mais um
expected_countobrigatório; quando a contagem de correspondências difere, retorna as correspondências e não executa nada. Por que: um filtro mais amplo do que o modelo esperava multiplica um erro. Teste: um filtro correspondendo a 12 reservas comexpected_count: 3; erro lista 12, nada mudou. Fonte: reproduzível com o teste acima. - [invariante] Valide cada argumento no servidor com as próprias regras da ferramenta e rejeite campos desconhecidos com a lista de permitidos. Por que: SDKs nem sempre verificam argumentos contra
inputSchema, e um campo ignorado faz o modelo acreditar que definiu algo. Teste: um campo extra retorna a lista permitida e não escreve nada. Fonte: especificação MCP tools, servidores DEVEM "Validar todas as entradas da ferramenta" (verif. em 2026-10-31). - [invariante] Texto de terceiros em resultados não é confiável: texto que um cliente, usuário final, sistema externo ou upload pode escrever (notas, corpos de e-mail, payloads de webhook, mensagens de erro remotas) é truncado, removido de caracteres de controle e retornado sob
untrusted_textcom uma nota para não seguir instruções nele; corpos brutos ficam de fora. Descrições descrevem a ferramenta e nunca dizem ao modelo como se comportar. Por que: texto injetado que direciona uma ferramenta com efeito externo é o principal risco; a etapa de pré-visualização é a segunda linha. Teste: um canário de injeção em cada campo de terceiros aparece apenas dentro do wrapper. Fonte: especificação MCP tools, servidores DEVEM "Sanitizar saídas da ferramenta"; critérios de revisão de conectores rejeitam padrões de injeção de prompt em descrições; ambos verif. em 2026-10-31. - [invariante] Resultados não carregam segredos e apenas os dados pessoais necessários para a tarefa: apresentadores com listas explícitas de campos, nunca um serializador de registro inteiro; logs contêm nome da ferramenta, resultado e IDs públicos, nunca argumentos. Por que: cada resultado vai para o provedor de modelo do usuário. Teste: preencha cada campo secreto de um fixture com um valor de isca, chame cada ferramenta de leitura, grep resultados e logs; nenhuma isca. Fonte: reproduzível com o teste acima; manutenção de registros em
gdpr-engineering. - [invariante] Falhas de negócio, validação e permissão são erros de execução da ferramenta (
isError: true) cujo texto começa comError:, nomeia causa, correção e a ferramenta correta com identificadores reais, e termina com uma linha de detalhes JSON. "Não encontrado" aponta para uma ferramentafind_, nunca para criar. Uma ferramenta oculta por direitos, conjunto de ferramentas ou plano responde com esse motivo. Por que: o modelo se autocorrige apenas em erros que vê e entende. Teste: cada caminho de erro no conjunto de contratos começa comError:e tem uma linha JSON analisável. Fonte: MCP schema,CallToolResult.isError: erros de ferramenta "DEVEM ser reportados dentro do objeto de resultado ... Caso contrário, o LLM não seria capaz de ver que um erro ocorreu e autocorrigir-se"; critérios de revisão de conectores falham em respostas genéricas "Internal Server Error"; ambos verif. em 2026-10-31. - [invariante] O bloco de texto e
structuredContentcarregam o mesmo JSON, e umoutputSchemadeclarado cobre cada resultado estruturado, incluindo erros. Por que: clientes diferem em qual canal atinge o modelo, e eles DEVEM validar contra um esquema declarado. Teste: para cada resultado no conjunto, o texto analisado é igual astructuredContent. Fonte: especificação MCP tools, uma ferramenta com conteúdo estruturado "DEVE também retornar o JSON serializado em um bloco TextContent" (verif. em 2026-10-31). - [invariante] Hora e dinheiro são trabalho do servidor: horas chegam como hora local mais fuso IANA ou ISO 8601 com deslocamento, nunca sem; horas locais inexistentes e ambíguas são rejeitadas com as opções; cada hora em um resultado mostra dia da semana, hora local com fuso, UTC e distância de agora. Totais, impostos e arredondamentos são computados no servidor a partir de strings decimais ou unidades menores inteiras. Por que: erros de um dia a mais e horário de verão são invisíveis sem o dia da semana. Teste:
2026-03-29 02:30emEurope/Berliné rejeitado;2026-10-25 02:30retorna ambos os deslocamentos. Fonte: reproduzível com o teste acima. - [invariante] Um kill switch em um lugar: um sinalizador global, definido por um comando CLI e uma alternância de administrador que ambos auditam, faz cada chamada MCP retornar 503 com
Retry-Afterenquanto as conexões permanecem; um interruptor por tenant com seu padrão definido uma vez permite que um administrador de tenant desligue o acesso do agente. Por que: um incidente precisa de um movimento que pare todas as gravações do agente. Teste: sinalizador ativado, chame uma ferramenta de gravação; 503, nenhuma linha, nenhum trabalho. Fonte: reproduzível com o teste acima. - [invariante] Cada gravação MCP é auditada com canal
mcp, nome da conexão e do cliente, definido no ponto de entrada como colunas, elist_recent_changesmostra o que esta conexão mudou. Por que: os tenants veem o que um agente fez, e um agente que perdeu o contexto encontra seu rascunho em vez de criar outro. Teste: cada ferramenta de gravação produz exatamente uma linha de auditoria com canalmcp. Fonte: reproduzível com o teste acima. - [invariante] Credenciais MCP e API não se cruzam: tokens MCP têm seu próprio tipo e prefixo, são armazenados com hash, mostrados uma vez e são rejeitados por toda rota de API; tokens de API e cookies de sessão são rejeitados pelo endpoint MCP. Com OAuth, aceite apenas tokens emitidos para este servidor e nunca passe um adiante. Valide
Origin. Por que: tokens MCP ficam em texto simples nos arquivos de configuração do cliente. Teste: cada emparelhamento retorna 401; umOriginruim retorna 403. Fonte: especificação de autorização MCP, servidores "NÃO DEVEM aceitar ou transitar nenhum outro token" (https://modelcontextprotocol.io/specification/2026-07-28/server/authorization, verif. em 2026-10-31). - [invariante] Depreciação, ferramentas de namespace e conjuntos: ferramentas não são removidas adicionando
.deprecatedou uma descrição que diz "não use"; existe um plano por escrito com datas, substitutos, logs e validação do modelo antes de remover. Os prefixos de namespace (bookings_,customers_) são um diretório, não autorização. Conjuntos de ferramentas para modelos pequenos são definidos com ferramentas menores, combinadas, com identificadores e resultados que cabem em uma única resposta de modelo. Por que: modelos são treinados em documentos de especificação e podem continuar chamando ferramentas com nomes antigos. Fonte: reproduzível com o teste acima. - [invariante] Erros de tempo limite são tratados com transações demarcadas ou usando a menor granularidade atômica; ferramentas longas implementam progresso (
progressToken,totalProgressna notificação). Resultados consistentemente lentos são sinalizados ao cliente marcando o método comoslowTool. Por que: clientes como Claude Code têm um tempo limite para cada chamada de ferramenta. Teste: atrase uma ferramenta além do tempo limite do cliente; o cliente vê o progresso ou um erro. Fonte: especificação MCP,Progress_Notification,ToolAnnotations.slowTool.