# Segurança da configuração do MCP para alterações no repositório

Uma configuração de MCP do repositório é material executável de implantação disfarçado de pequeno arquivo JSON. Ela pode selecionar um binário, buscar um pacote, escolher onde esse processo será iniciado, passar dados para ele e dar acesso ao checkout. Por isso, a segurança da configuração do MCP é um problema de revisão de pull request antes de se tornar um problema de segurança do agente.

Já vi equipes passarem uma hora discutindo se um agente deveria poder chamar uma ferramenta e, depois, fazer o merge de um lançador que pode instalar qualquer coisa que um nome de pacote público resolva naquele momento. A lista de ferramentas é a parte visível. O processo que cria essa lista já atravessou um limite.

## A configuração é uma instrução para iniciar um processo

Uma alteração no `.mcp.json` merece a mesma atenção que uma alteração em um script de build, porque um cliente precisa transformar seus campos em um processo local. O esquema exato varia conforme o host, mas o formato comum é conhecido: um comando, uma matriz de argumentos, entradas de ambiente opcionais e, às vezes, um diretório de trabalho ou uma configuração de transporte.

Esse processo começa com as permissões do desenvolvedor ou do host do agente que o inicia. Ele pode ler arquivos que esse usuário consegue ler, criar arquivos onde esse usuário pode escrever, herdar valores de ambiente selecionados e fazer requisições de rede, a menos que o sistema operacional ou outro controle bloqueie essas ações. O fato de seu papel final ser «servidor MCP» não reduz essas permissões comuns de processo.

Revisores costumam deduzir segurança pelo nome do pacote ou pela suposta limitação das ferramentas anunciadas. Nenhuma das duas conclusões é confiável. Um servidor chamado `issue-reader` pode expor apenas operações de leitura depois da inicialização, enquanto seu lançador chama um wrapper de shell que lê um token no diretório pessoal. Um servidor com um nome amplo pode ser um binário incluído no repositório que faz exatamente o que o projeto documenta. Comece pelas evidências, não pelos nomes.

A linha perigosa costuma ser visualmente banal:

```json
{
  "mcpServers": {
    "docs": {
      "command": "npx",
      "args": ["-y", "@example/docs-mcp"]
    }
  }
}
```

Isso não significa que `npx` ou um pacote de registro seja automaticamente inaceitável. Significa que o pull request delegou parte da cadeia de fornecimento executável a um momento posterior, em cada máquina que iniciar o servidor. O revisor precisa saber qual versão do pacote será executada, de onde ela vem, o que instala e o que faz na primeira inicialização.

Uma configuração incluída no repositório também cria uma pressão social. Quando está no repositório, um novo colaborador vê um servidor descrito como parte da configuração do projeto e presume que ele já foi analisado. É justamente por isso que essa classe de arquivo precisa de um responsável e de um hábito de revisão.

## As mensagens do MCP e os lançadores do repositório são camadas diferentes

A especificação do Model Context Protocol descreve a conversa entre um cliente e um servidor MCP, incluindo inicialização, descoberta de ferramentas, chamadas de ferramentas e comportamento do transporte. Ela não cria um formato universal de `.mcp.json` nem torna inofensiva uma configuração do cliente. Cada cliente decide onde lê a configuração e como inicia um servidor local por stdio.

Essa distinção parece acadêmica até que uma revisão dê errado. O protocolo pode limitar um servidor a um conjunto declarado de métodos depois que a conexão é estabelecida. A configuração do host decide qual programa terá a oportunidade de anunciar esses métodos em primeiro lugar. Se você examina apenas o comportamento voltado para o protocolo, começa a revisão depois que o lançador já agiu.

Mantenha duas perguntas separadas:

- Que ações o servidor conectado pode solicitar ou executar por meio de suas ferramentas?
- O que o lançador local faz antes de o cliente ter uma conexão com o servidor?

A primeira pergunta pertence às permissões do agente, ao fluxo de aprovação e ao design das ferramentas. A segunda pertence à confiança no repositório, à execução de processos, à origem das dependências e ao escopo do sistema de arquivos. Uma boa revisão responde às duas, mas nunca permite que a resposta de uma substitua a resposta da outra.

A documentação do protocolo incentiva clientes e servidores a negociar capacidades. Isso é útil para compatibilidade, não para estabelecer confiança em um comando do repositório. A negociação de capacidades pode informar ao cliente que um servidor oferece ferramentas. Ela não prova que o caminho do comando, o conteúdo do pacote ou o hook de inicialização merecem acesso ao ambiente do desenvolvedor.

