Files

5.9 KiB
Raw Permalink Blame History

Notion PC Bridge (notion-mcp) — deploy + verify

Repo hytthynn/notion-mcp → C:/Users/User/tmp/notion-mcp. Суть: Notion AI (Custom MCP server) управляет Windows-ПК через desktop-commander (26 тулов: файлы/терминал/процессы/поиск) поверх ngrok-туннеля. Cookie Notion не нужны — только MCP api_key + ngrok authtoken.

Процедура (проверена end-to-end)

  1. Зависимости: Node 22+; npm ci в репо (mcp-proxy 6.7.19 + @wonderwhy-er/desktop-commander 0.2.52, ~660 пакетов, ~3 мин). НЕ запускать npm audit fix --force (ломает версии).
  2. ngrok: один exe — скачать https://bin.equinox.io/c/bNyj1mQVY4c/ngrok-v3-stable-windows-amd64.zip, распаковать в hermes/tools/ngrok/. Authtoken: ngrok config add-authtoken "$NGROK_AUTHTOKEN" → сохранится в %LOCALAPPDATA%/ngrok/ngrok.yml.
  3. config.json писать вручную Python-скриптом (configure_notion.py = интерактивный визард с getpass — не пайпится). База config.example.json; обязательные поля:
    • api_key ≥32 симв (secrets.token_urlsafe(48)) — bridge.py exit'ит при коротком;
    • secret_path = 'mcp-'+secrets.token_hex(24) — часть capability-URL;
    • desktop_commander.allowedDirectories — НЕ пустой (пустой = вся ФС, bridge отказывается стартовать); задавать выделенную test-папку;
    • ngrok_bin = полный путь к ngrok.exe (иначе ищет только winget Packages);
    • ngrok.config_path = путь к ngrok.yml со строкой authtoken: (bridge парсит из yml ТОЛЬКО authtoken+domain и всегда驱动 ngrok CLI-флагами; env NGROK_AUTHTOKEN сам не читает);
    • port — проверить занятость: netstat -ano | grep ':PORT .*LISTENING'. 8888 на этой машине держит Docker (com.docker.backend) — использовать 8890; Docker не убивать, менять порт в config.json.
  4. Запуск bridge.py (background=true, долгоживущий сервер, без notify):
    cd C:/Users/User/tmp/notion-mcp && env -u PYTHONPATH USERPROFILE='C:\Users\User' PATH="/c/Users/User/AppData/Local/hermes/tools/ngrok:$PATH" <Python311>/python.exe bridge.py > bridge.log 2>&1
    
    USERPROFILE передавать ИНЛАЙН аргументом env — bash export в background-вызове до ребёнка не доходит, Path.home() падает RuntimeError: Could not determine home directory (bridge.py читает home на строке ~38, до всего).
  5. Public URL: curl -s http://127.0.0.1:4040/api/tunnels (локальный ngrok API) → public_url. Полный MCP URL = public_url + '/' + secret_path. Домен на free-тарифе рандомный при каждом старте — всегда перечитывать, CONNECTION.txt обновлять.
  6. Верификация перед отдачей (три пробы):
    • GET на MCP endpoint → 405 = норма (MCP говорит POST, 405 доказывает что путь живой);
    • POST с неверным X-API-Key → 401 (auth enforcement работает);
    • JSON-RPC initialize POST ({"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{...}}}) с хедерами X-API-Key, Content-Type: application/json, Accept: application/json, text/event-stream, ngrok-skip-browser-warning: 1 (обязателен — ngrok-free отдаёт browser-interstitial) → 200 SSE c serverInfo: desktop-commander.
  7. Handoff: полный URL + api_key + шаги Notion AI записать в локальный CONNECTION.txt в репо — секреты в чат НЕ paste'ить (правило Влада). Notion AI: Connections → Add custom MCP server → URL, Name (например windows), Auth = API Key, header name X-API-Key, value = api_key → Connect.

Safety — ВСЕГДА предупреждать юзера перед подключением

  • File-операции ограничены allowedDirectories, но терминал desktop-commander НЕ sandboxed (README прямо говорит) — Notion AI через терминальные тулы читает/ранит ЛЮБОЙ файл машины, минуя folder-рестрикт. blockedCommands в config.json — базовый блок-лист, не изоляция.
  • На машине с прод-инфрой (ключи/сессии/гейтвеи): рекомендовать выделенную машину/VM (демо автора юзало отдельные "Notion PC") или ручные аппрувы; «Skip all approvals + With internet» на основной машине = облачный LLM с полным шеллом + prompt-injection-риск.
  • Держать bridge остановленным когда не используется (публичный endpoint, защищён только api_key+secret path).

Питфолы

  • Идентификация PID на порту: tasklist //FI "PID eq N" молча пуст → powershell -NoProfile -Command "Get-Process -Id N; (Get-CimInstance Win32_Process -Filter 'ProcessId=N').CommandLine".
  • «port N is already in use» в bridge.log = живой посторонний процесс (Docker), а не орфан прошлого запуска — netstat + PowerShell-идентификация до смены порта.
  • Краш на старте без лога = смотреть bridge.log: bridge.py падает быстро (конфиг-валидация, home dir), ngrok API при этом не поднимается.