cmdshellmcp

Un servidor MCP de comandos y operaciones de archivos restringidos para agentes de IA. Limita la ejecución de comandos mediante una lista de permitidos explícita, permite deshabilitar cada herramienta individualmente y admite tokens de autenticación generados automáticamente. Proporciona comandos Unix restringidos, operaciones de archivos, parches y recuperación de URL sin exponer un shell sin restricciones.

Documentación

cmdshellmcp

cmdshellmcp es un servidor MCP de shell de comandos restringido para agentes de IA. Expone un pequeño conjunto de comandos Unix permitidos, operaciones de archivos relativamente seguras, aplicación de parches y obtención de URL para que un cliente MCP pueda realizar tareas locales limitadas sin acceso shell sin restricciones.

El servidor está implementado en Python y se ejecuta como servidor MCP utilizando el paquete fastmcp. Por defecto escucha en 127.0.0.1:8003 utilizando el transporte HTTP transmisible a menos que se seleccione --sse.

[!CAUTION]

Este servidor proporciona ejecución remota de comandos (RCE), que normalmente se considera una vulnerabilidad de seguridad crítica. Permitir comandos potentes—como bash, sh, python, perl, sudo, docker, o comandos capaces de escribir archivos—puede permitir que un atacante o un LLM no confiable eluda las restricciones previstas y tome control del sistema. Por ejemplo, permitir python o bash puede efectivamente permitir ejecución arbitraria de código y acceso a archivos. Si el servidor carece de autenticación fuerte, es accesible por clientes no confiables, o está controlado por un LLM no confiable o con inyección de prompts, puede causar daños graves, incluyendo pérdida de datos, robo de credenciales, instalación de malware o compromiso de otros sistemas. Ejecute cmdshellmcp (cmdshellmcp2.py) solo en un entorno aislado protegido y desechable con privilegios cuidadosamente limitados a los requeridos para su tarea prevista y con acceso limitado a archivos, credenciales, dispositivos y redes—por ejemplo, un contenedor Docker efímero o una máquina virtual que pueda destruirse de forma segura después de su uso.

Advertencia

Esta es una aplicación experimental / de prueba que evolucionó a partir del uso de un servidor MCP (herramientas) que permiten ejecutar comandos shell con fines de programación. Desafortunadamente, para tal propósito, a menudo es necesario proporcionar al cliente LLM (modelo) comandos shell bastante potentes para "hacer su trabajo" para un alcance / contexto / intención particular.

Los comandos permitidos por defecto no son necesariamente seguros, es decir, los agentes LLM o prácticamente los clientes que llaman a la API MCP pueden 'escaparse' y hacer cosas fuera de un contexto, por ejemplo, el directorio de trabajo definido con la opción --cwd. Tampoco valida si los argumentos son seguros después de todo.

También hay herramientas (funciones MCP expuestas) que exponen operaciones de escritura y modificación de archivos, incluyendo la ejecución de comandos shell.

  • La autenticación está habilitada por defecto. Mantenga el token generado aleatoriamente, o establezca un auth_token fijo usando el campo auth en el archivo de configuración o --auth. Usar --noauth prácticamente significa que está otorgando ejecución remota de comandos (RCE) a cualquier cliente (incluyendo posiblemente maliciosos o fraudulentos) que pueda alcanzar el servidor.
  • Ejecute esto como un usuario sin privilegios. Ejecutar como root es en el mejor de los casos una tontería
  • No use esto con clientes no confiables o LLMs no confiables
  • Úselo en un entorno aislado desechable, por ejemplo, un contenedor Docker independiente o una máquina virtual que pueda permitirse desechar incluyendo su contenido
  • Revise la lista de permitidos en cmdshellmcp.json y los valores predeterminados codificados, revíselos antes de usarlos.

Características

  • Ejecución shell permitida para un conjunto seleccionado de comandos
  • Operaciones de lectura/escritura/listado de archivos bajo un directorio de trabajo configurado
  • Edición transaccional de archivos de texto con sed, copias de seguridad numeradas y diffs unificados
  • Aplicación de parches de diff unificado mediante patch
  • Soporte de obtención HTTP con embellecimiento HTML opcional
  • Autenticación con token Bearer con un token seguro generado aleatoriamente por defecto
  • Registro de auditoría en stdout y/o un archivo
  • Restricciones de ruta para evitar escapar del directorio de trabajo actual

Uso de IA en este repositorio

Esta aplicación y su contenido, por ejemplo, esta página, se creó con la ayuda de LLM (modelos de lenguaje grandes) como

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