Isso também explica por que um servidor conhecido ainda pode ser arriscado em uma nova configuração. O código do servidor pode não ter mudado, mas um argumento modificado pode apontá-lo para outro endpoint, outra fonte de credenciais ou outro diretório de projeto. O contexto de inicialização faz parte do comportamento.

## Leia o comando iniciado antes das ferramentas anunciadas

Revise a resolução do comando como uma cadeia, não como a primeira string do JSON. Pergunte qual programa o host realmente executará depois de resolver o comando pelo ambiente atual. `python`, `node`, `uvx`, `npx`, `bunx`, `sh` e um caminho relativo significam que outro resolvedor decidirá qual será o executável final.

Um caminho absoluto direto é o mais fácil de inspecionar, embora ainda precise de uma explicação sobre sua origem. Um caminho relativo ao repositório pode ser aceitável quando o repositório contém o arquivo e a revisão normal de código o abrange. Um nome de comando simples exige que você examine o comportamento do PATH do desenvolvedor. Um comando de shell exige o maior nível de desconfiança, porque aspas, expansão, pipes, redirecionamentos e substituição de comandos escondem mais execução do que uma matriz simples de argumentos.

Os argumentos merecem uma análise própria. Revisores costumam ler os dois primeiros argumentos, reconhecer um nome de pacote e seguir adiante. Leia cada elemento. Um argumento pode selecionar um arquivo de configuração fora do checkout, escolher um diretório de saída, ativar um carregador de plugins, apontar para um registro alternativo ou transformar um comando inofensivo em um avaliador de scripts.

Considere a diferença entre estas duas entradas:

```json
{
  "command": "node",
  "args": ["tools/mcp-server.js", "--root", "."]
}
```

```json
{
  "command": "node",
  "args": ["tools/mcp-server.js", "--config", "../../shared/runtime.json"]
}
```

A segunda entrada pode ser legítima. Ela também cria uma dependência de um arquivo fora do limite do checkout que o pull request talvez não mostre. Se o servidor lê instruções ou credenciais desse arquivo, os revisores não conseguem avaliar a alteração apenas pelo diff. Peça ao autor que inclua a configuração referenciada na revisão, substitua-a por um caminho explícito dentro do repositório ou explique por que o arquivo externo é necessário.

Não aceite uma resposta vaga como «o pacote cuida disso». O pacote pode cuidar disso corretamente hoje, mas a alteração ainda atribui autoridade a ele. Uma boa resposta informa o programa, sua versão, suas entradas e os arquivos esperados. Se o autor não executou o comando exato a partir de um checkout limpo, está descrevendo uma intenção, não um comportamento.

Adote um padrão direto para wrappers de shell: rejeite-os, a menos que o repositório tenha uma necessidade concreta que uma matriz de argumentos não consiga expressar. Um wrapper pode parecer conveniente porque define um valor de ambiente e inicia dois auxiliares. Ele também faz a revisão depender das regras de análise do shell e de todos os comandos alcançados pelo wrapper. Se precisar existir, coloque a configuração em um script revisado com um nome de arquivo claro e revise esse script como código de aplicação.

## Executores de pacotes transformam a inicialização em um evento da cadeia de fornecimento

Um executor de pacotes pode baixar e executar código durante a primeira inicialização do servidor. Por isso, o nome do pacote sozinho não é uma revisão completa da dependência. É por isso que formas como `npx -y package`, `uvx package` e semelhantes exigem mais análise do que um comando que chama um arquivo incluído no repositório.

O principal argumento a favor de um executor sem versão fixada é a conveniência. Os colaboradores não precisam instalar nada manualmente, e o projeto parece permanecer atualizado. O custo é que o repositório deixa de informar exatamente qual código pediu a cada colaborador que executasse. Uma versão recém-publicada do pacote, uma dependência alterada ou uma mudança no roteamento do registro pode mudar o comportamento sem um novo pull request.

No mínimo, torne a versão explícita e deixe a origem clara:

```json
{
  "mcpServers": {
    "schema-checker": {
      "command": "npx",
      "args": ["-y", "@acme/schema-mcp@1.4.2"],
      "cwd": "${workspaceFolder}"
    }
  }
}
```

Fixar a versão não certifica o pacote. Isso torna estável o alvo da revisão. O revisor pode inspecionar essa versão, compará-la com a anterior e exigir um pull request intencional para uma atualização. Use o lockfile do gerenciador de pacotes, dados de integridade ou um artefato incluído no repositório quando a cadeia de ferramentas ao redor oferecer suporte. Uma string de versão sem qualquer forma de verificar o conteúdo baixado deixa uma lacuna, mas ainda é melhor que um nome sem limites.

