No description
  • Go 84%
  • HTML 15%
  • Dockerfile 1%
Find a file
2026-08-12 19:38:43 +03:00
cmd/mcp-fs feat: В режиме debug выводится jwt для отладки 2026-08-12 19:12:58 +03:00
docs first commit 2026-08-12 11:34:19 +03:00
internal fix: Исправление возвращения массива списка файлов согласно спецификации MCP 2026-08-12 19:38:43 +03:00
.gitignore fix: derive JWT AllowAnonymous from jwt.unauthorozerd instead of storage config 2026-08-12 18:01:11 +03:00
.woodpecker.yml fix: CI 2026-08-12 18:14:19 +03:00
config.yml.example first commit 2026-08-12 11:34:19 +03:00
Dockerfile first commit 2026-08-12 11:34:19 +03:00
go.mod feat: В режиме debug выводится jwt для отладки 2026-08-12 19:12:58 +03:00
go.sum feat: В режиме debug выводится jwt для отладки 2026-08-12 19:12:58 +03:00
LICENSE first commit 2026-08-12 11:34:19 +03:00
README.md first commit 2026-08-12 11:34:19 +03:00

mcp-fs

MCP-сервер для работы с файловой системой (filesystem) на Go. Работает в двух режимах:

  • stdio — классический локальный MCP-сервер на stdin/stdout;
  • http — многопользовательский streamable HTTP (MCP) + REST API + встроенный Web UI для управления файлами.

Аутентификация в HTTP-режиме — по JWT (HS256), который выдаёт внешний провайдер (например, OpenWebUI). Идентификатор пользователя берётся из настраиваемого claim и маппится на директорию.

Возможности

  • Режимы: stdio / http (streamable HTTP).
  • Логирование в JSONL через log/slog: файл или консоль; в stdio — stderr, в http — stdout; уровни error|warn|info|debug|off.
  • JWT (HS256): настраиваемый claim, секрет, режим reject / skip.
  • Привязка идентификатора пользователя к директории:
    • нет маппинга → одна общая директория для всех;
    • известный id → своя директория;
    • аноним/неизвестный id → общая «анонимная» директория.
  • 11 MCP-инструментов для чтения/записи/поиска/управления файлами.
  • REST API + Web UI (загрузка, скачивание, правка текста, переименование, удаление, каталоги) с теми же правами доступа, что и MCP.
  • Защита от выхода за пределы директории пользователя (включая симлинки).

Сборка

Требуется Go 1.26+.

go build -o mcp-fs ./cmd/mcp-fs
# или с версионированием
go build -ldflags="-s -w \
  -X git.ymnuktech.ru/ymnuk/mcp-fs/internal/version.Version=1.0.0 \
  -X git.ymnuktech.ru/ymnuk/mcp-fs/internal/version.Commit=$(git rev-parse --short HEAD) \
  -X git.ymnuktech.ru/ymnuk/mcp-fs/internal/version.BuildTime=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
  -o mcp-fs ./cmd/mcp-fs

Быстрый старт

Локально (stdio)

./mcp-fs --mode stdio --storage-root ./data

Подключение через любой MCP-клиент, поддерживающий stdio. В stdio-режиме JWT не используется — файлы размещаются в общей/анонимной директории.

Многопользовательский HTTP

