Skip to main content
Glama
BigTolly

mcp-local-access

by BigTolly

Languages: Русский | English

MCP (Local Access)

MCP-сервер позволяет облачным сервисам с поддержкой MCP, например Notion AI. Предоставлять доступ к одной папке на вашем компьютере: читать файлы, искать по ним, править, создавать и удалять, а также запускать разрешённые команды.

MCP сервер — это «руки», облачный сервис — «мозг». Вместо копирования кода в чат и обратно, MCP позволяет работать с файлами на ПК.

Зачем это нужно

  • Чат с ИИ видит реальный проект, а не пересказ: точные файлы, точные строки.

  • Правки применяются сразу в файлы, с диффом в ответе.

  • Рамки задаёте вы: одна корневая папка, список запрещённых путей, белый список команд, при желании — режим «только чтение».

Статус. Версия разработана для Windows и MacOS, но реально протестирована только на Windows; ветки для macOS/Linux написаны, но не проверены.

Popup


Содержание

  1. Как это работает

  2. Структура проекта

  3. Требования

  4. Установка

  5. Настройка

  6. Запуск и публикация

  7. Подключение клиента

  8. Настройка через JSON на примере accio.com

  9. Инструменты

  10. Rules.md — краткое описание вашего проекта

  11. Права доступа

  12. Лимиты

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

  14. Диагностика

  15. FAQ


Related MCP server: Secure Local Workspace MCP

1. Как это работает

Notion AI / claude.ai / chatgpt
        │  HTTPS + Bearer-токен
        ▼
cloudflared-туннель  или  свой web сервер с TLS
        │  HTTP внутри локальной сети
        ▼
server.py  (слушает host:port, отдаёт /mcp)
        │  все пути проверяются: песочница + права
        ▼
rootDir + явно разрешённые внешние папки Read/Write

Стек: Python + MCP SDK для Python (FastMCP) + uvicorn, транспорт Streamable HTTP на пути /mcp, авторизация Bearer-токеном. Сервер ничего не знает о способе публикации: он просто слушает host:port, а как этот порт оказался в интернете — дело туннеля или прокси.

2. Структура проекта

Путь

Что это

server.py

MCP-сервер: конфиг, песочница путей, права, регистрация инструментов, авторизация

dialog-box.py

диалог Ask me: интерфейс, звук и ожидание ответа пользователя

config.json

файл настроек, создайте его из config.example.json

config.example.json

шаблон настроек

.env

файл хранит токен MCP_TOKEN, который генерируется при первом запуске server.py

requirements.txt

четыре зависимости: mcp, uvicorn, python-dotenv, websockets

README.md

этот файл

Rules.md

базовые инструкции для ИИ, чтобы ИИ знал минимальный контекст по вашему проекту

logs/ (внутри rootDir)

cmd-NNN.log — вывод команд, audit.log — журнал действий, processes.json — реестр процессов

3. Требования

  • Python 3.10 или новее (python --version).

  • Способ отдать локальный порт наружу по HTTPS — одно из двух:

    • cloudflared, если своего веб сервера нет:

      • Windows: winget install Cloudflare.cloudflared

      • macOS: brew install cloudflared

    • свой обратный прокси с TLS (на базе Nginx, Caddy, Traefik) перед этим портом.

При первом запуске server.py, сам генерирует токен, установка для cloudflare для этой задачи не нужна, он сам сам cloudflared.

4. Установка

cd local-access
python -m venv .venv

# Windows
.venv\Scripts\activate

# macOS / Linux
source .venv/bin/activate

pip install -r requirements.txt

5. Настройка

Все настройки лежат в config.json. Скопируйте шаблон и поправьте:


# Windows
copy config.example.json config.json

# macOS / Linux
cp config.example.json config.json
{
  "rootDir": "C:/usr/mcp/local-access",
  "host": "0.0.0.0",
  "port": 8000,
  "readOnly": false,
  "jsonResponse": false,
  "rulesFile": "Rules.md",
  "logDir": "logs",
  "audit": true,
  "maxCommandTimeout": 600,
  "permissions": { "...": "см. раздел 11" }
}

Ключ

Значение

rootDir

Основная папка файловой песочницы. Всё вне неё закрыто, кроме явно подключённых абсолютными правилами Read/Write папок. Обязательный параметр.

host

Интерфейс для прослушивания. По умолчанию 0.0.0.0 — годится и для туннеля на этой машине, и для прокси на другом хосте. 127.0.0.1 — принимать только локальные подключения.

port

Локальный порт; туннель или прокси смотрит сюда. По умолчанию 8000.

readOnly

true отключает edit_file, multi_edit, create, delete, move и run_command.

jsonResponse

true нужен только клиенту, который не умеет SSE-ответы.

rulesFile

Путь к файлу-путеводителю для модели. Необязательный, см. раздел 10.

logDir

Папка внутри rootDir для логов команд и журнала действий. По умолчанию logs.

audit

false выключает logs/audit.log. По умолчанию true.

maxCommandTimeout

Верхняя граница timeout для run_command, в секундах. По умолчанию 600.

permissions

Правила по путям и командам, см. раздел 11.

browser

Мост к расширению Browser MCP, см. «Браузер (Browser MCP)» в разделе 9. Необязательный.

Несколько файлов настроек