Não confunda o cache de um gerenciador de pacotes com revisão. O cache apenas muda de onde os bytes vieram em uma máquina. Ele não informa ao revisor quais bytes os outros colaboradores receberão, se hooks de instalação serão executados ou se uma máquina limpa terá o mesmo comportamento.

Faça uma pergunta desconfortável que identifica muitas alterações frágeis: o que acontece se o pacote estiver ausente? Se a resposta for «ele se instala sozinho», a configuração tem um caminho de rede e execução de código. Se a resposta for «a inicialização falha e orienta a instalar uma dependência documentada», a equipe escolheu uma configuração mais lenta, porém mais visível. Nenhuma das respostas é necessariamente correta. Fingir que a diferença não existe é errado.

## Diretórios de trabalho e entradas de ambiente definem o escopo real

O campo `cwd` informa ao processo onde começar a procurar arquivos relativos, e essa escolha costuma controlar mais coisas do que as pessoas esperam. Muitas ferramentas descobrem configurações do projeto, runtimes de linguagem, arquivos de exclusão, credenciais e plugins subindo a partir do diretório de trabalho. Um servidor iniciado na raiz do repositório pode ver um mundo diferente daquele de um servidor iniciado em um diretório separado de fixtures.

Defina o diretório de trabalho deliberadamente. Se um servidor precisa apenas das especificações de API geradas em `tools/specs`, não o inicie em um diretório pai que contenha material de implantação e anotações privadas de desenvolvimento. Se ele precisa inspecionar todo o checkout, diga isso no pull request. O objetivo não é tornar todos os caminhos minúsculos. É fazer com que o acesso aprovado pelo revisor corresponda ao acesso recebido pelo servidor.

As entradas de ambiente têm peso semelhante. Algumas são configurações operacionais inofensivas, como localidade ou porta. Outras selecionam um registro de pacotes, ampliam um caminho de busca de módulos, ativam um modo de depuração que grava corpos de requisições ou carregam credenciais. Uma configuração que usa `${TOKEN}` oculta o valor do token no arquivo, mas ainda passa esse segredo para um processo filho.

Evite credenciais na configuração de MCP do repositório sempre que o servidor puder operar por meio de uma fronteira de ação separada. Se um servidor realmente precisar de um segredo, documente a origem, a finalidade e se o processo pode passá-lo para processos filhos. Não coloque um token de produção de exemplo em um comentário ou em uma transcrição de shell copiada. Os desenvolvedores copiarão o exemplo que for mais fácil de fazer funcionar.

Preste atenção também aos valores de ambiente herdados. Um processo pode herdar muito mais do que o JSON lista. O host decide o que passa, mas o autor do repositório pode escolher um comando que leia locais padrão do runtime. Esse é outro motivo para preferir lançadores diretos e pequenos a shells genéricos e executores de pacotes.

## Um diff de pull request pode revelar o grafo de execução

Você pode revisar a maioria das alterações de configuração sem executar código não confiável, expandindo o diff para os processos e as entradas que ele implica. Comece com um diff focado, em vez da visualização do arquivo renderizado, para que argumentos removidos e caminhos alterados continuem visíveis:

```sh
git diff --check
git diff -- .mcp.json
```

O primeiro comando informa erros de espaços em branco quando os encontra. O segundo deve mostrar cada linha adicionada, removida e alterada no caminho da configuração. Se o repositório armazena o arquivo em outro lugar ou o gera, ajuste o caminho e peça também a alteração do gerador. Uma configuração gerada sem seu código-fonte é uma revisão incompleta.

Veja esta pequena alteração:

```diff
 "mcpServers": {
   "release-notes": {
-    "command": "node",
-    "args": ["tools/release-notes-server.js"],
-    "cwd": "${workspaceFolder}"
+    "command": "npx",
+    "args": ["-y", "release-notes-mcp"],
+    "cwd": ".."
   }
 }
```

A história visível diz que a equipe substituiu um auxiliar local por um servidor publicado. O grafo de execução revela mais. O cliente resolve `npx` pelo PATH do desenvolvedor. O `npx` pode buscar `release-notes-mcp` e suas dependências. O pacote é iniciado fora da raiz do repositório, porque `cwd` agora aponta para o diretório pai. O servidor pode descobrir configurações nesse local, gravar arquivos de cache ali ou ler projetos vizinhos. Cada seta precisa de uma resposta antes do merge.

Uma revisão disciplinada pede evidências nesta ordem:

1. Identifique o executável final e o pacote exato ou arquivo do repositório que o fornece.
2. Liste cada caminho de arquivo, URL, registro e valor de ambiente que possa mudar o comportamento da inicialização.
3. Informe o diretório de trabalho do processo e os diretórios que o servidor lê ou grava durante o uso normal.
4. Confirme o que acontece em uma máquina limpa antes de existir qualquer cache de pacote.
5. Compare a finalidade declarada da ferramenta com as permissões de que o lançador realmente precisa.