El código inicial fue escrito por el autor y refactorizado con la ayuda de los LLMs, y las actualizaciones también se realizan parcialmente de forma manual. Después de agregar o cambiar características, a menudo se realizan pruebas adicionales ejecutándolas manualmente, por ejemplo, en la interfaz web de llama-server de llama.cpp

Instalación

  1. Clone el repositorio.
  2. Cree y active un entorno virtual si lo desea.
  3. Instale las dependencias:
python -m venv .venv
source .venv/bin/activate
pip install fastmcp requests beautifulsoup4

Si su entorno usa un requirements.txt, también puede instalar desde allí:

pip install -r requirements.txt

La herramienta editFile también requiere GNU sed (incluyendo su opción --sandbox) y diff instalados en el servidor. Estos programas se invocan directamente por la herramienta dedicada y no necesitan aparecer en allowed_commands.

Configuración

Cuando no se proporciona --conf, el servidor lee el archivo de configuración opcional cmdshellmcp.json del directorio actual. Si el archivo no existe, el servidor usa valores predeterminados de línea de comandos e integrados.

Ejemplo:

{
  "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_"
}

Claves de configuración admitidas:

  • host: host de enlace; predeterminado 127.0.0.1
  • port: puerto de enlace; predeterminado 8003
  • quiet: suprime la salida de auditoría en stdout cuando true
  • auditlog: ruta opcional a un archivo de registro de auditoría
  • allowed_commands: lista de comandos permitidos para ejecución
  • disableTools: lista de nombres de herramientas MCP a omitir del servidor, sensible a mayúsculas y se requiere coincidencia exacta del nombre
  • auth: cadena de token Bearer fija opcional; cuando se omite, se genera un token aleatorio al inicio

Ejecutar el servidor

Inicie el servidor con la configuración predeterminada:

python cmdshellmcp2.py

Cuando se omite --cwd, el servidor solicita un directorio de trabajo y muestra el directorio actual del proceso como predeterminado. Presione Enter para aceptarlo. Si la entrada estándar no está disponible (por ejemplo, cuando se ejecuta como servicio), el directorio actual se selecciona automáticamente. El servidor luego se inicia en 127.0.0.1:8003 usando el transporte HTTP transmisible.

Modo SSE

python cmdshellmcp2.py --sse

Esto ejecuta el servidor en el transporte SSE en lugar de HTTP transmisible.

Clientes de navegador y CORS

Ambos transportes HTTP incluyen middleware CORS para clientes MCP basados en navegador. El servidor acepta solicitudes de cualquier origen, admite los métodos MCP GET, POST y DELETE y solicitudes de verificación previa OPTIONS del navegador, y expone el encabezado de respuesta mcp-session-id al JavaScript del navegador.

Debido a que se permiten todos los orígenes, no exponga el servidor a una red no confiable sin autenticación y controles de red apropiados. Para restringir el acceso del navegador, reemplace allow_origins=["*"] en CORS_MIDDLEWARE con los orígenes confiables específicos.

Directorio de trabajo personalizado

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

Esto establece el directorio de trabajo utilizado por las herramientas de archivos y shell sin solicitar. La ruta debe existir y debe ser un directorio; ~ se expande y la ruta seleccionada se normaliza a una ruta absoluta.

Host y puerto personalizados

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

Autenticación

La autenticación está habilitada por defecto. Si ni --auth ni un valor auth en el archivo de configuración proporcionan un token fijo, el servidor genera un token codificado en base64 criptográficamente seguro y lo muestra en el registro de inicio. El token generado se muestra en negrita cuando stdout admite estilo ANSI. Configure el cliente MCP para enviar ese valor como su token Bearer.

Para usar un token fijo en su lugar:

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

Para ejecutar explícitamente sin autenticación:

python cmdshellmcp2.py --noauth

--auth y --noauth son mutuamente excluyentes. --noauth también anula un valor auth en el archivo de configuración. Deshabilitar la autenticación es inseguro en cualquier red que no sea completamente confiable.

Registro de auditoría

python cmdshellmcp2.py --quiet --auditlog /tmp/cmdshellmcp.log
  • --quiet deshabilita el registro de auditoría en stdout.
  • --auditlog agrega eventos de auditoría al archivo especificado.

Opciones de línea de comandos

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]

