cmdshellmcp

Um servidor MCP de comandos e operações de arquivo restritos para agentes de IA. Limita a execução de comandos por meio de uma lista de permissões explícita, permite que cada ferramenta seja desativada individualmente e suporta tokens de autenticação gerados automaticamente. Fornece comandos Unix restritos, operações de arquivo, aplicação de patches e busca de URLs sem expor um shell irrestrito.

Documentação

cmdshellmcp

cmdshellmcp é um servidor MCP de shell de comandos restrito para agentes de IA. Ele expõe um pequeno conjunto de comandos Unix permitidos, operações de arquivo relativamente seguras, aplicação de patches e busca de URLs, para que um cliente MCP possa realizar tarefas locais limitadas sem acesso irrestrito ao shell.

O servidor é implementado em Python e executa como um servidor MCP usando o pacote fastmcp. Por padrão, ele escuta em 127.0.0.1:8003 usando o transporte HTTP streamable, a menos que --sse seja selecionado.

[!CAUTION]

Este servidor fornece execução remota de comandos (RCE), o que normalmente é considerado uma vulnerabilidade crítica de segurança. Permitir comandos poderosos—como bash, sh, python, perl, sudo, docker, ou comandos capazes de escrever arquivos—pode permitir que um atacante ou LLM não confiável contorne as restrições pretendidas e assuma o controle do sistema. Por exemplo, permitir python ou bash pode efetivamente permitir execução arbitrária de código e acesso a arquivos. Se o servidor não tiver autenticação forte, for acessível por clientes não confiáveis, ou for controlado por um LLM não confiável ou com injeção de prompt, pode causar danos graves, incluindo perda de dados, roubo de credenciais, instalação de malware ou comprometimento de outros sistemas. Execute cmdshellmcp (cmdshellmcp2.py) apenas em um sandbox protegido e descartável, com privilégios cuidadosamente limitados aos necessários para a tarefa pretendida e com acesso limitado a arquivos, credenciais, dispositivos e redes—por exemplo, um contêiner Docker efêmero ou máquina virtual que possa ser destruído com segurança após o uso.

Aviso

Este é um aplicativo experimental / de rascunho que evoluiu do uso de um servidor MCP (ferramentas) que permitem executar comandos de shell para fins de codificação. Infelizmente, para esse propósito, muitas vezes é necessário fornecer ao cliente LLM (modelo) comandos de shell bastante poderosos para "fazer seu trabalho" para um escopo / contexto / intenção específico.

Os comandos permitidos por padrão não são necessariamente seguros, ou seja, agentes LLM ou praticamente clientes que chamam a API MCP podem "escapar" e fazer coisas fora de um contexto, por exemplo, o diretório de trabalho definido com a opção --cwd. Também não valida se os argumentos são, afinal, seguros.

Também existem ferramentas (funções MCP expostas) que expõem operações de escrita e modificação de arquivos, incluindo execução de comandos de shell.

  • A autenticação está habilitada por padrão. Mantenha o token gerado aleatoriamente, ou defina um auth_token fixo usando o campo auth no arquivo de configuração ou --auth. Usar --noauth praticamente significa que você está dando execução remota de comandos (RCE) a qualquer cliente (incluindo possivelmente maliciosos ou fraudulentos) que possa alcançar o servidor.
  • Execute isso como um usuário sem privilégios. Executar como root é, na melhor das hipóteses, tolice.
  • Não use isso com clientes não confiáveis ou LLMs não confiáveis.
  • Use em um sandbox descartável, por exemplo, um contêiner Docker autônomo ou máquina virtual que você possa descartar, incluindo o conteúdo.
  • Revise a lista de permissões em cmdshellmcp.json e os padrões codificados, revise-os antes de usar.

Recursos

  • Execução de shell com lista de permissões para um conjunto selecionado de comandos
  • Operações de leitura/escrita/lista de arquivos sob um diretório de trabalho configurado
  • Edição transacional de arquivos de texto com sed, backups numerados e diffs unificados
  • Aplicação de patches de diff unificado via patch
  • Suporte a busca HTTP com embelezamento HTML opcional
  • Autenticação por token Bearer com um token seguro gerado aleatoriamente por padrão
  • Registro de auditoria para stdout e/ou um arquivo
  • Restrições de caminho para evitar escapar do diretório de trabalho atual