Essa sequência é um artefato concreto, não uma cerimônia. Ela evita a falha comum em que um revisor aprova a descrição de uma ferramenta enquanto um instalador, seletor de runtime ou diretório pai passa despercebido em uma alteração de uma linha.

Se precisar executar uma configuração para validá-la, faça isso com uma conta descartável ou em um ambiente isolado, sem credenciais de produção e com o cache de pacotes vazio. Registre o executável resolvido, a versão do pacote, os hosts acessados e os arquivos gravados. Uma captura de tela mostrando a descoberta bem-sucedida das ferramentas é uma evidência fraca, porque omite justamente a parte que causou o maior risco.

## As descrições das ferramentas não compensam um lançador amplo

Um esquema de ferramenta restrito não apaga o que aconteceu antes do handshake do MCP. As equipes costumam tratar a saída de `tools/list` de um servidor como um inventário de permissões e concluem que parece segura porque oferece `search_docs` e `read_status`. Esse inventário descreve a interface depois da inicialização. Ele diz pouco sobre instalação de pacotes, descoberta de configurações, telemetria, processos filhos ou arquivos lidos na inicialização.

Essa distinção funciona nos dois sentidos. Um servidor com uma ferramenta capaz de escrever pode ser aceitável quando o lançador é fixado, local, documentado e limitado ao projeto pretendido. Um servidor somente leitura pode ser inaceitável quando uma configuração do repositório executa silenciosamente um pacote mutável da rede com acesso ao workspace mais amplo do desenvolvedor.

Peça ao autor que descreva o ciclo de vida em linguagem simples: o que é iniciado, o que lê antes de se conectar, o que baixa, se baixar alguma coisa, e o que o servidor pode acessar depois de conectado. A explicação deve caber na descrição de um pull request. Se for necessária uma investigação longa para responder, a configuração é indireta demais para uma revisão rotineira.

Não deixe que «confiamos nesse fornecedor» encerre a discussão. Confiança é relevante, mas não fixa uma versão, não limita um diretório de trabalho e não mostra se o comando usa um registro alternativo. Esses são controles separados, com modos de falha separados.

## A propriedade do repositório precisa de uma proteção em tempo de execução

A revisão do repositório impede que instruções de inicialização inseguras se tornem parte normal da configuração do projeto. Os controles em tempo de execução continuam importantes, porque uma dependência aprovada pode ser comprometida, um prompt malicioso pode ser usado ou um usuário pode pedir que um agente aja de forma ampla demais.

Mantenha o limite claro. O repositório controla o que propõe iniciar. A pessoa que opera a máquina controla se uma determinada sessão do agente pode agir. Ações sensíveis precisam de registros que permitam reconstruir o que o agente fez e revogar a sessão quando a execução der errado.

O Sallyport mantém as credenciais de API e SSH fora do processo do agente, enquanto a autorização da sessão e o registro de atividades oferecem ao operador um local separado para aprovar e inspecionar ações. Isso não torna inofensivo um comando arriscado do repositório, por isso a revisão da configuração continua vindo primeiro.

Para equipes que usam outro limite, aplique o mesmo teste. Um desenvolvedor consegue ver qual processo do agente solicitou acesso? Consegue interromper esse processo sem encerrar trabalhos não relacionados? Consegue distinguir uma ação tentada de uma ação bem-sucedida? Se a resposta for não, uma configuração ruim terá mais tempo para causar problemas depois do merge.

## Trate esses arquivos como código de execução sob responsabilidade definida

O repositório deve atribuir responsáveis pelo código, ou uma regra de revisão equivalente, à configuração do MCP, aos scripts wrapper e a quaisquer manifestos de pacote que determinem o que o lançador instala. O responsável não precisa ser especialista em segurança. Precisa ter contexto suficiente para perguntar por que essa alteração inicia este programa com esse acesso.

Mantenha as configurações pequenas. Um servidor por finalidade é mais fácil de revisar do que um auxiliar universal que alcança todos os sistemas usados por um desenvolvedor. Coloque requisitos incomuns ao lado da configuração, em prosa comum: comando esperado, política de versão do pacote, diretório de trabalho, acesso de rede necessário e entradas previstas. Essa observação transforma conhecimento informal em algo que o próximo revisor pode questionar.

O teste prático é simples. Entregue o diff a um desenvolvedor experiente que não conheça o recurso e peça que explique o que será executado em uma máquina limpa, onde será executado e o que poderá ver. Se ele não conseguir responder a partir do pull request e do código referenciado, a alteração precisa de mais trabalho antes de entrar no repositório.