cp config.yml.example config.yml
# задайте jwt.secret и storage.*
./mcp-fs --config config.yml
  • MCP-эндпоинт: POST /mcp (stdlib MCP-клиентам указать URL http://host:8091/mcp)
  • Web UI: http://host:8091/
  • REST API: http://host:8091/api/...

HTTP-эндпоинты требуют заголовок Authorization: Bearer <jwt> (или jwt.header). В режиме unauthorized: reject запросы без валидного JWT отклоняются с 401.

Конфигурация

Парсинг — библиотека go-simple-args. Приоритет: CLI > env > config.yml > defaults. Путь к файлу: --config config.yml или CONFIG_FILE=....

Раздел Ключ CLI / env Default Описание
mode --mode / MODE stdio stdio или http
log file --log-file / LOG_FILE пусто путь JSONL-лога; пусто = консоль
log level --log-level / LOG_LEVEL info error|warn|info|debug|off
http addr --http-addr / HTTP_ADDR :8091 адрес прослушивания
http mcp_path --http-mcp-path / HTTP_MCP_PATH /mcp путь MCP-эндпоинта
http stateless --http-stateless / HTTP_STATELESS true auth на каждый запрос
http max_upload_bytes --http-max-upload-bytes / HTTP_MAX_UPLOAD_BYTES 104857600 лимит upload (100 MiB)
http max_text_file_size --http-max-text-file-size / HTTP_MAX_TEXT_FILE_SIZE 10485760 лимит текста (10 MiB)
mcp default_lines --mcp-default-lines / MCP_DEFAULT_LINES 0 строк по умолчанию для read_file (0 = все)
jwt secret --jwt-secret / JWT_SECRET пусто HS256 секрет
jwt claim --jwt-claim / JWT_CLAIM sub поле JWT — id пользователя
jwt header --jwt-header / JWT_HEADER Authorization заголовок с токеном
jwt unauthorized --jwt-unauthorized / JWT_UNAUTHORIZED reject reject (401) или skip (аноним)
storage root --storage-root / STORAGE_ROOT ./data общая директория
storage anonymous --storage-anonymous / STORAGE_ANONYMOUS пусто директория анонимов
storage users — (только YAML) map id -> директория

Полный пример — config.yml.example.

JWT из OpenWebUI

  1. Войдите в OpenWebUI как нужный пользователь.
  2. Извлеките JWT (например, из localStorage браузера: ключ token / id_token, или из cookie).
  3. Укажите в config.yml тот же секрет, которым OpenWebUI подписывает токены, и claim, который соответствует id пользователя (обычно sub).
  4. В Web UI вставьте токен один раз — он сохранится в localStorage (mcp_fs_jwt). При истечении (ответ 401) токен удаляется автоматически, и UI запросит его заново.

REST API

Все маршруты — под JWT-аутентификацией, ошибки — {"error": "..."}.

Метод Путь Описание
GET /api/root текущий root и identity
GET /api/list?path= список каталога
GET /api/tree?path= рекурсивное дерево
GET /api/file?path= текстовый контент файла
POST /api/file запись текстового файла {path, content}
GET /api/download?path= скачивание файла
POST /api/upload?path= multipart-загрузка файлов
POST /api/dir создать каталог {path}
POST /api/move переименовать/переместить {from, to}
DELETE /api/file?path= удалить файл
DELETE /api/dir?path= удалить каталог (рекурсивно)

MCP-инструменты

list_directory, directory_tree, read_file, write_file, edit_file, create_directory, move_file, delete_file, delete_directory, search_files, get_file_info.

  • read_file определяет тип по содержимому/MIME: текст (txt, md, csv, json, yaml, …) возвращается строкой, изображения — ImageContent, аудио — AudioContent, остальные бинарные — embedded-ресурсом. Для текстовых файлов доступно чтение по частям: offset (1-based номер первой строки) и limit (макс. число строк от offset; если не задан — берётся mcp.default_lines из конфига, 0 = все строки).
  • delete_directory удаляет только пустой каталог; для удаления непустого каталога (рекурсивно) передавайте recursive: true.

Пути в инструментах указываются внутри директории пользователя, например /docs/note.txt (не абсолютные пути хоста).

Docker

docker build -t mcp-fs .
docker run -p 8091:8091 -v "$PWD/data:/app/data" mcp-fs --config /app/config.yml

CI

Woodpecker-пайплайн см. в .woodpecker.yml: lint/test, сборка бинарника, Docker-образ, release.

Документация

  • ROADMAP.md — план с декомпозицией и отметками выполнения.
  • security.md — модель защиты путей, остаточные риски.
  • config.yml.example — пример конфигурации.

Безопасность

  • Каждый запрос (в т.ч. каждый MCP-вызов в stateless-режиме) проверяется на валидность JWT.
  • Все пути проверяются на выход за root двухуровнево: лексически (filepath.Rel, запрет ../абсолютных путей) и через EvalSymlinks (симлинки наружу блокируются даже при создании новых файлов). Подробно — docs/security.md.
  • Не выставляйте сервер без jwt.secret в открытый интернет.