Uso de IA neste repositório

Este aplicativo e seu conteúdo, por exemplo, esta página, foi criado com auxílio de LLMs (modelos de linguagem grandes), como

  • ChatGPT 5.6 sol (light), Codex
  • Github Co-pilot MAI-Code-1.1-Flash

O código inicial foi escrito pelo autor e refatorado com auxílio dos LLMs, e as atualizações também são feitas parcialmente manualmente. Após a adição ou alteração de recursos, testes adicionais são frequentemente feitos executando-os manualmente, por exemplo, na Interface Web do llama-server do llama.cpp

Instalação

  1. Clone o repositório.
  2. Crie e ative um ambiente virtual, se desejado.
  3. Instale as dependências:
python -m venv .venv
source .venv/bin/activate
pip install fastmcp requests beautifulsoup4

Se o seu ambiente usar um requirements.txt, você também pode instalar a partir dele:

pip install -r requirements.txt

A ferramenta editFile também requer GNU sed (incluindo sua opção --sandbox) e diff instalados no servidor. Esses programas são invocados diretamente pela ferramenta dedicada e não precisam aparecer em allowed_commands.

Configuração

Quando --conf não é fornecido, o servidor lê o arquivo de configuração opcional cmdshellmcp.json do diretório atual. Se o arquivo não existir, o servidor usa valores padrão de linha de comando e embutidos.

Exemplo:

{
  "host": "127.0.0.1",
  "port": 8003,
  "quiet": false,
  "auditlog": null,
  "disableTools": ["writeFile", "editFile", "applyPatch"],
  "allowed_commands": [
    "ls", "pwd", "date", "cat", "grep", "egrep",
    "whoami", "head", "tail", "sed", "wc", "file", "du", "df",
    "free", "ps", "uname", "hostname", "uptime", "w", "last",
    "mkdir", "cp", "mv", "awk"
  ],
  "auth": "_my_secret_auth_token_"
}

Chaves de configuração suportadas:

  • host: host de vinculação; padrão 127.0.0.1
  • port: porta de vinculação; padrão 8003
  • quiet: suprime a saída de auditoria para stdout quando true
  • auditlog: caminho opcional para um arquivo de log de auditoria
  • allowed_commands: lista de comandos permitidos para execução
  • disableTools: lista de nomes de ferramentas MCP a serem omitidos do servidor, sensível a maiúsculas/minúsculas e é necessária correspondência exata do nome
  • auth: string opcional de token bearer fixo; quando omitido, um token aleatório é gerado na inicialização

Executando o servidor

Inicie o servidor com as configurações padrão:

python cmdshellmcp2.py

Quando --cwd é omitido, o servidor solicita um diretório de trabalho e mostra o diretório atual do processo como padrão. Pressione Enter para aceitá-lo. Se a entrada padrão não estiver disponível (por exemplo, ao executar como um serviço), o diretório atual é selecionado automaticamente. O servidor então inicia em 127.0.0.1:8003 usando o transporte HTTP streamable.

Modo SSE

python cmdshellmcp2.py --sse

Isso executa o servidor no transporte SSE em vez de HTTP streamable.

Clientes de navegador e CORS

Ambos os transportes HTTP incluem middleware CORS para clientes MCP baseados em navegador. O servidor aceita solicitações de qualquer origem, suporta os métodos MCP GET, POST e DELETE e solicitações de preflight OPTIONS do navegador, e expõe o cabeçalho de resposta mcp-session-id ao JavaScript do navegador.

Como todas as origens são permitidas, não exponha o servidor a uma rede não confiável sem autenticação e controles de rede apropriados. Para restringir o acesso do navegador, substitua allow_origins=["*"] em CORS_MIDDLEWARE pelas origens confiáveis específicas.

Diretório de trabalho personalizado

python cmdshellmcp2.py --cwd /path/to/project

