Skip to main content
Glama
andreasronge

ptc-fs-mcp

by andreasronge

ptc-fs-mcp

Небольшой файловый MCP-сервер: чтение и запись файлов в пределах одного изолированного корневого каталога, через stdio.

Демонстрационное программное обеспечение. Оно существует для того, чтобы у агентных сред выполнения был реальный, детерминированный внешний инструмент, на который можно ссылаться в руководствах, примерах и интеграционных тестах. Он намеренно достаточно мал, чтобы его можно было прочитать за один присест и скопировать в свой проект. Не разворачивайте его как продакшн-сервис файлов.

Он был создан для агентного фреймворка PtcRunner, где возможность работы с файловой системой появляется целиком через конфигурацию хоста, а не через код времени выполнения. В самом сервере нет ничего специфичного для PtcRunner — он говорит на обычном MCP через stdio, так что любой MCP-клиент может его установить.

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

Инструменты

Инструмент

Действие

Возврат

list_directory

чтение

Отсортированные постраничные записи в пределах относительного префикса

search_files

чтение

Отсортированные постраничные пути, содержащие литеральную подстроку

search_text

чтение

Постраничные литеральные совпадения с указанием пути и строки

read_text_file

чтение

Постраничное точное содержание UTF-8 байтов

write_text_file

запись

Заменяет один обычный файл, сообщает путь и байты

Четыре инструмента чтения принимают необязательные cursor и limit и возвращают ровно items, next_cursor и content_hash. Начните без курсора и следуйте за next_cursor, пока он не станет null. Для read_text_file конкатенация текста элементов text восстанавливает файл.

Живые байты

Чтение отражает файловую систему на момент вызова, поэтому запись видна следующему чтению. В этом и заключается смысл сервера, и у этого есть два следствия, которые лучше проговорить сразу, чем обнаружить позже.

Курсоры скорее ломаются, чем рвутся. Курсор несёт в себе дайджест состояния, от которого зависит обход. Если состояние изменилось, следующая страница отклоняется с ошибкой traversal state changed; start the traversal again. Только состояние, от которого реально зависит результат, влияет на курсор, поэтому несвязанное изменение не инвалидирует обход:

Инструмент

Не работает, когда

Работает, когда

list_directory

Изменяется набор записей в пределах охвата

Изменение происходит глубже в уже перечисленном поддереве

search_files

Изменяется набор путей в пределах охвата

Изменяется содержимое файла, путь которого уже возвращён

search_text

Изменяется содержимое любого файла в пределах охвата

Изменение происходит за пределами охваченного пути

read_text_file

Изменяется содержимое файла

Изменяется любой другой файл

Каждый вызов возвращает content_hash. Это HMAC-SHA-256 от байтов, которые возвращает инструмент, ключом служит производный от корня процесса секрет. Поэтому инструмент доказывает, что прочитал именно те байты, которые вернул. write_text_file возвращает и длину, и хэш, поэтому запись и последующее чтение можно сверить друг с другом.

Нет хэша всего дерева и нет snapshot_identity для установки. Дайджест может покрывать только ограниченный снимок, и этот сервер не делает снимков.

Запуск

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

Параметр

Назначение

--root <dir>

Каталог, к которому ограничивается доступ. Обязательный.

--include <glob>

Обслуживать только пути, соответствующие шаблону. Обязательный, повторяемый.

--exclude <glob>

Никогда не обслуживать пути, соответствующие шаблону. Повторяемый; может сужать --include.

--max-file-size <bytes>

Не обслуживать файлы больше этого размера.

--max-write-size <bytes>

Максимальный размер полезной нагрузки write_text_file. По умолчанию 65536.

--include обязателен, и по умолчанию файлы не обслуживаются — сервер, запущенный без него, ничего не отдаёт. Исключённые пути пропускаются до любого stat или open, поэтому они не могут просочиться в результаты и не создают условий гонки. Инструменты чтения применяют одинаковые glob-выражения, и search_text дополнительно требует, чтобы совпадающий путь был текстовым.

Запись доверяет дескриптору, а не пути. write_text_file принимает только одно простое имя файла — без каталогов, без обхода, без точек — и открывает его с O_CREAT|O_EXCL, поэтому он никогда не перезаписывает существующий файл. Затем он записывает и вызывает fsync, и все это через тот же дескриптор. Символьные ссылки запрещены, а не разыменовываются. Файл за пределами --include отклоняется до открытия.

Протокол

Только 2026-07-28. Нет запасного initialize, нет согласования понижения версии, нет ветки совместимости: открытие протокола 2025 года отклоняется ошибкой неподдерживаемой версии протокола с указанием реализуемого профиля. Рекламируется только capability tools — никаких Roots, Sampling, Logging или Tasks.