По умолчанию читается config.json. Другой файл задаётся ключом --config (короткая форма -c). Путь указывать не нужно: файл всегда ищется в папке, где лежит server.py.

rem start.cmd
start "MCP Server" cmd /k python C:\usr\mcp\local-access\server.py --config new.json
  • Порядок выбора: --config, затем переменная CONFIG_FILE, затем config.json.

  • Файл, указанный через --config, обязан существовать — иначе сервер не стартует с ошибкой Config file not found. Отсутствие config.json по умолчанию, как и раньше, не ошибка.

  • Активный файл настроек всегда закрыт для модели, как бы он ни назывался: правило *(**/config.json) не покрыло бы new.json. Неактивные запасные конфиги защищайте сами, например "*(**/new.json)" в deny.

  • Имя активного файла сервер печатает при старте в строке config.

В .env — только секретный токен:

# Windows
copy .env.example .env

# macOS / Linux
cp .env.example .env

Оставьте MCP_TOKEN пустым: при первом запуске сервер сгенерирует токен и запишет его в эту же строку. Чтобы сменить токен, очистите значение и перезапустите сервер.

6. Запуск и публикация

Нужны два терминала.

Терминал 1 — сервер:

python server.py

Он печатает эндпоинт, токен и действующие правила:

  local-mcp v0.1
  root      : C:\usr\mcp\my-project
  endpoint  : http://0.0.0.0:8000/mcp
  token     : 3f8c1d...
  read-only : no
  rules     : C:\usr\mcp\local-mcp-v0.1\Rules.md
  logs      : C:\usr\mcp\my-project\logs (audit.log: last 300 lines)
  processes : adopted 1, cleaned 4 old log(s)
  config    : C:\usr\mcp\local-mcp-v0.1\config.json
  perms     : defaultMode allow
    allow : List(**), Read(**), Edit(**), Create(**), Delete(**)
    deny  : *(**/.env), *(**/.env.*), *(**/config.json), *(**/secrets/**)
    run   : Run(npm run dev), Run(npm run build), Run(git status)
  browser   : ws://localhost:9009 - press 'Connect' in the Browser MCP extension

  WARNING: the server is listening on the whole local network.
  For a tighter setup set "host": "127.0.0.1" in config.json.

Вариант A — быстрый туннель cloudflared

Терминал 2:

cloudflared tunnel --url http://localhost:8000 --protocol http2

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

Requesting new quick Tunnel on trycloudflare.com... +--------------------------------------------------------------------------------------------+ | Your quick Tunnel has been created! Visit it at (it may take some time to be reachable): | | https://verbal-univ-silent-bracelet.trycloudflare.com | +--------------------------------------------------------------------------------------------+

В данном случае это https://verbal-univ-silent-bracelet.trycloudflare.com. Обратите внимание, что после каждого запуска url адрес меняется.

  • --protocol http2 заставляет туннель работать по TCP вместо QUIC/UDP. Без этого флага соединение может отваливаться с timeout: no recent network activity, а запросы — падать с ошибкой Cloudflare 1033.

  • Туннель не нужно перезапускать вместе с сервером: он пробрасывает localhost:8000, и пока порт тот же, публичный адрес продолжает работать. Перезапуск нужен, только если вы поменяли port или туннель сам умер.

  • Адрес быстрого туннеля меняется при каждом старте cloudflared — значит, URL в клиенте придётся обновлять.

Вариант B — использование веб сервер перед сервером (постоянный адрес)

Для этого варианта нужно иметь:

  • статический внешний IP-адрес

  • веб сервер с TLS, на котором можно настроить обратный прокси

  • домен, при этом для mcp можно использовать домен третьего уровня

  • получить сертификат для работы https

В config.json:

  • "host": "0.0.0.0" — значение по умолчанию и то, что нужно прокси: на 127.0.0.1 сервер доступен только с этой машины.

  • "port": 8000 — или любой свободный порт, на который смотрит прокси.

  • После правки файла перезапустите server.py и проверьте строку endpoint.

На хосте с прокси нужен только location /mcp — публичный путь это дело прокси, сервер всегда отдаёт /mcp. Пример настройки обратного прокси на Nginx:

server {
    listen 443 ssl;
    server_name mcp.example.com;

    location /mcp {
        proxy_pass http://192.168.1.50:8000/mcp;   # машина, где запущен server.py
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_buffering off;              # SSE должен стримиться, а не буферизоваться
        proxy_request_buffering off;
        proxy_read_timeout 3600s;
        chunked_transfer_encoding on;
    }
}
  • Ключевые строки — proxy_buffering off и proxy_request_buffering off: транспорт стримит SSE, и буферизующий прокси подвешивает каждый ответ до таймаута.

  • Заголовок Authorization проходит без изменений, токен доезжает как есть.

  • Сервер сам переписывает заголовок Host, так что защита SDK от DNS-rebinding не требует дополнительных заголовков (ошибки 421 не будет).

  • TLS терминируется на прокси, до сервера идёт обычный HTTP внутри локальной сети.

Адрес после этого постоянный: https://mcp.example.com/mcp вводится в клиент один раз и переживает перезапуски и сервера, и прокси.

7. Подключение клиента

Notion (Settings → Connections → добавить MCP-сервер):

  • URL: https://ваш-адрес/mcp — суффикс /mcp обязателен

  • Аутентификация: Bearer-токен, префикс Bearer, сам токен — из терминала 1

8. Настройка через JSON на примере accio.com

Некоторые сервисы позволяют добавить MCP-сервер не по полям формы, а одним блоком JSON. Например, в accio.com: Settings → MCP → секция Custom → кнопка Add custom MCP (или «+ Add») → диалог Add Custom MCP Server. В диалоге есть четыре вкладки Configuration Mode: JSON | Stdio (Local) | HTTP | SSE. Откройте вкладку JSON и вставьте этот текст:

{
  "mcpServers": {
    "Local Access": {
      "type": "http",
      "url": "https://your-host.example.com/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_TOKEN"
      }
    }
  }
}

Затем замените значения по умолчанию на свои:

  • url — адрес вашего туннеля или домена, суффикс /mcp обязателен

  • Authorization — префикс Bearer и ваш токен (MCP_TOKEN из .env, сервер печатает его при запуске)

Ключ Local Access — это просто имя подключения в интерфейсе, его можно поменять. Тот же JSON подходит любому клиенту, который принимает конфигурацию в формате mcpServers.

9. Инструменты

Пути внутри проекта задаются относительно rootDir. Для явно разрешённых внешних папок доступны абсолютные пути и ссылки из проекта. Остальные внешние пути и ссылки блокируются. См. «Внешние папки и сетевые пути» в разделе 11.

Путеводитель

Инструмент

Что делает

project_guide()

Возвращает Rules.md: контекст проекта и правила работы

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

Файлы

Инструмент

Что делает

list_files(path, recursive, max_depth)

Листинг папки; recursive включает вложенные

read_file(path, offset, limit)

Файл с номерами строк, постранично для больших файлов

search_text(pattern, path, glob, regex, case_sensitive, context, max_results)

Поиск по проекту, отдаёт путь:строка: текст

file_info(path)

Размер, число строк, время изменения, текст или бинарник

edit_file(path, old_string, new_string, replace_all)

Точная замена, возвращает дифф

multi_edit(path, edits, regex)

Несколько замен в одном файле, всё или ничего

create(path, type, content, overwrite)

Новый файл или папка

move(source, destination, overwrite)

Переименование или перемещение

delete(path, recursive)

Безвозвратное удаление файла или папки

  • Шумные папки (node_modules, .git, dist, build, .venv, venv, __pycache__, .next, .idea) при листинге и поиске пропускаются всегда.

  • У delete нет корзины: непустая папка требует recursive=true, сам корень удалить нельзя, а папка, внутри которой лежит запрещённый правилами файл, не удаляется целиком.

  • move требует прав Delete на источник и Create на приёмник, поэтому защищённый файл нельзя вынести из-под защиты переносом.

  • Один файл не стоит править двумя вызовами одновременно: edit_file и multi_edit читают и пишут файл целиком, параллельные вызовы затирают друг друга.

Команды

Инструмент

Что делает

run_command(command, cwd, timeout, background, wait_for, idle_timeout, restart)

Запускает команду внутри rootDir

list_processes()

Всё, что запущено в этой сессии: живое и завершённое

check_process(id, lines, wait, wait_for)

Состояние и свежий вывод одного процесса

stop_process(id, stop_all)

Убивает процесс вместе с детьми

  • Команды идут через системный шелл, поэтому &&, ||, | и ; работают. Каждое звено цепочки проверяется по правилам Run(...) отдельно, команда без подходящего правила отклоняется.

  • Команда должна быть одной строкой: перевод строки — разделитель команд, так что многострочная строка выполнилась бы лишь частично. Склеивайте шаги через && или положите их в скрипт.

  • background=false (по умолчанию) — для команд, которые завершаются: вызов возвращает код выхода и вывод, процесс после этого гарантированно мёртв. По таймауту убивается всё дерево процессов.

  • background=true — для долгоживущих команд вроде npm run dev. Процесс живёт после вызова; когда вернуть управление, решают wait_for (регулярное выражение) или idle_timeout, например wait_for: "ready in|listening on".

  • Вывод стримится в logs/cmd-NNN.log, а не держится в памяти, так что шумная сборка не разорвёт ответ. Длинный вывод возвращается началом и хвостом.

  • Каждый запущенный процесс отслеживается, и короткий блок [processes] дописывается к любому результату инструмента, пока что-то работает или только что завершилось:

[processes]
  #1 running   npm run dev  (pid 24188, 96s, log logs/cmd-001.log)
  #2 exited (exit 0)  npm run build  (log logs/cmd-002.log)

Так модель остаётся в курсе терминала: MCP-сервер не может сам прислать уведомление внутрь хода модели, поэтому состояние заново подкладывается контекстом при следующем вызове.

  • Запуск команды, которая уже работает, отклоняется со ссылкой на её номер; restart=true сначала останавливает старый процесс.

  • Вывод принудительно переводится в UTF-8 (chcp 65001 на Windows, PYTHONIOENCODING, NO_COLOR), чтобы логи не превращались в кракозябры.

  • Процессы убиваются деревом (taskkill /T на Windows, целая группа процессов на macOS/Linux, сначала SIGTERM, потом SIGKILL), потому что порт держит не npm, а его дочерний node.

  • При выходе сервера все запущенные процессы останавливаются, так что Ctrl+C не оставляет висящих dev-серверов.

  • Реестр процессов дублируется в logs/processes.json. Жёсткое убийство сервера (закрытая консоль, диспетчер задач) не выполняет очистку и оставляет детей живыми, поэтому при старте каждая запись сверяется с ОС: живой pid подхватывается обратно в реестр и показывается как running (adopted), остальное считается завершённым, а их cmd-NNN.log удаляются. Номера продолжают расти, поэтому имена логов не конфликтуют. Подхваченный процесс можно смотреть и останавливать как обычно — теряется только точный код выхода, потому что сервер больше не владеет его хэндлом.

Диалоговое окно

Инструмент show-dialog-box() открывает диалоговое окно Ask me, которое позволяет входить в зацикленный диалог с агентом.

Popup

Сценарий работы

  1. При работе с MCP Local Access в инструкциях агента добавляется правило: после завершения задачи или уточняющего вопроса в чате вызвать show-dialog-box.

  2. Введите следующий запрос в появившемся окне и нажмите OK. Пустой ответ - завершают диалог.

  3. Если пользователь не ответил в течение 50 сек (таймаут можно изменить), то инструмент перезапускает себя, если этого не делать, то инструмент завершится по таймауту.

Настройка

В файле config.json есть параметры, которые настраивают работу с этим инструментом, пример:

"dialog": {
  "width": 500,
  "height": 300,
  "sound": true,
  "autoPrompt": true,
  "waitTimeoutSeconds": 50
}

Настройки задают начальную ширину и высоту окна, время перезапуска инструмента (менять нужно осознанно), включить звук при вызове инструмента, нужно ли вызывать инструмент после ответа.

Автоматическое указание агенту

Инструкция передаётся в MCP-поле instructions и дополнительно с первым результатом инструмента; повторно — при изменении файла правил или после длительного перерыва.

Параметр принимает только JSON true/false. Если параметр отсутствует, указания включены.

Ожидание и таймауты

Когда время одного вызова истекает, инструмент возвращает Call show-dialog-box again to continue waiting for the same window. Агент должен вызвать его снова: продолжится ожидание того же окна, без дубликата и без потери набранного текста. Если ответ уже отправлен, следующий вызов заберёт сохранённый результат. Одновременно ожидающий второй вызов получает сообщение, что ожидание уже идёт.

Управление браузером (Browser Control)

Инструменты MCP Local Access browser_* управляют вашим браузером, используя расширение Browser Control. server.py поднимает WebSocket на ws://localhost:9009, к которому подключается расширение. Браузер могут использовать облачные агенты и работать с сайтами под вашей авторизацией.

Как включить

  1. Установите в Chrome расширение Browser Control.

  2. В браузере откройте нужную вкладку, нажмите на иконку Browser Control и кнопку Connect this tab. Либо агент может сам открыть Chrome и подключить свою вкладку без дополнительных действий со стороны пользователя.

Инструмент

Что делает

browser_status()

Показывает, слушает ли сервер порт, подключена ли вкладка и есть ли агентская сессия; выводит расширенную диагностику

browser_launch(url)

Запускает Chrome с вашим профилем и подключает вкладку агента без Connect

browser_close(force)

Закрывает вкладку агента; Chrome, запущенный агентом, завершает по PID

browser_navigate(url)

Открывает URL, возвращает снимок страницы

browser_go_back() / browser_go_forward()

Назад / вперёд по истории

browser_snapshot()

ARIA-снимок страницы с ref элементов

browser_click(element, ref)

Клик по элементу

browser_hover(element, ref)

Наведение на элемент

browser_drag(start_element, start_ref, end_element, end_ref)

Перетаскивание

browser_type(element, ref, text, submit)

Ввод текста, submit=true — Enter после

browser_select_option(element, ref, values)

Выбор в выпадающем списке

browser_press_key(key)

Нажатие клавиши, например Enter, ArrowLeft

browser_wait(time)

Пауза в секундах, отклоняет отрицательные, бесконечные и превышающие 60 секунд значения

browser_screenshot()

Скриншот вкладки (PNG)

browser_get_console_logs()

Возвращает логи и структурированную информацию о состоянии их сбора

browser_cdp(method, params)

Одна команда Chrome DevTools Protocol для подключённой вкладки (DOM.getDocument, Performance.getMetrics, ...). Browser.*, Target.* и отключение Runtime/Log/Page запрещены, как и куки/хранилище всего браузера и Network.loadNetworkResource

browser_evaluate(expression, await_promise)

JavaScript в странице, как в консоли DevTools; возвращает значение или исключение

browser_cdp_events(action, domain, limit)

Подписка на события домена CDP (subscribe/unsubscribe/read/clear)

browser_diagnostics()

Реальная проверка debugger, подписок, ошибок и потерь сбора

browser_console(limit, level, clear)

Console API, исключения и браузерные Log-сообщения; очистка только явно

browser_network(action, request_id, limit, part, max_chars)

start/stop/clear/list/detail/body; сеть выключена по умолчанию

browser_capture(mode, scale)

viewport/full_page, original/reduced; PNG веб-страницы, не всего окна Chrome

Сценарий для модели: browser_snapshot → взять ref нужного элемента → browser_click / browser_type. Действия возвращают свежий снимок страницы.

В readOnly у browser_cdp разрешены только читающие методы (get*, describe*, query*, search*, resolve*); browser_evaluate и подписки запрещены. В журнал аудита записываются только имя метода и длина выражения, но не параметры и не код.

Сервер отказывает в методах, которые выходят за пределы вкладки (CDP_DENIED_DOMAINS / CDP_DENIED_METHODS в server.py): Browser.*, Target.*, Network.getAllCookies, Storage.getCookies/setCookies/clearCookies, Network.clearBrowserCookies/clearBrowserCache, Storage.clearDataFor*, Network.loadNetworkResource, Network.getCookies с urls, Page.close/crash, а также Page.navigate на javascript: и, по умолчанию, на file:// (если browser.allowFileUrls не установлен в true). Browser Control дополнительно ограничивает Network.setCookie(s)/deleteCookies и методы с origin/securityOrigin/storageKey сайтом текущей вкладки.

Настройка MCP Local Access (необязательно; ниже приведены значения по умолчанию):

"browser": {
  "enabled": true,
  "host": "localhost",
  "port": 9009,
  "timeoutSeconds": 30,
  "allowFileUrls": false,
  "chromePath": "",
  "userDataDir": "",
  "profileDirectory": "",
  "launchArgs": [],
  "launchTimeoutSeconds": 30,
  "closeTimeoutSeconds": 10
}

Ключ

Значение

enabled

false отключает мост: порт не слушается, инструменты отвечают отказом

host

Интерфейс WebSocket. localhost — только эта машина; наружу не открывайте

port

Порт, к которому подключается расширение. Browser MCP знает только 9009; в Browser Control порт меняется в popup

timeoutSeconds

Сколько ждать ответа расширения на одну команду

allowFileUrls

По умолчанию false: сервер запрещает переходы на file:// через browser_navigate / browser_cdp Page.navigate. Для разрешения доступа необходимо установить browser.allowFileUrls в true в config.json, а также установить флажок «Allow file:// pages» во всплывающем окне Browser Control и разрешить расширению «Доступ к URL файлов» в настройках Chrome. Эти разрешения дополняют, а не заменяют друг друга. Такой доступ позволяет агенту читать любой доступный браузеру файл компьютера в обход rootDir и правил Deny

chromePath

browser_launch: путь к chrome.exe / Google Chrome. Пусто: ищется сам (реестр App Paths, Program Files, LocalAppData; /Applications; google-chrome/chromium в PATH)

userDataDir

browser_launch: --user-data-dir. Пусто: ваш обычный профиль (сайты остаются залогинены)

profileDirectory

browser_launch: --profile-directory, например Profile 1. Пусто: профиль по умолчанию

launchArgs

browser_launch: дополнительные флаги Chrome (список строк)

launchTimeoutSeconds

Сколько секунд browser_launch ждёт подключения расширения (минимум 5)

closeTimeoutSeconds

Сколько browser_close ждёт, пока Chrome закроется сам после закрытия последней вкладки, прежде чем завершать его по PID

Важно

  • В режиме readOnly доступны чтение страницы, снимки и чтение диагностики. Действия браузера, очистка журналов и включение/выключение сетевого сбора запрещены.

  • В logs/audit.log записываются переходы, клики и события ввода; сам введённый текст не записывается (это может быть пароль) — только его длина.

  • Браузер не входит в песочницу rootDir: агент видит всё, что открыто в подключённой вкладке, и действует от вашего имени.

  • Для browser_navigate и Page.navigate сервер всегда запрещает переходы на javascript:, а на file:// — только при browser.allowFileUrls=false (значение по умолчанию). Разрешение на сервере не отменяет отдельные ограничения Browser Control и Chrome.

Агентский браузер: browser_launch / browser_close

Установить расширение Browser Control может только пользователь. Агент может сам открыть Chrome и подключить свою вкладку. Если userDataDir пуст, Chrome запускается с вашим профилем и может открывать сайты под вашей учётной записью.

Варианты запуска Chrome:

  • Chrome не запущен: агент запускает его, а после работы закрывает свою вкладку. Если других открытых вкладок нет, агент завершает Chrome по PID.

  • Chrome уже открыт: новая вкладка открывается в последнем активном окне, а по завершении работы агент закрывает только свою вкладку.

Для работы с браузером вкладка агента не обязана быть активной. Агент может получать снимки страницы, нажимать на элементы, переходить по ссылкам и делать скриншоты даже в свёрнутом окне. Страница при этом «спит», и её обновления происходят медленнее.

Сбор ограничен основной CDP-целью выбранной вкладки; дочерние CDP-сессии не агрегируются. Журналы хранятся в памяти и теряются при перезапуске worker, отключении или смене вкладки.

Большие снимки ограничены 20 000 пикселей по стороне и бюджетом 32 млн пикселей с учётом DPR/масштаба; возможны ограничения конкретного Chrome, sticky и lazy loading. Перед выпуском требуется фактическая проверка в Chrome: mock-тесты её не заменяют.

10. Rules.md — краткое описание вашего проекта

Rules.md — (опционально) необязательный файл, кратко опишите свой проект, или можете указать путь к файлам документации по вашему проекту. Смысл простой — не объяснять модели детали о проекте в каждом новом диалоге.

  • Название файла и путь к этому файлу, можно изменить в переменной rulesFile в config.json. Если переменная не задана, то берется файл Rules.md, расположенный в папке указанной в rootDir.

  • Файл перечитывается при изменении, перезапуск не нужен: новый текст приходит со следующим результатом инструмента и в следующей сессии клиента.

11. Права доступа

Правила живут в блоке permissions файла config.json и записываются в стиле Claude Code: Инструмент(glob-путь).

{
  "permissions": {
    "defaultMode": "allow",
    "allow": ["List(**)", "Read(**)", "Edit(**)", "Create(**)", "Delete(**)"],
    "deny": [
      "*(**/.env)",
      "*(**/.env.*)",
      "*(**/config.json)",
      "*(**/secrets/**)",
      "*(**/*.pem)",
      "*(**/*.key)",
      "Edit(**/.git/**)",
      "Create(**/.git/**)",
      "Delete(**/.git/**)"
    ]
  }
}
  • Глаголы: List, Read, Edit, Create, Delete, Run, внешний Write или * для всех путевых глаголов. Соответствие инструментам: List и Read вместе определяют, что показывает list_files; Read покрывает ещё search_text и file_info; Edit покрывает multi_edit; Create — create и приёмник move; Delete — delete и источник move.

  • Run стоит особняком: его аргумент — шаблон команды, а не путь, поэтому *(**) его никогда не выдаёт. См. «Правила для команд» ниже.

  • defaultMode: "allow" — внутри rootDir разрешено всё, что не запрещено (чёрный список). Внешние папки всегда требуют явного абсолютного Read/Write.

  • defaultMode: "deny" — работают только пути из allow (белый список).

  • deny всегда сильнее allow.

  • Обычные шаблоны задаются относительно rootDir, разделитель — /, поддерживаются * (один сегмент), ** (любая глубина) и ? (один символ).

  • Шаблоны привязаны к корню, поэтому .env совпадает только с файлом в корне. Чтобы накрыть все подпапки, нужен префикс **/ — **/.env.

  • secrets/** скрывает и саму папку secrets.

  • Запрещённые элементы не показываются в list_files, вместо них выводится число скрытых записей.

  • Если блока permissions нет, действуют встроенные значения: разрешено всё, кроме **/.env, **/.env.*, **/secrets/**, **/*.pem, **/*.key, плюс запрет записи и удаления внутри **/.git/**.

  • Правила читаются при старте — после правки перезапустите сервер.

  • *(**/config.json) доступ к файлу закрыт для модели, чтобы не переписала собственные права.