Isso define o diretório de trabalho usado pelas ferramentas de arquivo e shell sem solicitação. O caminho deve existir e deve ser um diretório; ~ é expandido e o caminho selecionado é normalizado para um caminho absoluto.

Host e porta personalizados

python cmdshellmcp2.py --host 0.0.0.0 --port 9000

Autenticação

A autenticação está habilitada por padrão. Se nem --auth nem um valor auth no arquivo de configuração fornecerem um token fixo, o servidor gera um token criptograficamente seguro, codificado em base64, e o exibe no log de inicialização. O token gerado é mostrado em negrito quando o stdout suporta estilo ANSI. Configure o cliente MCP para enviar esse valor como seu token bearer.

Para usar um token fixo:

python cmdshellmcp2.py --auth my-secret-token

Para executar explicitamente sem autenticação:

python cmdshellmcp2.py --noauth

--auth e --noauth são mutuamente exclusivos. --noauth também substitui um valor auth no arquivo de configuração. Desabilitar a autenticação é inseguro em qualquer rede que não seja completamente confiável.

Registro de auditoria

python cmdshellmcp2.py --quiet --auditlog /tmp/cmdshellmcp.log
  • --quiet desabilita o registro de auditoria para stdout.
  • --auditlog anexa eventos de auditoria ao arquivo especificado.

Opções de linha de comando

python cmdshellmcp2.py [--cwd PATH] [--host HOST] [--port PORT] \
  [--allow COMMAND [COMMAND ...]] [--conf FILE] [--auth TOKEN | --noauth] \
  [--disableTools TOOL[,TOOL...]] [--editdelbk] [--sse] [--quiet] \
  [--auditlog FILE]

Opções:

  • --cwd: diretório de trabalho usado para operações de arquivo e shell; quando omitido, solicita com o diretório atual do processo como padrão
  • --host: host de vinculação do servidor
  • --port: porta de vinculação do servidor
  • --allow: substitui a lista de permissões para o processo atual; pode ser repetido
  • --disableTools: nomes de ferramentas MCP separados por vírgula a serem omitidos; substitui a lista disableTools do arquivo de configuração; sensível a maiúsculas/minúsculas e é necessária correspondência exata do nome
  • --editdelbk: exclui o backup numerado após editFile ter concluído com sucesso e composto sua resposta de diff unificado
  • --conf: caminho do arquivo de configuração JSON; quando omitido, o padrão é cmdshellmcp.json no diretório atual
  • --auth: usa um token bearer fixo em vez de gerar um
  • --noauth: desabilita explicitamente a autenticação bearer; mutuamente exclusivo com --auth e inseguro em redes não confiáveis
  • --sse: usa transporte SSE em vez de HTTP streamable
  • --quiet: suprime a saída de auditoria no stdout
  • --auditlog: escreve logs de auditoria em um arquivo fornecido

Substituição da lista de permissões

python cmdshellmcp2.py --allow ls pwd date whoami cat grep

Isso substitui allowed_commands do arquivo de configuração para esse processo.

Desabilitando ferramentas

python cmdshellmcp2.py --disableTools writeFile,editFile,applyPatch,fetch

Isso impede que ferramentas correspondentes sejam registradas pelo servidor. Os nomes das ferramentas são sensíveis a maiúsculas/minúsculas e devem corresponder aos nomes das funções na seção Ferramentas MCP expostas a agentes de IA abaixo. A opção de linha de comando substitui a lista disableTools do arquivo de configuração.

Modelo de segurança

Este servidor é intencionalmente restrito. Ele é projetado para ser relativamente seguro em um ambiente controlado, em vez de um shell geral sem restrições.