Ограничения

  • Только относительные пути. Абсолютные пути, сегменты ./.., NUL-байты и разделители Windows отклоняются, а не разрешаются.

  • Символические ссылки пропускаются и никогда не разыменовываются, поэтому ссылка внутри корня не может достать байты за его пределами. Финальный open использует O_NOFOLLOW, так что ссылка, подменённая после проверки, всё равно не сработает.

  • Каталог появляется в списке только потому, что в нём есть обслуживаемое содержимое, поэтому имя каталога, не входящего в охват, не утекает.

  • write_text_file принимает одно имя в нижнем регистре — без каталогов, без обходов — ограничивает полезную нагрузку и подтверждает, что целевой файл является обычным файлом, через дескриптор, в который будет производиться запись, а не через отдельный stat, который могла бы обойти символическая ссылка. Запись за пределами --include отклоняется, потому что запись, которую нельзя прочитать обратно, — это не функция, а баг. Включения, доходящие только до подкаталогов, не разрешают запись на верхнем уровне; см. Запуск.

  • Списки путей не зависят от содержимого; инструменты работы с содержимым отказываются от того, что не могут декодировать. read_text_file завершается ошибкой на файле, не являющемся корректным UTF-8, а search_text пропускает строку, байты которой не декодируются. Строка либо есть целиком, либо её нет.

  • Результаты подгоняются под полный декодированный результат MCP, а текстовый поиск также имеет ограничение на объем сканирования. Пустая страница поиска может нести курсор, если разреженный файл требует большего сканирования.

Ничего не порождается, сеть не открывается, переменные окружения не читаются. Ошибки — это короткий практичный текст, а не трассировки стека.

Что сервер не защищает

Корень должен быть доверенным и достаточно стабильным, чтобы противник не мог гоняться за вами. Переносимые Node-пути не могут защитить каждый родительский каталог, поэтому корень, который могут подменить во время вызова, выходит за рамки. Сервер отклоняет абсолютные пути и .., но не защищает от корня, который может быть переименован или подменён во время работы. Не размещайте его поверх недоверенного корня.

Временные окна: каталог перечисляется по содержимому, а файл проверяется на регулярность при открытии. Недоверенный корень может переключать записи между этими двумя проверками. Сервер закрывает окно с помощью O_NOFOLLOW и проверки fstat на том же дескрипторе, но не может предотвратить подмену родительского каталога. Для доверенного корня этого достаточно.

Running

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

Параметр

Назначение

--root <dir>

Корневой каталог, в пределах которого всё ограничено. Обязательно.

--include <glob>

Обслуживать пути, соответствующие glob. Повторяемый. Обязательно.

--exclude <glob>

Никогда не обслуживать пути, соответствующие glob. Повторяемый.

--max-file-bytes <n>

Не обслуживать файлы больше этого размера.

--max-write-bytes <n>

Наибольший полезный размер записи. По умолчанию 65536.

--include обязателен, и по умолчанию никакие файлы не обслуживаются — сервер, запущенный без него, не отдаёт ничего. Исключённые пути пропускаются до любого stat или open, поэтому даже имена файлов, соответствующих --exclude, никогда не наблюдаются вне корня.

Порядок применения: сначала --exclude, затем --include. Каталог обслуживается только тогда, когда он содержит обслуживаемый файл, поэтому пустые каталоги не видны.

Протокол

2026-07-28 только. Нет запасного initialize, нет согласования понижения версии, нет ветки совместимости: открытие эпохи 2025 года отклоняется ошибкой неподдерживаемой версии протокола с именем реализуемого этим сервером профиля. Рекламируется только возможность tools — никаких Roots, Sampling, Logging или Tasks.

Ограничения

  • Только относительные пути. Абсолютные пути, сегменты ./.., NUL-байты и разделители Windows отклоняются, а не разрешаются.

  • Символические ссылки пропускаются, а не разыменовываются, поэтому ссылка внутри корня не может дотянуться до байтов вне его. Финальный open использует O_NOFOLLOW, поэтому ссылка, подставленная после проверки, всё равно не сработает.

  • Каталог появляется в списке только потому, что содержит что-то обслуживаемое, поэтому имя каталога ничего не утекает.

  • write_text_file принимает одно имя файла в нижнем регистре — без каталогов, без обхода — ограничивает полезную нагрузку и подтверждает, что целевой файл является обычным, через дескриптор, в который будет производиться запись, а не через отдельный stat, который ссылка могла бы обойти. Запись за пределами --include отклоняется, потому что запись, которую нельзя прочитать обратно, — это скорее ловушка, чем функция. Поскольку запись попадает в корень, правила включения, которые не покрывают записанное имя, отклоняют её: включайте все пути, куда вы разрешаете писать.

  • Перечисление путей не зависит от содержимого; инструменты работы с содержимым отказывают в том, что не могут прочитать. read_text_file завершается ошибкой на недопустимом UTF-8. Поиск строки пропускает строки, которые не декодируются как UTF-8, чтобы не смещать номера строк.

  • Результаты ограничены максимальным размером и квотами на количество элементов; исчерпание квоты возвращает частичные результаты с cursors и hasMore: true. Пустой результат по-прежнему является допустимым ответом.

GXP3, GXP4, GXP5, GXP6, GXP7, GXP8, GXP9 отсутствуют в оригинале.

-
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