Внешние папки и сетевые пути

Чтобы подключить конкретную папку вне rootDir, владелец добавляет в permissions.allow абсолютное правило Read(папка) или Write(папка). Корень проекта менять не нужно. Это разрешение папки и всего её содержимого, а не всего диска.

{
  "permissions": {
    "defaultMode": "allow",
    "allow": [
      "List(**)", "Read(**)", "Edit(**)", "Create(**)", "Delete(**)",
      "Read(J:/src/**)",
      "Write(//debian/share/work)"
    ],
    "deny": [
      "*(**/.env)", "*(**/.env.*)", "*(**/config.json)",
      "*(**/secrets/**)", "*(**/*.pem)", "*(**/*.key)",
      "Delete(//debian/share/work/important/**)"
    ]
  }
}

Внешнее правило

Листинг, поиск, сведения и чтение

Изменение и создание

Удаление и перемещение

Read(J:/src)

Да

Нет

Нет

Write(J:/src)

Да

Да

Да, с проверкой прав источника и назначения

  • Read(J:/src) и Read(J:/src/**) равнозначны: включают саму папку и любую глубину вложенности. То же для Write. Другие шаблоны в разрешении внешней папки запрещены: укажите конкретный абсолютный путь.

  • Поддерживаются дисковые пути Windows (J:/src, J:/Просто так), UNC (//debian/share/src) и абсолютные пути Linux/macOS (/mnt/share/src). Используйте /; в JSON обратный слеш требует экранирования, например "Read(J:\\src)" в виде JSON должен содержать два обратных слеша. На Linux UNC не подключает Samba автоматически: сначала смонтируйте ресурс средствами ОС и разрешите его локальный путь.

  • Read с относительным шаблоном сохраняет старое значение внутри проекта. Write в allow используется только с абсолютной внешней папкой.

  • Широкие относительные Edit(**), *(**) и defaultMode: "allow" не дают внешних прав и не повышают внешний Read до Write.

  • Можно обращаться через src/main.py, если rootDir/src — ссылка на разрешённую папку, либо напрямую через J:/src/main.py или //debian/share/src/main.py. Абсолютные пути внутри самого rootDir по-прежнему не принимаются: используйте относительные.

  • Проверяется конечная цель ссылок. Выход в неразрешённую папку, соседний src-other, неполный UNC, J:src, device-пути и Windows alternate data streams не разрешаются. Листинг и поиск пропускают запрещённые папки и не зацикливаются на ссылках.

  • deny проверяется по пути ссылки, конечной цели и пути относительно подключённой папки. Запреты вроде *(**/.env) и *(**/secrets/**) действуют также снаружи; ссылка с невинным именем не открывает запрещённую цель. Запрет папки действует и на её содержимое. Write(...) в deny блокирует все файловые операции; для запрета только удаления используйте Delete(...).

  • При вложенных разрешениях наиболее конкретная папка определяет уровень: Write(J:/src) плюс Read(J:/src/vendor) делает vendor доступной только для чтения. На одной и той же папке Write повышает Read.

  • readOnly: true блокирует все инструменты изменения даже при Write. Сам rootDir и корни подключённых папок нельзя удалить или переместить. Изменение файла через символьную ссылку сохраняет ссылку; удаление обычной символьной ссылки удаляет ссылку, а не её цель.

  • Доступ зависит и от Windows/SMB: учётная запись сервера должна видеть ресурс. UNC обычно надёжнее буквы диска, привязанной к пользовательскому сеансу. При старте реальный путь разрешения фиксируется; подключите ресурс заранее. Если диск переподключён на другой ресурс или изменена цель ссылки-разрешения, перезапустите сервер и перепроверьте настройки.

  • Read/Write не выдают Run. Внешний cwd команды требует Write и отдельного разрешения Run. Однако команда, запущенная даже внутри проекта, не ограничена файловой песочницей и может обращаться к другим путям.

  • Эти проверки не заменяют изоляцию ОС: локальный процесс, одновременно меняющий ссылки, и разрешённые на стороне Samba переходы за пределы share требуют отдельных ограничений ОС/SMB.

После изменения кода или правил перезапустите сервер. config.example.json по умолчанию не открывает никаких внешних папок; реальные пути добавляет владелец в свой config.json.

Проверка реализации без чтения рабочего конфига и секретов:

python -m unittest discover -s tests -v

Тесты используют временные папки; UNC и сопоставление сетевого диска дополнительно проверяются моделированием, без подключения к реальному Samba-ресурсу.

Правила для команд

Команды — всегда белый список. Run(...) принимает шаблон команды, где * означает любой текст; всё остальное сравнивается буквально, пробелы сжимаются, регистр игнорируется. Если ни одного правила Run нет, run_command отказывает во всём.

{
  "permissions": {
    "allow": [
      "Run(npm run dev)",
      "Run(npm run build)",
      "Run(npm install)",
      "Run(git status)",
      "Run(git diff*)",
      "Run(python *)",
      "Run(pytest*)"
    ],
    "deny": ["Run(*rm -rf*)"]
  }
}
  • Цепочка разбивается по &&, ||, |, ; и переводам строк, и каждый сегмент должен сам совпасть с правилом из allow. Для npm run build && git status нужны оба правила; npm run build && rm -rf / упадёт на втором сегменте.

  • Разбиение учитывает кавычки, поэтому git commit -m "fix | bug" — один сегмент.

  • deny и здесь сильнее, а defaultMode: "allow" на команды не распространяется.

Насколько это обеспечивает безопасность. Не сильно, и это осознанный компромисс. Белый список спасает от случайностей и опечаток, а не от целенаправленной атаки: одного разрешённого python * или npm run * достаточно, чтобы сделать всё, что может обычная программа, включая выход за rootDir — песочница путей ограничивает только файловые инструменты, но никогда не дочерний процесс. Держите список коротким и конкретным и относитесь к запуску команд как к «я доверяю этому клиенту свою учётную запись», а не как к песочнице.

Пример белого списка: читать только src и Markdown, писать только в src/generated:

{
  "permissions": {
    "defaultMode": "deny",
    "allow": ["List(src/**)", "Read(src/**)", "Read(*.md)", "Create(src/generated/**)", "Edit(src/generated/**)"]
  }
}

12. Лимиты

Зашиты в server.py (константы в начале файла), меняются правкой кода:

Лимит

Значение

Размер одного ответа

100 000 символов, дальше обрезка

Чтение/запись содержимого файла

1 МБ

Записей в листинге

1000

Таймаут синхронной команды

60 с по умолчанию, максимум — maxCommandTimeout (600 с)

Ожидание фоновой команды

idle_timeout 15 с по умолчанию

Одновременно фоновых процессов

8

Вывод команды в одном ответе

20 000 символов

Завершённый процесс в реестре

10 минут

audit.log

последние 300 строк

Rules.md

20 000 символов

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

  • Любой, у кого есть публичный URL и токен, может читать, менять и удалять внутри rootDir и явно подключённых внешних папок — в пределах разрешённых операций и правил deny.

  • run_command — самая широкая дыра: дочерний процесс не связан песочницей путей, поэтому разрешённые python *, node * или npm run * могут добраться до всего, до чего дотягивается ваша учётная запись. Держите список Run коротким.

  • Останавливайте фоновые процессы, когда закончили (stop_process(stop_all=true)); остановка сервера по Ctrl+C убивает их тоже. Жёсткое убийство — нет, но при следующем старте живые процессы подхватываются обратно.

  • Выключайте туннель, когда он не нужен.

  • Токен меняется так: очистить MCP_TOKEN в .env и перезапустить сервер.

  • rootDir держите настолько узким, насколько позволяет задача: родительская папка втягивает в песочницу и сам этот проект вместе с его .env.

Если нужно строже: включите подтверждение вызовов на стороне клиента, сузьте rootDir, поставьте "readOnly": true или уберите все правила Run.

14. Диагностика

Симптом

Причина

rootDir is not set

Нет config.json или в нём не задан rootDir

Config file not found

Файла из --config нет в папке server.py

browser : NOT started - port 9009 is busy

Порт занят другим Browser MCP (npx @browsermcp/mcp); закройте его

No connection to the browser extension

Во вкладке не нажат Connect в расширении Browser MCP

the active config file is protected

Модель обратилась к активному файлу настроек — он закрыт всегда

401 от клиента

Токен не совпадает или заголовок называется не Authorization

421 Invalid Host header

Старый server.py без перезаписи Host

406 Not Acceptable

Клиент не отправил заголовок Accept, показанный в разделе 8

Cloudflare 1033

Туннель потерял соединение; перезапустите с --protocol http2

Клиент не видит инструментов

В URL нет суффикса /mcp

Новый инструмент не появился

Клиент закэшировал старый список; переподключите коннектор

Ответы подвешиваются до таймаута

Прокси буферизует SSE: proxy_buffering off

Permission denied

Путь совпал с правилом deny в config.json

Permission denied: Run(...)

Ни одно правило Run не подошло команде или звену цепочки

Already running as #N

Та же команда ещё жива: посмотрите её, остановите или передайте restart=true

Timed out after Ns

Долгоживущая команда запущена без background=true

command must be a single line

Переводы строк разделяют команды; склейте шаги через &&

Absolute paths inside rootDir are not allowed

Для проекта используйте относительный путь

Path escapes the allowed root directory or external grants

Внешняя цель не разрешена абсолютным Read/Write или ссылка вышла из разрешённой папки

external directory is read-only

Для изменения внешней папки нужно явное Write, внутренних Edit(**) недостаточно

15. FAQ

А что если я хочу несколько rootDir?

Один процесс — один основной корень. Для конкретных внешних папок добавьте абсолютные Read/Write из раздела 11. Другие варианты:

  • Указать общую родительскую папку и сузить доступ правами, например List(project-a/**), Read(project-a/**), Edit(project-a/**) и то же для project-b. Просто и работает, но всё лежит в одной песочнице.

  • Запустить второй экземпляр сервера со своим конфигом и портом: конфиг задаётся ключом --config (файл ищется рядом с server.py) или переменной CONFIG_FILE, а порт — в самом конфиге.

    python server.py --config config-b.json
    CONFIG_FILE=/path/to/config-b.json python server.py     # macOS / Linux
    set CONFIG_FILE=C:\path\to\config-b.json && python server.py   # Windows

    Мост к браузеру может работать только в одном экземпляре (порт 9009 один): в остальных поставьте "browser": { "enabled": false }.

    Каждому экземпляру нужен свой публичный адрес (второй туннель или второй location в Nginx) и своё подключение в клиенте. Токен берётся из .env рядом с server.py, так что для разных токенов нужны разные копии проекта.

Обязателен ли cloudflared?

Нет. Нужен любой способ отдать порт наружу по HTTPS: свой Nginx/Caddy/Traefik, именованный туннель Cloudflare, любой другой туннель. Сервер о нём ничего не знает.

Можно ли обойтись без публикации наружу?

Notion и claude.ai работают через интернет, им нужен публичный HTTPS-адрес. Локальные клиенты (Cursor и другие, запущенные на этой же машине) могут подключаться прямо к http://127.0.0.1:8000/mcp — тогда поставьте "host": "127.0.0.1".

Может ли модель выйти за пределы rootDir?

По умолчанию файловыми инструментами — нет. Владелец может явно разрешить конкретные внешние папки абсолютными Read/Write; другие внешние пути и ссылки остаются закрыты. Через run_command — да: дочерний процесс песочницей не ограничен. Если это неприемлемо, не давайте правил Run или включите readOnly.

Как вообще запретить любые изменения?

"readOnly": true — остаются только чтение, поиск и листинг. Промежуточный вариант: оставить Edit/Create на рабочую папку и запретить остальное через deny.

Нужен ли Rules.md?

Нет, сервер работает и без него. Но с ним не приходится в каждом новом диалоге объяснять, что это за проект и как в нём принято работать. См. раздел 10.

Работает ли это на macOS и Linux?

Код есть (/bin/sh, start_new_session, убийство группы процессов), но проверен он только на Windows. Считайте первый запуск на macOS/Linux тестовым.

Модель удалила нужный файл — как откатить?

Никак средствами сервера: корзины нет, удаление безвозвратное. Держите проект под git, а в logs/audit.log можно посмотреть, что именно произошло. Рекомендуется настроить запуск бекапа проекта перед каждой новой задачей.

Почему клиент спрашивает подтверждение на каждый вызов?

Это поведение клиента, сервер на него не влияет. В Notion подтверждения настраиваются в свойствах подключения; права сервера работают независимо от того, что вы нажали.

Токен утёк. Что делать?

Очистить значение MCP_TOKEN в .env, перезапустить сервер (он сгенерирует новый токен), вписать новый токен в клиента. Старый перестаёт работать сразу.

Почему logs/ внутри rootDir, а не рядом с сервером?

Чтобы модель могла сама прочитать вывод команды через read_file. Побочный эффект: добавьте logs/ в .gitignore.

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    B
    maintenance
    Enables secure remote access to your computer's filesystem and terminal through MCP, allowing AI assistants to manage files, run commands, and automate tasks from anywhere via a hosted relay.
    200
    -
  • A
    license
    A
    quality
    B
    maintenance
    Enables ChatGPT and Codex to safely work with explicitly authorized local project folders through MCP, providing constrained file reading, searching, patch editing, Git inspection, and whitelisted tasks without exposing arbitrary shell, deletion, or deployment capabilities.
    17
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables a ChatGPT MCP connection to read, create, and modify text files in explicitly shared folders across multiple self-hosted computers, and to run PowerShell or sh terminal commands under separate per-device authorization.
    MIT