Os recursos de segurança incluem:

  • Os comandos de shell devem estar explicitamente presentes na lista de permissões
  • Os nomes dos comandos são verificados antes da execução
  • A ferramenta cmdshell espera um nome de comando e uma matriz de argumentos, não uma string de shell bruta
  • Piping não é suportado por design
  • Para várias ferramentas, o servidor MCP valida caminhos para evitar acesso ou escrita fora do diretório de trabalho fornecido https://github.com/ag88/cmdshellmcp/blob/main/cmdshellmcp2.py#L153. No entanto, para executar comandos Unix/Linux allow_listed reais, essa verificação não é realizada para os argumentos. Isso ocorre porque há situações em que é necessário acessar recursos compartilhados, por exemplo, um arquivo/recurso em, digamos, /usr/share, /usr/include, etc. Restrições estreitas significariam configurações de lista de permissões específicas por comando + argumentos, o que seria uma lista muito grande e detalhada, difícil de manter (manualmente) e possivelmente lenta, pois precisaria realizar a verificação a cada vez. Portanto, deve-se considerar cuidadosamente os comandos Unix/Linux allow_list específicos para seu contexto/uso/intenção, ao configurá-los, por exemplo, em cmdshellmcp.json
  • As ferramentas de arquivo rejeitam caminhos absolutos e caminhos contendo ..
  • As escritas são limitadas a locais abaixo do cwd configurado
  • editFile aceita apenas uma pequena lista de permissões de opções sed que não selecionam arquivos, executa GNU sed em modo sandbox e não invoca um shell
  • editFile escreve a saída bem-sucedida em um arquivo temporário antes de substituir atomicamente o arquivo de origem; execuções de sed com falha deixam a origem inalterada
  • A aplicação de patches bloqueia opções perigosas de alteração de caminho

Em resumo: o shell é um sandbox estreito para operações controladas de leitura/escrita, não um terminal de host completo.

Ferramentas MCP expostas a agentes de IA

O servidor registra as seguintes ferramentas:

1. cmdshell(command, args)

Executa um comando Unix configurado com argumentos.

Parâmetros:

  • command: o nome do comando, que deve aparecer na lista de permissões
  • args: lista de argumentos/flags a serem passados ao comando

Exemplo:

cmdshell("ls", ["-la"])
cmdshell("grep", ["-R", "needle", "."])

Observações:

  • O nome do comando deve estar na lista de permissões.
  • Os argumentos são passados como uma lista, reduzindo o risco de injeção de shell.
  • Padrões glob podem ser expandidos automaticamente.
  • Globs entre aspas podem ser passados literalmente para evitar expansão.

2. writeFile(file, text, append=False, newline=True)

Escreve texto em um arquivo abaixo do diretório de trabalho atual.

Parâmetros:

  • file: um caminho relativo sob cwd
  • text: conteúdo a ser escrito
  • append: se true, anexa em vez de sobrescrever
  • newline: adiciona uma nova linha final quando true

Exemplo:

writeFile("notes.txt", "hello from the agent")

Isso é restrito a caminhos relativos abaixo do diretório de trabalho configurado.

3. readFile(file)

Lê um arquivo de texto UTF-8 abaixo do diretório de trabalho atual.

Exemplo:

readFile("README.md")

4. listFiles(path=".")

Lista as entradas em um diretório abaixo do diretório de trabalho configurado.

Exemplo:

listFiles(".")
listFiles("src")

Retorna uma lista de entradas separadas por novas linhas, com / anexado para diretórios.

5. editFile(file, script, args=None)

Edita um arquivo de texto existente abaixo do diretório de trabalho configurado usando GNU sed. O parâmetro dedicado script é a única fonte do programa de edição; o comando é executado com uma lista de argumentos em vez de através de um shell.

Parâmetros:

  • file: caminho relativo de um arquivo regular existente abaixo de cwd
  • script: uma expressão ou programa de edição sed, como s/old/new/g, /pattern/d ou 10,20s/old/new/g
  • args: opções seguras opcionais de sed, como -n ou -E; opções que ativam edição in-place, fornecem outra expressão ou arquivo de programa, ou selecionam arquivos adicionais de entrada/saída são rejeitadas

Exemplo:

editFile("src/example.py", "s/old_name/new_name/g")

Antes de executar sed, a ferramenta copia a origem para o próximo backup numerado não utilizado. Por exemplo, a primeira edição acima cria src/example.py.bk1; se esse nome existir, usa .bk2, depois .bk3, e assim por diante. Backups existentes nunca são sobrescritos.

