Skip to main content
Glama
andreasronge

ptc-fs-mcp

by andreasronge

ptc-fs-mcp

Ein kleiner Dateisystem-MCP-Server: Liest und schreibt Dateien unter einem einzigen eingeschränkten Root über stdio.

Demosoftware. Sie existiert, damit agentische Laufzeiten ein echtes, deterministisches externes Werkzeug haben, auf das sie in Tutorials, Beispielen und Integrationstests verweisen können. Sie ist bewusst klein genug, um in einem Durchgang gelesen und in das eigene Projekt kopiert zu werden. Stellen Sie sie nicht als Produktionsdateidienst bereit.

Sie wurde für das PtcRunner-Framework gebaut, bei dem eine Dateisystemfähigkeit vollständig über die Host-Konfiguration und nicht über Laufzeitcode entsteht. Nichts am Server ist PtcRunner-spezifisch – er spricht einfaches MCP über stdio, sodass jeder MCP-Client ihn installieren kann.

npx -y ptc-fs-mcp --root ./workspace --include '**'

Werkzeuge

Werkzeug

Effekt

Rückgabe

list_directory

lesen

Sortierte, paginierte Einträge unter einem relativen Präfix

search_files

lesen

Sortierte, paginierte Pfade, die eine wörtliche Teilzeichenfolge enthalten

search_text

lesen

Paginierte wörtliche Treffer mit Pfad- und Zeilenbeleg

read_text_file

lesen

Paginierte exakte UTF-8-Byte-Blöcke

write_text_file

schreiben

Ersetzt eine reguläre Datei, meldet Pfad und Bytes

Die vier Lesewerkzeuge akzeptieren optionale cursor- und limit-Parameter und geben genau items, next_cursor und content_hash zurück. Beginnen Sie ohne Cursor und folgen Sie next_cursor, bis er null ist. Bei read_text_file rekonstruiert das Verketten der text-Elemente die Datei exakt.

Live-Bytes

Lesezugriffe spiegeln das Dateisystem zum Zeitpunkt des Aufrufs wider, sodass ein Schreibvorgang für den nächsten Lesezugriff sichtbar ist. Das ist der Sinn des Servers, und es hat zwei Konsequenzen, die es wert sind, ausgesprochen statt entdeckt zu werden.

Cursors schlagen fehl, statt zu reißen. Ein Cursor trägt einen Digest des Zustands, von dem seine Traversierung abhängt. Wenn sich dieser Zustand geändert hat, wird die nächste Seite mit the filesystem changed since this cursor was issued; start the traversal again abgelehnt. Eine still gerissene Seite – halb vor der Änderung, halb danach – ist das eine Ergebnis, für das sich ein Fehler lohnt.

Nur der Zustand, von dem ein Ergebnis tatsächlich abhängt, ist gebunden, sodass ein Cursor nicht durch eine unzusammenhängende Änderung ungültig wird:

Werkzeug

Schlägt fehl, wenn

Überlebt

list_directory

Die aufgelisteten Einträge sich ändern

Eine Datei erscheint tiefer in einem aufgelisteten Unterverzeichnis

search_files

Die Menge der passenden Pfade sich ändert

Der Inhalt einer passenden Datei wird bearbeitet

search_text

Inhalt oder Identität einer Datei im Geltungsbereich sich ändern

Eine Änderung außerhalb des durchsuchten Präfixes

read_text_file

Diese eine Datei sich ändert

Jede andere Datei sich ändert

Cursors sind mit einem prozessspezifischen Schlüssel signiert, an das Werkzeug und seine Argumente gebunden und müssen genau wie ausgegeben präsentiert werden. Ein Cursor aus einer anderen Traversierung, einem anderen Prozess oder einer bearbeiteten Zeichenfolge wird abgelehnt.

Jedes Ergebnis trägt content_hash, den SHA-256-Digest der Bytes, die der Aufruf zurückgegeben hat. Ein Zitat benennt dann die tatsächlich gelesenen Bytes und nicht einen Baum, der zufällig zu einem anderen Zeitpunkt existierte. write_text_file meldet denselben Digest über die geschriebenen Bytes, sodass ein Schreibvorgang und der darauffolgende Lesezugriff gegeneinander geprüft werden können.

Es gibt keinen Gesamtbaum-Hash und kein snapshot_identity zu installieren. Ein Digest kann nur eine begrenzte Erfassung abdecken, und dieser Server nimmt keine vor.

Ausführen

ptc-fs-mcp --root ./workspace --include 'lib/**' --include 'docs/**' --exclude '**/secrets/**'

Option

Bedeutung