Opciones:

  • --cwd: directorio de trabajo utilizado para operaciones de archivos y shell; cuando se omite, solicita con el directorio actual del proceso como predeterminado
  • --host: host de enlace del servidor
  • --port: puerto de enlace del servidor
  • --allow: anula la lista de permitidos para el proceso actual; puede repetirse
  • --disableTools: nombres de herramientas MCP separados por comas a omitir; anula la lista disableTools del archivo de configuración; sensible a mayúsculas y se requiere coincidencia exacta del nombre
  • --editdelbk: elimina la copia de seguridad numerada después de que editFile haya completado exitosamente y compuesto su respuesta de diff unificado
  • --conf: ruta del archivo de configuración JSON; cuando se omite, predeterminado a cmdshellmcp.json en el directorio actual
  • --auth: usa un token Bearer fijo en lugar de generar uno
  • --noauth: deshabilita explícitamente la autenticación Bearer; mutuamente excluyente con --auth e inseguro en redes no confiables
  • --sse: usa transporte SSE en lugar de HTTP transmisible
  • --quiet: suprime la salida de auditoría en stdout
  • --auditlog: escribe registros de auditoría en un archivo dado

Anulación de la lista de permitidos

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

Esto anula allowed_commands del archivo de configuración para ese proceso.

Deshabilitar herramientas

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

Esto evita que las herramientas coincidentes se registren en el servidor. Los nombres de las herramientas son sensibles a mayúsculas y deben coincidir con los nombres de funciones en la sección Herramientas MCP expuestas a agentes de IA a continuación. La opción de línea de comandos anula la lista disableTools del archivo de configuración.

Modelo de seguridad

Este servidor está intencionalmente restringido. Está diseñado para ser relativamente seguro en un entorno controlado en lugar de un shell general sin restricciones.

Las características de seguridad incluyen:

  • Los comandos shell deben estar explícitamente presentes en la lista de permitidos
  • Los nombres de comandos se verifican antes de la ejecución
  • La herramienta cmdshell espera un nombre de comando y una matriz de argumentos, no una cadena shell cruda
  • La canalización (piping) no está soportada por diseño
  • Para varias herramientas, el servidor MCP valida rutas para prevenir acceso o escritura fuera de su directorio de trabajo dado https://github.com/ag88/cmdshellmcp/blob/main/cmdshellmcp2.py#L153. Sin embargo, para ejecutar comandos Unix/Linux allow_listed reales, esta verificación no se realiza para los argumentos. Esto se debe a que hay situaciones donde es necesario acceder a recursos compartidos, por ejemplo, un archivo/recurso en digamos /usr/share, /usr/include, etc. Restricciones estrechas significarían configuraciones de lista de permitidos verbosas por comando + argumentos específicos que serían una lista muy grande y detallada, difícil de mantener (manualmente) y posiblemente lenta ya que necesita realizar la verificación cada vez. Por lo tanto, uno debe considerar cuidadosamente los comandos Unix/Linux allow_list específicos para su contexto / uso / intención, al configurarlos por ejemplo en cmdshellmcp.json
  • Las herramientas de archivos rechazan rutas absolutas y rutas que contienen ..
  • Las escrituras se limitan a ubicaciones debajo del cwd configurado
  • editFile acepta solo una pequeña lista de permitidos de opciones sed que no seleccionan archivos, ejecuta GNU sed en modo sandbox y no invoca un shell
  • editFile escribe la salida exitosa en un archivo temporal antes de reemplazar atómicamente el origen; las ejecuciones fallidas de sed dejan el origen sin cambios
  • La aplicación de parches bloquea opciones peligrosas que cambian rutas

En resumen: el shell es un sandbox estrecho para operaciones controladas de lectura/escritura, no un terminal de host completo.

Herramientas MCP expuestas a agentes de IA

El servidor registra las siguientes herramientas:

1. cmdshell(command, args)

Ejecuta un comando Unix configurado con argumentos.

Parámetros:

  • command: el nombre del comando, que debe aparecer en la lista de permitidos
  • args: lista de argumentos/banderas para pasar al comando

Ejemplo:

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

Notas:

  • El nombre del comando debe estar en la lista de permitidos.
  • Los argumentos se pasan como una lista, reduciendo el riesgo de inyección de shell.
  • Los patrones glob pueden expandirse automáticamente.
  • Los globs entre comillas pueden pasarse literalmente para prevenir la expansión.

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

Escribe texto a un archivo dentro del directorio de trabajo actual.

Parámetros:

  • file: una ruta relativa bajo cwd
  • text: contenido a escribir
  • append: si true, añade en lugar de sobrescribir
  • newline: añade una nueva línea al final cuando true

Ejemplo:

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

Esto está restringido a rutas relativas por debajo del directorio de trabajo configurado.

3. readFile(file)

Lee un archivo de texto UTF-8 dentro del directorio de trabajo actual.

Ejemplo:

readFile("README.md")

4. listFiles(path=".")

Lista las entradas en un directorio dentro del directorio de trabajo configurado.

Ejemplo:

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

Devuelve una lista de entradas separadas por nuevas líneas, con / añadido para directorios.

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

