- Go 84%
- HTML 15%
- Dockerfile 1%
|
|
||
|---|---|---|
| cmd/mcp-fs | ||
| docs | ||
| internal | ||
| .gitignore | ||
| .woodpecker.yml | ||
| config.yml.example | ||
| Dockerfile | ||
| go.mod | ||
| go.sum | ||
| LICENSE | ||
| README.md | ||
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-клиентам указать URLhttp://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
- Войдите в OpenWebUI как нужный пользователь.
- Извлеките JWT (например, из localStorage браузера: ключ
token/id_token, или из cookie). - Укажите в
config.ymlтот же секрет, которым OpenWebUI подписывает токены, и claim, который соответствует id пользователя (обычноsub). - В 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в открытый интернет.