--root <dir>

Verzeichnis, auf das beschränkt wird. Erforderlich.

--include <glob>

Stelle passende Pfade bereit. Erforderlich, wiederholbar.

--exclude <glob>

Stelle passende Pfade nie bereit. Wiederholbar; darf nur einschränken.

--max-file-bytes <n>

Stelle keine Dateien bereit, die größer als dies sind.

--max-write-bytes <n>

Größte write_text_file-Nutzlast. Standard 65536.

--include ist obligatorisch und der Standard ist keine Dateien, sodass ein ohne es gestarteter Server nichts offenlegt. Ausgeschlossene Pfade werden vor jedem stat oder open übersprungen, sodass sie nie inventarisiert werden. Globs matchen * innerhalb eines Segments und ** über Segmente hinweg; lib/** wählt sowohl lib/a.ts als auch lib/deep/a.ts aus.

Schreibvorgänge landen im Root, also müssen die Include-Regeln ihn erreichen. write_text_file benennt einen einzelnen Basisnamen, nie ein Verzeichnis, sodass jeder Schreibvorgang direkt in den Root geht. Eine Include-Menge, die nur in Unterverzeichnisse reicht – --include 'lib/**' – stellt diese Dateien zum Lesen bereit, kann aber keinen Schreibvorgang akzeptieren, und jeder Versuch wird mit no --include pattern of this root matches a file in the root itself abgelehnt. Das ist eine legitime Konfiguration für eine schreibgeschützte Installation, also startet der Server trotzdem und sagt dies auf stderr:

ptc-fs-mcp: no --include pattern matches a file in the root itself, so
write_text_file will refuse every call.

Wo das Schreibwerkzeug zugeordnet ist, verwenden Sie --include '**' oder fügen Sie ein Muster auf Root-Ebene wie --include '*.md' neben den Verzeichnismustern hinzu.

Installieren Sie es aus einem Host-Dokument, indem Sie eine Version festlegen:

"transport": {
  "type": "stdio",
  "command": "npx",
  "args": ["-y", "ptc-fs-mcp@0.1.0", "--root", "workspace", "--include", "**"],
  "inherit_environment": true
}

Spawning ohne geerbte Umgebung

Diese Form benötigt PATH doppelt: npx wird darauf gefunden, und die installierte Binärdatei beginnt mit #!/usr/bin/env node, das den Interpreter ebenfalls darauf auflöst. Ein Host, der mit einer bereinigten Umgebung spawnet – PtcRunners inherit_environment: false, das seine eigenen End-to-End-Tests verwenden – kann den Server überhaupt nicht starten, und der Fehler erscheint als Akquisitionsfehler wie provider_unavailable und nicht als etwas, das PATH benennt. Versionsmanager machen das schärfer, nicht weicher: Ein nvm-Interpreter lebt unter einem Pfad wie ~/.nvm/versions/node/v20.19.0/bin/node und existiert nirgendwo sonst.

Die beiden Konfigurationen schließen sich gegenseitig aus. Um hermetic zu spawnen, installieren Sie das Paket im Voraus und benennen Sie Interpreter und Skript absolut, wobei Sie sowohl npx als auch den Shebang umgehen:

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

Der Server selbst braucht nichts aus der Umgebung: Er spawnet keinen Prozess, öffnet keine Netzwerkverbindung und liest keine eigene Variable. --root wird relativ zum Arbeitsverzeichnis aufgelöst, also machen Sie es ebenfalls absolut, es sei denn, der Host setzt ein cwd, das Sie kontrollieren. hermetic_workspace in examples/ptc-host.json ist diese Form.

Autorität aufteilen, ohne Server aufzuteilen

Ein MCP-Host wählt aus, welche Upstream-Werkzeuge zu Fähigkeiten werden, sodass eine Installation dieses Pakets nur read_text_file zuordnen kann, während eine zweite Installation – auf einen anderen Root gerichtet – nur write_text_file zuordnet. Ein generiertes Leserprogramm kann dann das Schreibwerkzeug überhaupt nicht auflösen. Siehe examples/ptc-host.json.

Verwendung aus Node

Das Paket ist auch eine Bibliothek. openRoot validiert die Konfiguration und fixiert den Root; createServer baut denselben McpServer, den die Binärdatei bedient, und Sie geben ihm den Transport, den Sie möchten.

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 ist eine ausführbare Version, die eine Datei schreibt, sie zurückliest und durchsucht – alles in einem Prozess über den In-Memory-Transport des SDK:

npm run build && node examples/embed.mjs

openRoot wirft ConfigError bei einer unbrauchbaren Konfiguration, und Werkzeuge erheben ToolError; beide werden exportiert, zusammen mit normalizeRelative, compileGlob, createSelector und DEFAULT_LIMITS, sodass ein Host den Pfadvertrag wiederverwenden kann, ohne ihn neu zu implementieren. TypeScript-Deklarationen werden mit dem Paket geliefert.

Protokoll

Nur 2026-07-28. Es gibt kein initialize-Fallback, keine Downgrade-Verhandlung und keinen Kompatibilitätszweig: Eine Öffnung aus der 2025-Ära wird mit dem Fehler für nicht unterstützte Protokollversion abgelehnt, der das Profil benennt, das dieser Server implementiert. Nur die tools-Fähigkeit wird beworben – keine Roots, kein Sampling, kein Logging, keine Tasks.

Einschränkung

  • Nur relative Pfade. Absolute Pfade, ./..-Segmente, NUL-Bytes und Windows-Trennzeichen werden abgelehnt statt aufgelöst.

  • Symbolische Links werden übersprungen, nie verfolgt, sodass ein Link im Root keine Bytes außerhalb erreichen kann. Das finale open verwendet O_NOFOLLOW, sodass ein nach der Prüfung eingetauschter Link immer noch fehlschlägt.

  • Ein Verzeichnis erscheint in einer Auflistung nur, weil es etwas Bereitgestelltes enthält, sodass der Name eines nicht bereitgestellten Verzeichnisses nie durchsickert.

  • write_text_file akzeptiert einen einzelnen Kleinbuchstaben-Basisnamen – keine Verzeichnisse, keine Traversierung – begrenzt die Nutzlast und bestätigt das Ziel als reguläre Datei über den Deskriptor, in den es schreiben wird, und nicht über ein separates stat, das ein Symlink überholen könnte. Ein Ziel außerhalb von --include wird abgelehnt, weil ein Schreibvorgang, den Sie nicht zurücklesen könnten, eine Falle statt eines Features ist. Da ein Schreibvorgang im Root landet, lehnen Include-Regeln, die nur in Unterverzeichnisse reichen, jeden Schreibvorgang ab; siehe Ausführen.

  • Pfadauflistungen sind inhaltsblind; Inhaltswerkzeuge lehnen ab, was sie nicht dekodieren können. read_text_file schlägt bei einer Datei fehl, die kein gültiges UTF-8 ist, und search_text überspringt eine Zeile, deren Bytes nicht dekodieren, sodass eine Zeile entweder vollständig oder gar nicht gemeldet wird.

  • Ergebnisse werden gegen das vollständig dekodierte MCP-Ergebnis angepasst, und die Textsuche hat auch ein Scan-Byte-Budget. Eine leere Suchseite kann daher einen Fortschrittscursor tragen, wenn eine spärliche Datei mehr Scannen benötigt.

  • Fehler sind kurzer, handlungsorientierter Text – keine Stacktraces, keine Host-Pfade.

  • Nichts wird gespawnt, kein Netzwerk wird verwendet, und stdout trägt nur Protokollnachrichten; Diagnosen gehen an stderr.

Was es nicht verteidigt

Der Root muss vertrauenswürdig und ruhig genug sein, dass ein privilegierter Akteur nicht mit Ihnen um die Wette läuft. Portable Node-Pfad-APIs können nicht jedes übergeordnete Verzeichnis deskriptorbeschränken, sodass ein Akteur, der ein übergeordnetes Verzeichnis während eines Aufrufs austauschen kann, außerhalb des Rahmens liegt. Der Server lehnt beobachtete Symlinks ab und verwendet ein finales Open ohne Follow; er behauptet nicht, eine aktiv feindselige Quellwurzel zu verteidigen.

Cursor-Staleness wird aus Größe, mtime, ctime und Inode-Nummer erkannt. Auf einem Dateisystem mit grober Zeitstempelgranularität würde eine In-Place-Neuschreibung exakt derselben Länge innerhalb desselben Zeitstempel-Ticks nicht erkannt. Jedes gängige Dateisystem, auf dem dies läuft, zeichnet Nanosekundenzeiten auf, und ctime ist nicht aus dem Benutzerraum setzbar.

Entwicklung

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 tests

Die Suite treibt das gebaute dist/cli.js als echten Kindprozess über echtes stdio, sodass das, was ausgeliefert wird, das ist, was getestet wird. Roots werden pro Test generiert statt committet, weil dieser Server sowohl schreibt als auch liest.

Lizenz

MIT. Siehe LICENSE.

-
license - not tested
-
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

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.

View all MCP Connectors

Latest Blog Posts

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