Edita un archivo de texto existente dentro del directorio de trabajo configurado usando GNU sed. El parámetro dedicado script es la única fuente del programa de edición; el comando se ejecuta con una lista de argumentos en lugar de a través de un shell.

Parámetros:

  • file: ruta relativa de un archivo regular existente bajo cwd
  • script: una expresión o programa de edición sed, como s/old/new/g, /pattern/d, o 10,20s/old/new/g
  • args: opciones seguras opcionales de sed, como -n o -E; las opciones que habilitan la edición en el lugar, proporcionan otra expresión o archivo de programa, o seleccionan archivos de entrada/salida adicionales son rechazadas

Ejemplo:

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

Antes de ejecutar sed, la herramienta copia el origen al siguiente respaldo numerado no utilizado. Por ejemplo, la primera edición anterior crea src/example.py.bk1; si ese nombre existe, usa .bk2, luego .bk3, y así sucesivamente. Los respaldos existentes nunca se sobrescriben.

sed escribe su resultado propuesto en memoria mientras se ejecuta en modo sandbox, lo que bloquea comandos GNU sed que leen archivos, escriben archivos o ejecutan programas. El origen se reemplaza desde un archivo temporal en el mismo directorio solo después de que sed sale exitosamente. Una ejecución fallida de sed deja el origen sin cambios y elimina el nuevo respaldo innecesario. Si el reemplazo ha comenzado y un paso posterior falla, el respaldo se conserva para recuperación.

En caso de éxito, la respuesta identifica el respaldo e incluye la salida de:

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

El respaldo es la versión anterior y el archivo actual es la versión nueva. Una edición sin cambios se informa explícitamente y aún conserva su respaldo numerado.

Inicie el servidor con --editdelbk para eliminar cada respaldo numerado después de una edición exitosa. La herramienta primero ejecuta diff y compone la respuesta completa, por lo que el diff unificado devuelto permanece disponible incluso si el respaldo ha sido eliminado. El mensaje de éxito identifica el respaldo eliminado. Los respaldos aún se conservan cuando el reemplazo o la generación del diff falla, por lo que permanecen disponibles para recuperación. Si la eliminación del respaldo en sí falla, la respuesta comienza con Error: e informa que la edición se completó pero el respaldo permanece.

Nota de seguridad: los respaldos contienen el archivo completo previo a la edición, incluidos los secretos que contenía. A menos que --editdelbk esté habilitado, permanecen en el disco después de ediciones exitosas. Protéjalos y elimínelos según la misma política de retención que el archivo de origen.

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

Aplica un diff a un archivo explícitamente nombrado usando GNU patch. El diff es normalmente un diff unificado y se pasa directamente a patch a través de la entrada estándar.

Parámetros:

  • file: archivo regular existente dentro del directorio actual configurado
  • diff: texto del parche, normalmente un diff unificado
  • pnum: número de componentes de ruta iniciales a eliminar, correspondiente a GNU patch's -pNUM; predeterminado 2
  • args: banderas GNU patch permitidas opcionales; las banderas que pueden seleccionar otro objetivo, entrada, salida, directorio, destino de respaldo/rechazo o valor de eliminación son rechazadas

Ejemplo:

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",
)

El file explícito es autoritativo; los nombres de archivo incrustados en el diff no se usan para elegir un objetivo. Las rutas absolutas y el recorrido de directorios padre son rechazados, y el objetivo resuelto debe permanecer dentro del directorio actual configurado.

7. fetch(url, prettify=False)

Obtiene una URL usando requests. Si prettify es true, analiza el HTML con BeautifulSoup y lo imprime de forma ordenada.

Ejemplo:

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

Inicio de ejemplo

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

Esto inicia un servidor con un directorio de trabajo fijo, host de enlace, puerto, autenticación y una lista de comandos permitidos mínima.

Notas

  • Transporte predeterminado: streamable-http
  • Host predeterminado: 127.0.0.1
  • Puerto predeterminado: 8003
  • La lista de comandos permitidos predeterminada se construye a partir de un pequeño conjunto de comandos relativamente seguros

Casos de uso típicos

  • Inspeccionar el estado del repositorio y del sistema de archivos
  • Leer archivos fuente y registros
  • Escribir pequeños archivos generados o cambios de configuración
  • Realizar sustituciones de texto revisables con respaldos automáticos
  • Aplicar parches pequeños
  • Obtener documentación o datos de la web
  • Ejecutar un conjunto limitado de diagnósticos relativamente seguros

Este servidor se usa mejor cuando un agente de IA necesita acceso local controlado sin que se le den comandos de sistema sin restricciones.