ptc-fs-mcp
ptc-fs-mcp
Un pequeño servidor MCP de sistema de archivos: lee y escribe archivos bajo una única raíz confinada, a través de stdio.
Software de demostración. Existe para que los runtimes agénticos tengan una herramienta externa real y determinista a la que apuntar en tutoriales, ejemplos y pruebas de integración. Es deliberadamente lo bastante pequeño como para leerse de una sentada y copiarse en tu propio proyecto. No lo despliegues como servicio de archivos de producción.
Fue construido para el framework agéntico PtcRunner, donde una capacidad de sistema de archivos llega enteramente a través de la configuración del host, no del código en tiempo de ejecución. Nada en el servidor es específico de PtcRunner: habla MCP plano sobre stdio, por lo que cualquier cliente MCP puede instalarlo.
npx -y ptc-fs-mcp --root ./workspace --include '**'Herramientas
Herramienta | Efecto | Devuelve |
| leer | Entradas ordenadas y paginadas bajo un prefijo relativo |
| leer | Rutas ordenadas y paginadas que contienen una subcadena literal |
| leer | Coincidencias literales paginadas con evidencia de ruta y línea |
| leer | Fragmentos de bytes UTF-8 exactos paginados |
| escribir | Reemplaza un archivo regular, informa ruta y bytes |
Las cuatro herramientas de lectura aceptan cursor y limit opcionales y devuelven exactamente items, next_cursor y content_hash. Comienza sin cursor y sigue next_cursor hasta que sea null. Para read_text_file, concatenar el text de los elementos reconstruye el archivo exactamente.
Bytes en vivo
Las lecturas reflejan el sistema de archivos en el momento de la llamada, por lo que una escritura es visible en la siguiente lectura. Ese es el propósito del servidor, y tiene dos consecuencias que vale la pena declarar en lugar de descubrir.
Los cursores fallan en lugar de rasgarse. Un cursor lleva un resumen del estado del que depende su recorrido. Si ese estado cambió, la siguiente página se rechaza con the filesystem changed since this cursor was issued; start the traversal again. Una página silenciosamente rasgada — mitad de antes del cambio, mitad de después — es el único resultado por el que vale la pena gastar un error.
Solo se vincula el estado del que realmente depende un resultado, por lo que un cursor no se invalida por un cambio no relacionado:
Herramienta | Falla cuando | Sobrevive |
| Las entradas listadas cambian | Un archivo aparece más profundo en un subdirectorio listado |
| El conjunto de rutas coincidentes cambia | Se edita el contenido de un archivo coincidente |
| Cambia el contenido o la identidad de cualquier archivo en alcance | Un cambio fuera del prefijo buscado |
| Ese archivo cambia | Cualquier otro archivo cambia |
Los cursores están firmados con una clave por proceso, vinculados a la herramienta y sus argumentos, y deben presentarse exactamente como se emitieron. Un cursor de otro recorrido, otro proceso o una cadena editada se rechaza.
Cada resultado lleva content_hash, el resumen SHA-256 de los bytes que devolvió esa llamada. Una cita entonces nombra los bytes realmente leídos en lugar de un árbol que casualmente existía en otro momento. write_text_file informa el mismo resumen sobre los bytes que escribió, por lo que una escritura y la lectura que la sigue pueden verificarse entre sí.
No hay hash de todo el árbol ni snapshot_identity que instalar. Un resumen solo puede cubrir una captura acotada, y este servidor no toma una.
Ejecución
ptc-fs-mcp --root ./workspace --include 'lib/**' --include 'docs/**' --exclude '**/secrets/**'Opción | Significado |
| Directorio al que confinarse. Obligatorio. |
| Servir rutas coincidentes. Obligatorio, repetible. |
| Nunca servir rutas coincidentes. Repetible; solo puede estrechar. |
| No servir archivos más grandes que esto. |
| Mayor carga útil de |
--include es obligatorio y el valor predeterminado es ningún archivo, por lo que un servidor iniciado sin él no expone nada. Las rutas excluidas se omiten antes de cualquier stat o open, por lo que nunca se inventarían. Los globs coinciden con * dentro de un segmento y ** entre segmentos; lib/** selecciona tanto lib/a.ts como lib/deep/a.ts.
Las escrituras aterrizan en la raíz, por lo que las reglas de inclusión deben alcanzarla.
write_text_file nombra un solo nombre base, nunca un directorio, por lo que cada escritura va directamente a la raíz. Un conjunto de inclusión que solo alcanza subdirectorios — --include 'lib/**' — sirve esos archivos para lectura pero no puede aceptar ninguna escritura, y cada intento se rechaza con no --include pattern of this root matches a file in the root itself. Esa es una configuración legítima para una instalación de solo lectura, por lo que el servidor se inicia de todos modos y lo dice en stderr:
ptc-fs-mcp: no --include pattern matches a file in the root itself, so
write_text_file will refuse every call.Donde la herramienta de escritura esté mapeada, usa --include '**' o añade un patrón de nivel raíz como --include '*.md' junto a los de directorio.
Instálalo desde un documento host fijando una versión:
"transport": {
"type": "stdio",
"command": "npx",
"args": ["-y", "ptc-fs-mcp@0.1.0", "--root", "workspace", "--include", "**"],
"inherit_environment": true
}Iniciar sin un entorno heredado
Esa forma necesita PATH dos veces: npx se encuentra en él, y el binario instalado comienza con #!/usr/bin/env node, que también resuelve el intérprete en él. Un host que inicia con un entorno depurado — inherit_environment: false de PtcRunner, que sus propias pruebas de extremo a extremo usan — no puede iniciar el servidor en absoluto, y el fallo llega como un error de adquisición como provider_unavailable en lugar de algo que nombre PATH. Los gestores de versiones lo hacen más agudo, no más suave: un intérprete nvm vive en una ruta como ~/.nvm/versions/node/v20.19.0/bin/node y no existe en ningún otro lugar.
Las dos configuraciones son mutuamente excluyentes. Para iniciar herméticamente, instala el paquete de antemano y nombra el intérprete y el script de forma absoluta, evitando tanto npx como el shebang:
npm install ptc-fs-mcp@0.1.0
node -p process.execPath
node -p "require.resolve('ptc-fs-mcp/package.json').replace(/package\.json$/, 'dist/cli.js')""transport": {
"type": "stdio",
"command": "/absolute/path/to/bin/node",
"args": [
"/absolute/path/to/node_modules/ptc-fs-mcp/dist/cli.js",
"--root",
"/absolute/path/to/workspace",
"--include",
"**"
],
"inherit_environment": false,
"env": {}
}El servidor en sí no necesita nada del entorno: no inicia ningún proceso, no abre ninguna conexión de red y no lee ninguna variable propia. --root se resuelve contra el directorio de trabajo, así que hazlo absoluto también a menos que el host establezca un cwd que controles. hermetic_workspace en examples/ptc-host.json es esta forma.
Dividir la autoridad sin dividir servidores
Un host MCP elige qué herramientas ascendentes se convierten en capacidades, por lo que una instalación de este paquete puede mapear solo read_text_file mientras que una segunda instalación — apuntando a una raíz diferente — mapea solo write_text_file. Un programa lector generado entonces no puede resolver la herramienta de escritura en absoluto. Ver examples/ptc-host.json.
Uso desde Node
El paquete también es una biblioteca. openRoot valida la configuración y fija la raíz; createServer construye el mismo McpServer que sirve el binario, y le das el transporte que quieras.
import { createServer, openRoot } from 'ptc-fs-mcp'
const root = openRoot({ root: './workspace', include: ['**'], exclude: ['*.secret'] })
const server = createServer(root)
await server.connect(myTransport)examples/embed.mjs es una versión ejecutable que escribe un archivo, lo lee de vuelta y lo busca — todo en un solo proceso sobre el transporte en memoria del SDK:
npm run build && node examples/embed.mjsopenRoot lanza ConfigError en una configuración inutilizable, y las herramientas lanzan ToolError; ambos se exportan, junto con normalizeRelative, compileGlob, createSelector y DEFAULT_LIMITS, para que un host pueda reutilizar el contrato de rutas sin reimplementarlo. Las declaraciones de TypeScript se incluyen con el paquete.
Protocolo
Solo 2026-07-28. No hay respaldo de initialize, ni negociación de degradación, ni rama de compatibilidad: una apertura de la era 2025 se rechaza con el error de versión de protocolo no soportada que nombra el perfil que implementa este servidor. Solo se anuncia la capacidad tools — sin Roots, Sampling, Logging ni Tasks.
Confinamiento
Solo rutas relativas. Las rutas absolutas, los segmentos
./.., los bytes NUL y los separadores de Windows se rechazan en lugar de resolverse.Los enlaces simbólicos se omiten, nunca se siguen, por lo que un enlace dentro de la raíz no puede alcanzar bytes fuera de ella. El
openfinal usaO_NOFOLLOW, por lo que un enlace intercambiado después de la comprobación aún falla.Un directorio aparece en un listado solo porque contiene algo servido, por lo que el nombre de un directorio no servido nunca se filtra.
write_text_fileacepta un solo nombre base en minúsculas — sin directorios, sin recorrido — limita la carga útil y confirma que el destino es un archivo regular a través del descriptor que escribirá en lugar de unstatseparado que un enlace simbólico podría adelantar. Un destino fuera de--includese rechaza, porque una escritura que no pudieras leer de vuelta es una trampa más que una característica. Dado que una escritura aterriza en la raíz, las reglas de inclusión que solo alcanzan subdirectorios rechazan cada escritura; ver Ejecución.Los listados de rutas son ciegos al contenido; las herramientas de contenido rechazan lo que no pueden decodificar.
read_text_filefalla en un archivo que no es UTF-8 válido, ysearch_textomite una línea cuyos bytes no se decodifican, por lo que una línea se informa completa o no se informa en absoluto.Los resultados se ajustan al resultado MCP decodificado completo, y la búsqueda de texto también tiene un presupuesto de bytes de escaneo. Una página de búsqueda vacía puede por tanto llevar un cursor de progreso cuando un archivo disperso necesita más escaneo.
Los errores son texto corto y accionable — sin stacktraces, sin rutas del host.
No se inicia nada, no se usa red, y stdout lleva solo mensajes de protocolo; los diagnósticos van a stderr.
Lo que no defiende
La raíz debe ser confiable y lo bastante quiescente como para que un actor privilegiado no esté compitiendo contigo. Las APIs de rutas portátiles de Node no pueden confinar por descriptor cada directorio ancestro, por lo que un actor capaz de intercambiar un directorio padre a mitad de llamada está fuera de alcance. El servidor rechaza los enlaces simbólicos observados y usa una apertura final sin seguimiento; no afirma defender una raíz fuente activamente hostil.
La obsolescencia del cursor se detecta a partir del tamaño, mtime, ctime y número de inodo. En un sistema de archivos con granularidad de timestamp gruesa, una reescritura en el lugar de exactamente la misma longitud dentro del mismo tick de timestamp no se detectaría. Cada sistema de archivos convencional en el que esto se ejecuta registra tiempos de nanosegundos, y ctime no se puede establecer desde el espacio de usuario.
Desarrollo
npm install
npm run build # tsc to dist/, with declarations and source maps
npm test # builds, then runs the suite against the built binary
npm run verify # format check, typecheck, and testsLa suite impulsa el dist/cli.js construido como un proceso hijo real sobre stdio real, por lo que lo que se envía es lo que se prueba. Las raíces se generan por prueba en lugar de confirmarse, porque este servidor escribe además de leer.
Licencia
MIT. Ver LICENSE.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Agent-native MCP server over the public saagarpatel.dev corpus. Read-only, stateless.
Artifact store for AI agents. Hosted OAuth at mcp.artifacta.io/mcp; local stdio via npm/PyPI.
Project management MCP for AI agents with safe task reads and writes.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/andreasronge/ptc-fs-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server