MCP stdio ou sockets Unix depende do limite de confiança
Comparação entre MCP stdio e sockets Unix para um broker de segredos local: duração, permissões, identidade, limpeza e implantação.

Um broker de segredos para desktop deve usar stdio quando a autoridade pertence a um único processo agente e um socket de domínio Unix quando ela pertence a um serviço local compartilhado. Isso parece uma escolha de transporte, mas mover bytes é a parte fácil. O difícil é decidir quem pode provocar uma ação, por quanto tempo a permissão dura e quais evidências restam depois que qualquer um dos lados falha.
Já vi equipes começarem com um socket porque ele parece infraestrutura e depois passarem semanas reconstruindo a atribuição do chamador e os controles de duração que a árvore de processos já oferecia. Também vi stdio ser estendido até virar um serviço residente por camadas de launchers, multiplexadores e estado oculto. Os dois transportes podem ser seguros. Qualquer um pode quebrar o modelo de ameaças em silêncio quando suas premissas de duração e identidade não combinam com a autoridade concedida.
Esta comparação considera um broker de desktop para macOS que guarda credenciais de API ou SSH e executa ações para agentes de IA locais. O agente nunca deve receber o segredo. Portanto, o broker protege algo mais importante que um cache local: ele decide qual processo pode transformar autoridade armazenada em efeito externo.
Escolha o limite de confiança antes do transporte
O transporte correto nasce da unidade de autorização. Se uma pessoa aprova uma execução do agente, um servidor stdio filho dá a essa aprovação um limite natural: o pipe existe para aquela relação entre processos e fecha quando uma ponta termina. Se várias ferramentas devem compartilhar um broker desbloqueado, um socket Unix oferece um ponto de encontro estável, mas o broker precisa criar seu próprio limite de sessão acima dele.
Escreva o modelo de ameaças como atores e ações, não como um desejo genérico de IPC seguro. Nomeie os processos locais que podem se conectar, as credenciais que o broker mantém, as operações que elas permitem e os eventos que devem encerrar o acesso. Inclua malware executado pelo mesmo usuário. Permissões de arquivo costumam barrar outros usuários, mas fazem pouco contra outro processo da mesma conta. Inclua uma cópia do binário do agente, um plugin alterado, um shell iniciado pelo agente e um processo antigo que sobrevive ao fim do trabalho visível.
Depois defina o que uma aprovação significa. Ela pode autorizar um executável assinado, um processo do sistema operacional, uma árvore de processos, uma tarefa do terminal ou todos os clientes do usuário conectado. São promessas diferentes. Um transporte não escolhe entre elas. Ele apenas facilita a aplicação de algumas.
A revogação é um bom teste de projeto. Pergunte o que o broker consegue revogar imediatamente sem bloquear todo o cofre. Com stdio, fechar um pipe ou matar o processo filho encerra o canal, embora descendentes possam ter herdado descritores se o launcher foi descuidado. Com socket, fechar uma conexão aceita encerra aquele cliente e deixa o listener disponível. Se o registro de autorização vive mais que a conexão, nenhum fechamento resolve sozinho.
Não coloque atacantes de rede no centro desta decisão local, a menos que o broker também exponha um listener de rede. Os riscos mais precisos são identidade confusa, autoridade ambiente da sessão, herança de descritores, substituição do socket no sistema de arquivos e autorização que dura mais que o trabalho aprovado.
Stdio vincula o canal à duração do processo
MCP sobre stdio funciona melhor quando o shim do broker deve nascer e morrer com o processo agente. O cliente inicia um comando servidor, escreve mensagens JSON-RPC na entrada padrão e lê respostas na saída padrão. EOF é um sinal de duração fornecido pelo sistema operacional, não uma convenção escondida em um heartbeat da aplicação.
A especificação de transporte do Model Context Protocol também traz uma exigência operacional útil: um servidor stdio não deve escrever dados fora do protocolo em stdout. Logs vão para stderr. É fácil tratar isso como mera higiene de framing, mas a regra impede que uma linha de diagnóstico vire uma mensagem inválida dentro de uma fronteira de segurança. Trate stdout como memória do protocolo. Configure bibliotecas e relatórios de falha antes da primeira resposta para que não a contaminem.
Stdio não exige nome no sistema de arquivos, diretório de sockets, modo de permissões, arquivo de descoberta nem listener residente. A configuração do agente aponta para um executável e seus argumentos. É uma vantagem real de implantação para um produto de desktop: não há endpoint para localizar nem caminho antigo para reparar. A atualização tem um ponto claro de ativação: o próximo shim usa o novo executável. Uma sessão ativa pode continuar na versão anterior, então registre a identidade e a versão do executável junto da sessão em vez de supor que todo cliente mudou na instalação.
A relação entre processos é uma evidência útil, mas não uma identidade completa. O broker pode examinar o shim ligado ao backend privado, e o shim pode examinar seu processo pai. Identificadores de processo podem ser reutilizados depois da saída, e pode haver um launcher entre o agente visível e o shim. Capture o token de auditoria ou a assinatura enquanto o processo está vivo. Não guarde apenas um PID para resolver depois.
Pipes também têm armadilhas de herança. Se um launcher marca descritores como herdáveis, um neto pode manter a ponta de escrita aberta depois que o agente termina. O broker então espera por um EOF que nunca chega. Ative close on exec onde a plataforma não fizer isso, feche as pontas sem uso logo após criar o processo e faça a sessão observar o pipe e o processo cliente esperado. EOF deve revogar o acesso, e a saída do processo deve revogá-lo de modo independente.
A concorrência é estreita de propósito. Uma instância de servidor stdio normalmente atende a um processo cliente. Isso dá isolamento claro ao custo de um processo extra por agente. Se o cofre real vive em uma aplicação de desktop, o executável stdio costuma ser um shim pequeno que encaminha pedidos tipados à aplicação. Esse salto interno ainda precisa de autenticação e vínculo de sessão. Stdio na borda MCP não torna seguro um backend compartilhado sem autenticação.
Um socket Unix cria a duração de um serviço
Um socket de domínio Unix combina com um broker que já é um serviço residente e recebe clientes independentes ao longo do tempo. O endpoint de escuta sobrevive à saída deles, então um aplicativo de barra de menus aceita chamadas sem que cada agente seja dono do processo broker. Vários clientes, contrapressão e atualizações centrais são diretos. O custo é que o serviço deve definir cada limite que um único processo cliente fornecia de forma implícita.
O pathname do socket serve para descoberta, não autenticação. Um cliente que o encontra ainda precisa de permissão para conectar, e um cliente conectado ainda precisa de uma decisão de autorização. Coloque o socket em diretório controlado pelo usuário ou aplicação, crie o diretório com modo 0700 e o socket com 0600. Evite caminho previsível em diretório compartilhado com escrita. O sticky bit de um diretório temporário evita alguns ataques de remoção, mas não o transforma em namespace confiável.
As permissões dependem de mais que o modo final. O umask do processo afeta a criação. Os diretórios pais determinam se outra conta pode percorrer ou substituir nomes. Um link simbólico ou nó antigo pode já ocupar o caminho. O serviço deve abrir um diretório pai confiável, examinar a entrada sem seguir links e removê-la apenas após provar que é o socket de uma instância morta ou um caminho pertencente à instalação atual. Fazer unlink sem verificar um nome conhecido cria uma corrida de substituição.
No macOS, um socket local aceito pode fornecer credenciais do peer por chamadas como getpeereid, e APIs de nível inferior expõem um token de auditoria. IDs de usuário e grupo dizem qual conta possui o processo peer. Não dizem qual aplicação a pessoa pretendia autorizar. Para isso, resolva o token para o processo vivo e avalie a identidade de assinatura, o caminho do executável e o contexto de inicialização conforme a política declarada do produto.
Um socket facilita multiplexação, mas isso pode borrar sessões. Nunca trate uma conexão bem sucedida como aprovação para toda requisição com identificador de sessão arbitrário. O servidor atribui a identidade da conexão, vincula a autorização a ela e rejeita tentativas do cliente de trocar de identidade. Se um helper reconectar depois de reinício do serviço, exija nova decisão, a menos que o modelo permita claramente que a aprovação sobreviva.
Desempenho raramente decide a escolha. Pipes locais e sockets Unix carregam pedidos JSON-RPC pequenos muito mais rápido que uma operação HTTP ou SSH termina. Meça se o broker transfere respostas grandes, mas não troque um modelo de autoridade compreensível por uma redução especulativa do custo de IPC local.
Permissões do socket não provam intenção
O modo 0600 significa que processos sob outros IDs de usuário não abrem o socket pelos controles discricionários normais. Não significa que todo processo do mesmo usuário mereça as credenciais guardadas. Malware de desktop, script de pacote não confiável, extensão de editor e agente aprovado muitas vezes compartilham o mesmo ID.
A diferença se perde porque permissões Unix são concretas e fáceis de examinar. Uma equipe vê um socket privado e chama o endpoint de autenticado. Ele só está autenticado até o limite da conta. Isso pode bastar se o modelo parar em outros usuários. Um broker para ferramentas autônomas geralmente promete controle dentro de uma sessão de login, então precisa de identidade mais estreita.
No macOS, informações de assinatura de código estreitam essa identidade. Valide o peer vivo, não uma string de caminho enviada no pedido. O caminho pode apontar para arquivo substituído, e um processo pode continuar executando uma imagem antiga já mapeada após atualização. Registre a autoridade de assinatura e o requisito designado que o sistema relata. Decida como builds de desenvolvimento com assinatura ad hoc funcionam; tratá-las em silêncio como a aplicação de produção transforma conveniência em bypass.
Identidade e autoridade do chamador também são diferentes. Identidade responde qual processo abriu a conexão. Autoridade responde se esse processo pode usar uma credencial específica para uma ação específica agora. Um agente com assinatura válida pode trabalhar em repositório não revisado ou ter seu prompt influenciado por conteúdo hostil. Aprovar toda ação porque o executável é familiar confunde procedência com consentimento.
Com stdio, o launcher pode apresentar a identidade do pai imediato ao iniciar o shim, e o broker pode vinculá-la a um nonce novo do canal. Com socket, o broker deriva a identidade do peer no accept e a vincula ao descritor aceito. Nos dois projetos, o broker deve ser a fonte da identidade. Um campo como client_name é informação de exibição, nunca evidência.
Um cartão de aprovação honesto mostra o que o sistema consegue estabelecer e o que a pessoa aprova. Se um wrapper impede atribuição confiável ao agente superior, informe ou negue a chamada. Subir a árvore até encontrar um nome conhecido cria um caminho de busca controlado pelo invasor.
A limpeza faz parte do modelo de segurança
A limpeza de stdio gira em torno de descritores e processos filhos. O caso normal é simples: o cliente fecha stdin, o servidor vê EOF, termina ou cancela o trabalho e sai. Os casos anormais importam mais. O cliente pode falhar enquanto um descendente segura o pipe. O servidor pode travar enquanto o cliente acha que ele morreu. Uma ação privilegiada pode terminar após a revogação se o cancelamento não chegar ao worker que a executa.
Dê a cada pedido um ID de operação atribuído pelo broker e um estado como queued, executing, completed, denied ou indeterminate. Quando o canal fechar, revogue imediatamente pedidos futuros. Para uma ação já enviada a uma API remota, não afirme cancelamento se o remoto não o aceita. Registre indeterminate se o processo local morrer antes de saber o resultado. Essa entrada impede que um loop de tentativa execute a ação duas vezes.
A limpeza de socket envolve dois objetos: conexões aceitas e pathname de escuta. Feche cada conexão após erro de protocolo, autorização falha, expiração por inatividade ou parada do serviço. Remova o pathname só se o serviço ainda possui o mesmo objeto. No reinício, EADDRINUSE é motivo para investigar, não permissão para fazer unlink do que existir.
Esta verificação shell ajuda no desenvolvimento porque mostra tipo, proprietário, modo e processo sem alterar nada:
sock="$TMPDIR/com.example.broker.sock"
stat -f 'type=%HT owner=%Su mode=%Sp inode=%i' "$sock"
lsof -n -U "$sock"
Um resultado normal do macOS tem este formato:
type=Socket owner=alice mode=srw------- inode=123456
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
Broker 48102 alice 9u unix 0x0123456789abcdef 0t0 /.../com.example.broker.sock
Não interprete os valores de exemplo. Confira que o tipo é Socket, o proprietário é o usuário esperado, o modo nega grupo e outros, e o processo é a instância instalada. Repita após crash forçado, atualização, logout e segundo lançamento. Um projeto que só limpa após saída normal não foi testado.
Se launchd inicia o serviço, mantenha essa responsabilidade de forma consistente. Misturar início gerenciado pelo aplicativo com launch agent pode criar duas instâncias disputando o caminho. Um componente deve ser dono da criação, prontidão e remoção; clientes precisam de estado unavailable explícito em vez de criar brokers repetidamente.
O trabalho de implantação muda de lugar
Stdio custa menos na instalação e mais quando muitos clientes compartilham estado residente. Cada cliente MCP precisa de uma entrada de comando. O shim deve localizar a aplicação assinada ou endpoint privado, negociar versão compatível e dar erro claro se a aplicação estiver ausente ou bloqueada. O empacotamento preserva permissões de execução e assinaturas. Não dependa de arquivos de inicialização do shell: aplicações GUI frequentemente abrem sem o ambiente esperado no terminal.
Um socket Unix exige instalação do serviço, dono da inicialização, descoberta, permissões do diretório, recuperação de falhas e compatibilidade entre atualizações independentes. Em troca, cada cliente usa um endpoint estável, e o serviço mantém estado do cofre e ordem de auditoria em um processo. Isso atrai quando a aplicação já roda continuamente.
A descoberta merece um contrato. No macOS, $TMPDIR é por usuário, mas o ambiente herdado pode variar entre terminal, editor e launcher GUI. Um caminho fixo em diretório protegido de suporte é mais fácil de analisar, embora sandbox e instalação possam limitá-lo. Se um comando de bootstrap entrega o caminho, autentique o resultado em vez de aceitar caminho arbitrário da configuração do projeto.
Diferença de versões ocorre nos dois modelos. Com stdio, o cliente escolhe qual executável shim iniciar. Com socket residente, durante atualizações graduais um cliente antigo pode alcançar serviço novo ou o contrário. Faça pequena negociação de versão na primeira troca. Rejeite combinações sem suporte antes de pedir aprovação, pois a pessoa não pode aprovar um pedido que cliente e broker interpretam de forma diferente.
Equipes de operações às vezes preferem sockets porque ferramentas conhecidas os listam. Desenvolvedores às vezes preferem stdio porque o comando roda no terminal. Nenhuma conveniência deve virar porta de depuração. Um cliente de diagnóstico capaz de enviar pedidos privilegiados segue a mesma atribuição e aprovação. Um flag que pula autorização acabará saindo da máquina de desenvolvimento.
A comparação muda se o broker não for residente. Iniciar uma aplicação gráfica completa a cada conexão stdio causa demora e avisos incômodos. Manter serviço socket só para evitar um shim pequeno adiciona atualização e limpeza. Decida primeiro a duração da aplicação e depois ajuste o transporte externo.
Relacione o projeto a ameaças explícitas
A equipe de plataforma deve registrar qual propriedade responde a cada ameaça. A tabela abaixo é registro de decisão, não placar. Um transporte vence uma linha apenas quando a implementação ao redor oferece o controle descrito.
| Ameaça ou requisito | Projeto stdio | Projeto com socket Unix |
|---|---|---|
| Outro usuário tenta acesso | Descritores privados e herança correta | Diretório pai protegido e modo 0600 |
| Outro processo do mesmo usuário conecta | Mais difícil se só o launcher possui o pipe, mas herança ainda é risco | Esperado por padrão, então credenciais do peer e identidade da aplicação são obrigatórias |
| Aprovação termina com uma execução | EOF e saída observada dão sinais naturais de revogação | Servidor cria sessão vinculada à conexão e à duração do processo |
| Vários agentes compartilham um cofre | Cada shim exige salto privado autenticado ao processo do cofre | Serviço residente aceita conexões autenticadas separadas |
| Broker falha e reinicia | Cliente vê EOF e inicia nova instância ou informa falha | Serviço trata caminho antigo e força reconexão e nova autorização |
| Executável muda durante execução | Identidade capturada continua ligada à sessão | Identidade capturada continua ligada à conexão; reconectar exige nova avaliação |
| Primeira instalação simples | Comando e executável assinado, sem endpoint | Registro de início, caminho protegido, descoberta e limpeza |
| Ordem central de auditoria | Núcleo compartilhado ordena eventos entre shims | Natural em serviço residente, embora o log durável ainda exija projeto |
Duas conclusões costumam sobreviver. Um invasor sob o mesmo usuário elimina quase todo o conforto dos bits de modo. A duração da aprovação importa tanto quanto o acesso ao endpoint. Um socket privado com aprovação para o dia inteiro pode conceder mais autoridade que um canal stdio bem delimitado.
Não atribua pesos numéricos se a equipe não consegue defendê-los. Uma credencial sensível pode fazer uma verificação de identidade ausente superar toda vantagem de implantação. Escreva um teste de aceitação por linha. Inicie, por exemplo, um processo não aprovado sob o mesmo usuário e prove a negação; aprove um agente, mate-o, mantenha um descendente vivo e prove que a autorização antiga não volta a funcionar.
As opções também podem ser combinadas. MCP usa stdio entre agente e shim, e o shim fala com o broker residente por socket privado ou outro IPC. É uma boa forma para desktop, mas cria duas fronteiras. Autentique ambas. O shim prova qual execução representa, e a aplicação prova que ele alcançou o broker esperado, não um endpoint substituído pelos arquivos do projeto.
Coloque a identidade numa sessão verificada
Um pequeno envelope de protocolo torna a decisão de segurança revisável. Ele não substitui a identidade do sistema. Ele vincula fatos já verificados pelo broker ao pedido que será executado.
Após inspecionar o chamador vivo, o broker cria identificador de sessão e nonce e os devolve pelo canal autenticado. Cada pedido seguinte inclui esse identificador, número crescente, capacidade e parâmetros. O broker rejeita sessão desconhecida, sequência repetida ou pulada quando exige ordem, capacidade fora da aprovação ou pedido recebido por outro canal.
{"session_id":"s_7M4K","sequence":12,"capability":"http:billing.read","action":{"method":"GET","path":"/v1/invoices"}}
A resposta repete identidade e estado da operação, não segredos nem headers injetados:
{"operation_id":"op_01J8","sequence":12,"state":"completed","result":{"status":200,"body_ref":"activity:8841"}}
São formatos de protocolo, não nomes universais. A propriedade útil é que o servidor atribui identidade, impõe ordem e devolve referência durável à ação. Não assine o envelope com uma chave entregue ao agente, pois isso o tornaria portador de credencial. Vincule ao canal local autenticado e mantenha segredos dentro do broker.
Em stdio, o vínculo pode incluir valor aleatório entregue só pelo pipe novo e identidade capturada do launcher. Em socket, pode incluir conexão aceita e token de auditoria do peer. Se pedidos transitam entre conexões, defina retomada deliberada com vida curta e tokens de uso único. Um identificador copiado de log não deve restaurar autoridade.
Separe dados de aprovação e de exibição. Caminho do repositório, rótulo fornecido pelo agente, nome da ferramenta e justificativa ajudam a decidir, mas o invasor pode escolhê-los. O registro distingue fatos verificados, afirmações do usuário, capacidades aprovadas e conteúdo. Numa investigação, isso evita que um rótulo bem escrito pareça evidência.
Um broker de desktop pode usar os dois sem confusão
Para uma aplicação macOS assinada e sempre ativa, o projeto mais claro costuma usar stdio na fronteira MCP e um salto local autenticado até a aplicação. O agente recebe o modelo de lançamento esperado e duração ligada ao processo. A aplicação mantém um cofre, uma interface de aprovação e histórico ordenado. Só funciona se o salto interno preservar a identidade externa em vez de reduzir todos os shims a um cliente confiável.
Sallyport usa esse formato: agentes compatíveis com MCP iniciam o shim sp mcp, enquanto a aplicação executa ações HTTP e SSH para que os segredos não cheguem ao agente. Sua aprovação de sessão começa pela autoridade de assinatura do processo, o ponto fraco que permissões de socket não resolvem.
Isso não torna sockets errados nem stdio suficiente. Uma equipe com vários clientes nativos pode expor um socket protegido como API principal. Então assume inspeção do peer, construção de sessões, recuperação de endpoint antigo e compatibilidade. Se não consegue explicar cada item, o socket não está pronto para carregar credenciais.
Escolha o projeto cujo estado de falha é mais fácil de negar. Se a atribuição falhar, negue. Se o broker não provar que um endpoint pertence à instância atual, não conecte nem faça unlink. Se um cliente reconectar após reinício, crie sessão nova. Essas regras custam alguma conveniência e removem a continuidade silenciosa que torna brokers locais perigosos.
A revisão final deve caber em uma página: unidade de autorização, evidência do chamador, início e fim da sessão, dono do endpoint, falhas, atualizações e auditoria. Se alguma resposta depende de "mesmo usuário", diga se malware dessa conta está fora do modelo. Essa frase mostra se stdio e sockets estão sendo comparados como transportes ou usados no lugar de uma decisão de segurança.
FAQ
MCP stdio é mais seguro que um socket Unix?
Não por si só. Stdio limita naturalmente o canal a um processo iniciado, enquanto o socket favorece um serviço residente; a segurança depende de essa duração combinar com a unidade de autorização.
O modo 0600 autentica a aplicação chamadora?
Não. Em geral ele restringe acesso ao usuário proprietário, mas todo processo desse usuário pode tentar conexão. Inspecione o peer vivo e tome outra decisão de autorização.
Um broker pode atender vários agentes por stdio?
Sim. Rode um shim por agente e dê a cada um um salto privado autenticado ao núcleo compartilhado. Separe identidades para um shim não usar a aprovação de outro.
O que fazer quando um cliente stdio falha?
O broker revoga chamadas futuras ao ver EOF ou saída e registra o resultado do trabalho ativo. Se uma ação remota pode ter terminado, marque indeterminate em vez de repetir às cegas.
Como remover um socket Unix antigo?
Examine o objeto sem seguir links e verifique tipo, proprietário e relação com instância morta. Nunca faça unlink só porque bind retornou EADDRINUSE.
Sockets Unix são rápidos o bastante para MCP?
Sim para pedidos comuns. Transporte local pesa pouco perto de HTTP ou SSH, então identidade e duração devem decidir antes do throughput.
A autorização deve sobreviver a reinício?
Em geral não. O reinício rompe canal e evidências do processo; exija reconexão e sessão nova, salvo promessa explícita e protegida de autorização durável.
O cliente pode enviar o próprio nome de processo?
Pode enviar rótulo para exibição, mas o broker não deve confiar nele. Derive identidade do processo vivo, token de auditoria, assinatura ou relação de lançamento.
Onde o macOS deve colocar um socket Unix?
Use diretório pai controlado pelo usuário ou aplicação e negue grupo e outros. Defina como terminal, editor e GUI descobrem o caminho sem confiar no projeto.
Quando combinar stdio e socket?
Isso serve a um cofre residente para agentes MCP iniciados separadamente. A borda stdio fornece sessões por processo; o socket interno ainda exige autenticação e contexto verificado.