sed escreve seu resultado proposto na memória enquanto executa em modo sandbox, o que bloqueia comandos GNU sed que leem arquivos, escrevem arquivos ou executam programas. A origem é substituída a partir de um arquivo temporário no mesmo diretório somente após sed sair com sucesso. Uma execução falha de sed deixa a origem inalterada e remove o novo backup desnecessário. Se a substituição começou e uma etapa posterior falhar, o backup é mantido para recuperação.

Em caso de sucesso, a resposta identifica o backup e inclui a saída de:

diff -u src/example.py.bk1 src/example.py

O backup é a versão antiga e o arquivo atual é a nova versão. Uma edição sem alteração é relatada explicitamente e ainda mantém seu backup numerado.

Inicie o servidor com --editdelbk para remover cada backup numerado após uma edição bem-sucedida. A ferramenta primeiro executa diff e compõe a resposta completa, para que o diff unificado retornado permaneça disponível mesmo que o backup tenha sido excluído. A mensagem de sucesso identifica o backup excluído. Backups ainda são mantidos quando a substituição ou geração de diff falha, então permanecem disponíveis para recuperação. Se a exclusão do backup em si falhar, a resposta começa com Error: e relata que a edição foi concluída, mas o backup permanece.

Nota de segurança: backups contêm o arquivo completo pré-edição, incluindo quaisquer segredos que ele continha. A menos que --editdelbk esteja habilitado, eles permanecem no disco após edições bem-sucedidas. Proteja e remova-os de acordo com a mesma política de retenção do arquivo de origem.

6. applyPatch(file, diff, pnum=2, args=None)

Aplica um diff a um arquivo explicitamente nomeado usando GNU patch. O diff é normalmente um diff unificado e é passado diretamente para patch via entrada padrão.

Parâmetros:

  • file: arquivo regular existente abaixo do diretório atual configurado
  • diff: texto do patch, normalmente um diff unificado
  • pnum: número de componentes de caminho iniciais a remover, correspondendo ao patch GNU -pNUM; padrão 2
  • args: flags opcionais permitidas do GNU patch; flags que podem selecionar outro alvo, entrada, saída, diretório, destino de backup/rejeição ou valor de strip são rejeitadas

Exemplo:

applyPatch(
    file="src/example.py",
    pnum=2,
    diff="--- a/src/example.py\n+++ b/src/example.py\n@@ -1 +1 @@\n-old\n+new\n",
)

O file explícito é autoritativo; nomes de arquivo embutidos no diff não são usados para escolher um alvo. Caminhos absolutos e travessia de diretório pai são rejeitados, e o alvo resolvido deve permanecer dentro do diretório atual configurado.

7. fetch(url, prettify=False)

Busca uma URL usando requests. Se prettify for true, analisa o HTML com BeautifulSoup e o imprime de forma organizada.

Exemplo:

fetch("https://example.com")
fetch("https://example.com", prettify=True)

Exemplo de inicialização

python cmdshellmcp2.py \
  --cwd /workspace/project \
  --host 0.0.0.0 \
  --port 8003 \
  --auth mytoken \
  --allow ls pwd date cat grep head tail wc

Isso inicia um servidor com um diretório de trabalho fixo, host de bind, porta, autenticação e uma lista de permissões de comandos mínima.

Notas

  • Transporte padrão: streamable-http
  • Host padrão: 127.0.0.1
  • Porta padrão: 8003
  • A lista de permissões padrão é construída a partir de um pequeno conjunto de comandos relativamente seguros

Casos de uso típicos

  • Inspecionar o estado do repositório e do sistema de arquivos
  • Ler arquivos de origem e logs
  • Escrever pequenos arquivos gerados ou alterações de configuração
  • Fazer substituições de texto revisáveis com backups automáticos
  • Aplicar pequenos patches
  • Buscar documentação ou dados da web
  • Executar um conjunto limitado de diagnósticos relativamente seguros

Este servidor é melhor usado quando um agente de IA precisa de acesso local controlado sem receber comandos de sistema irrestritos.