MCP и подключаемые инструменты
Подключайте Nessie к MCP-серверам и свяжите внешнего агента с Nessie, чтобы он работал в Nessie от вашего имени.
Что такое MCP
Model Context Protocol — это небольшое открытое соглашение о том, как описывать инструменты языковой модели и вызывать их. Сервер объявляет, что умеет: список именованных инструментов, у каждого из которых есть JSON Schema для аргументов. Клиент находит этот список и вызывает инструменты от имени модели. Соглашение намеренно простое: ценность в том, что одна интеграция, написанная один раз, работает с любой моделью и любым продуктом, который на нём говорит.
Nessie работает с обеих сторон
Об этом стоит говорить точно: две стороны легко спутать, и они намеренно идут разными путями.
- Исходящие. Nessie — это MCP-клиент. Вы подключаете серверы, которые запускает кто-то другой, и инструменты, которые они предоставляют, становятся инструментами ваших агентов. Управление идёт через
/api/mcp/*. - Входящие. Nessie также является MCP-сервером. Агент, работающий где-то ещё, например Claude Code или Codex на компьютере разработчика, может обращаться к вашим доскам и документам. Эндпоинт —
POST /mcp. Он намеренно вынесен за пределы/api, чтобы два противоположных значения не стояли в одном сегменте пути рядом.
Подключение MCP-сервера к Nessie
Записи каталога и экземпляры
Есть два объекта, и различие между ними важно:
- Запись каталога — это описание сервера: его имя, транспорт, способ аутентификации. Её пишут один раз, и ею можно делиться.
- Экземпляр — это запись, установленная в одной области вместе с учётными данными, которые открывают доступ к серверу. Установка одной и той же записи для двух разных команд даёт два экземпляра.
Большинство людей ни с одним из них напрямую не работает. Вход в админку — магазин приложений по адресу /admin/apps. Он показывает тот же каталог, скрывая параметры подключения, а Connect в карточке создаёт экземпляр. Операторы, которым нужны исходные объекты, работают с /api/mcp/catalog и /api/mcp/instances.
Если сервера ещё нет в каталоге, POST /api/mcp/discover принимает адрес и возвращает то, что сам сервер сообщает о себе. POST /api/mcp/library/import загружает запись из публичной библиотеки серверов. Подключение при первом запуске можно создать сразу по адресу через POST /api/apps/custom.
Транспорты
Nessie подключается к удалённым серверам по HTTP. Работают два транспорта: streamable HTTP (текущий транспорт MCP) и более старый HTTP+SSE. Выбирайте http, если только сервер не предлагает лишь sse.
Транспорт stdio отклоняется для коннекторов, которые пишете вы сами. stdio означает, что MCP-сервер — это локальный процесс, который запускает хост. Запускать произвольные программы внутри сервера Nessie оператор не может осмысленно проверить. Серверы, которые опубликованы только как пакет для локального запуска, по той же причине не попадают в импорт из библиотеки. Значение ws существует в поле протокола, но не реализовано: подключение с ним завершается ошибкой, когда Nessie пытается его открыть.
Как Nessie аутентифицируется на сервере
Запись каталога объявляет один из пяти способов: none, api_key, bearer, basic или oauth2.
api_key — это внедрение заголовка: запись указывает, какой заголовок и с каким префиксом. Поэтому Authorization: Bearer …, X-API-Key: … и X-Auth-Token: Token … — один и тот же способ, только настроенный по-разному. Ключ вставляется один раз через POST /api/mcp/instances/:instanceId/secret и больше никогда не читается. Ни один ответ маршрутов экземпляра не содержит ссылки на учётные данные.
oauth2 бывает в двух видах. Сервер с фиксированными эндпоинтами указывает их в записи каталога. Сервер, который публикует стандартные метаданные, не требует никакой настройки: Nessie читает документы защищённого ресурса и сервера авторизации и создаёт себе клиент через динамическую регистрацию клиента или через документ метаданных client ID, который публикует по своему well-known пути. В любом случае рукопожатие выглядит так: POST /api/mcp/instances/:instanceId/oauth/start, затем перенаправление провайдера обратно на GET /api/mcp/oauth/callback. State-токен одноразовый, хранится в базе и действителен десять минут. PKCE используется всегда.
Одно правило часто удивляет: даже владелец организации не может привязать личный или OAuth-токен к другому человеку. Общие учётные данные возможны только для записи с API-ключом, которая проверена как таковая. Всё остальное отклоняется. Учётные данные, принадлежащие человеку, остаются учётными данными этого человека.
Где хранятся учётные данные
Секреты коннекторов и пакеты OAuth-токенов хранятся в вашей собственной базе Postgres, в конвертах AES-256-GCM, привязанных к назначению. Шифруются они под набором ключей развёртывания, который версионируется независимо, и адресуются только непрозрачными ссылками secret_*. Они намеренно не лежат во внешнем хранилище секретов, где хранятся другие секреты Nessie. Продакшен, в котором не настроено зашифрованное хранилище, не запускается вовсе. Он не станет проводить OAuth-рукопожатие, которое затем некуда было бы сохранить.
Где действует подключение
Экземпляр устанавливается в одной из шести областей: system, organization, project, team, channel или user. Область — это жёсткий предел того, какие запуски могут использовать экземпляр. Разрешения её только сужают, но не расширяют, поэтому коннектор, установленный с вашей учётной записью, не может незаметно стать общеорганизационным.
Кто и где может устанавливать коннекторы, устроено так же: владельцы и администраторы организации управляют установками в любой области; все остальные устанавливают коннекторы в своей личной области user и видят общие установки, до которых им разрешён доступ.
Выдача инструмента агенту
Подключение сервера никому не передаёт его инструменты. Когда экземпляр установлен, Nessie опрашивает его и регистрирует каждый объявленный инструмент в реестре инструментов. Именно его админка показывает по адресу /admin/advanced/tools: встроенные инструменты, инструменты MCP, инструменты наборов и инструменты исполнителя в одном списке. У каждого указан источник и статус.
У зарегистрированного инструмента один из трёх статусов: active, pending_review или disabled. Инструменты из общей установки получают pending_review и должны быть одобрены на этой странице, прежде чем агент их увидит. Инструменты из вашей личной установки в области user сразу активны, потому что единственный затронутый человек — это вы. Проверка повторяется регулярно, это не разовая формальность. Если при следующей проверке окажется, что описание или схема инструмента изменились, он возвращается в pending_review и перестаёт предлагаться, пока кто-нибудь не посмотрит на изменение.
Какие агенты могут вызывать активный инструмент, решается одним из двух способов:
- Подключение, созданное через магазин приложений, требует явного разрешения. Агент получает инструмент только тогда, когда разрешение его называет. Разрешение сверяется с текущим отпечатком инструмента, поэтому изменённый инструмент требует нового решения.
- В остальных случаях инструмент включён по умолчанию для агентов в пределах области его действия. Отдельных агентов выключают на панели сведений об инструменте.
Разрешение называет ровно одного субъекта: роль или агента, но не оба сразу. Запрет побеждает разрешение. Для подключения в области user вопрос не возникает: любой агент, с которым разговаривает этот человек, может его использовать. Единственное, что его отключает, — явное выключение.
Легко искать не там. /settings/accounts в админке — это подключённые аккаунты: Slack, почтовые ящики, подписки на модели и OAuth-связи с Linear, Jira, GitHub и Trello, на которых держатся внешние доски. MCP-коннекторы находятся в /admin/apps, а инструменты, которые они создают, — в /admin/advanced/tools.
Подтверждение вызовов инструментов
Некоторые вызовы инструментов останавливаются и ждут человека. Когда это происходит, запуск не падает: вызов замораживается. Сохраняются имя инструмента, хеш его аргументов и состояние, достаточное для продолжения. Запрос на подтверждение появляется в /api/approvals, чтобы кто-то на него ответил. Тот, кто решает, видит ограниченную по объёму сводку того, что запрашивается. Точные аргументы и исходный запуск остаются на сервере. Возобновление идемпотентно, поэтому дублированное подтверждение не выполнит вызов дважды.
Есть два разных вида проверок, и настраиваются они по-разному.
- Структурные проверки — это свойство некоторых встроенных инструментов, их нельзя настроить. Так защищены: отправка почты с почтового ящика агента или подключённого ящика, отправка черновика Gmail, отключение почтового аккаунта, создание, изменение или отмена события в календаре, а также действие в браузере, которое записывало бы данные на сайт, в который браузер не вошёл. На ответ им даётся 24 часа вместо обычных 30 минут.
- Политики задаются правилами политик для пары
tool/invoke. Правило, которое разрешает вызов, может содержатьrequiresApproval. Без проверенного подтверждения оценка возвращаетAPPROVAL_REQUIREDвместо выполнения. Правила оцениваются сначала запреты, а затем разрешения, и упорядочены по области.
Какой экран админки создаёт правило политики requiresApproval для инструмента, мы не смогли подтвердить. Модель данных и принудительное применение уже реализованы, но экран, который записывает такое правило, нам неизвестен.
Подключение внешнего агента к Nessie
Обратное направление. Nessie запускает собственный MCP-сервер по адресу POST /mcp поверх streamable HTTP. Он не хранит состояние: каждый вызов несёт собственные учётные данные и сам определяет действующее лицо. Поэтому сессии нет, и привязать клиента к одной реплике нельзя. ChatGPT, Claude, Claude Code, Codex или агент на чьём-то ноутбуке могут работать с вашими досками, документами и клиентами изнутри того приложения, которым этот человек уже пользуется. Пошаговая инструкция для каждого приложения находится в разделе Подключение ChatGPT, Claude и других ИИ-приложений.
Что такое выданные учётные данные на самом деле
Это важно понять правильно, потому что очевидная модель, ключ API с прикреплёнными правами, здесь неверна.
Выданные учётные данные никогда не являются личностью. Они указывают на человека, и каждый вызов выполняется от его имени, через те же сервисные функции, которые вызывают HTTP-маршруты. Поэтому агент не может дотянуться до того, до чего человек, давший ему доступ, не мог бы дотянуться, нажимая кнопки. Области только сужают этот доступ. Нет ни одной области, которая его расширила бы.
Актуальность проверяется при каждом вызове и никогда не берётся из токена: отзыв, срок действия, версия токена пользователя, его членство в организации и текущая роль. Понижение, деактивация или «выйти со всех устройств» срабатывают при следующем вызове агента, а не по истечении срока учётных данных.
Токен непрозрачный, с префиксом nag1_, хранится только как хеш SHA-256 и живёт 90 дней. На любом маршруте, кроме MCP-эндпоинта, он отклоняется с конкретным 403. Предъявлять его в другом месте — это не частичный успех, который стоит повторить. Именно это не даёт «я дал агенту доступ к своим доскам» превратиться в «я отдал ему свой аккаунт».
Два способа подключения
Приложение, которое умеет открыть браузер, например ChatGPT, Claude, Claude Code или Codex, использует профиль авторизации MCP: OAuth 2.1 с PKCE (S256), динамическую регистрацию клиента по адресу POST /mcp/oauth/register, токены обновления и resource indicators. Приложение отправляет человека на /settings/security. Он выбирает, что приложению можно делать и какие из его команд оно охватывает, и браузер возвращает его в приложение с кодом. Токен доступа живёт час и обновляется. Подключение живёт 90 дней.
Агент командной строки не управляет браузером и не может принимать адрес обратного вызова, поэтому он подключается через device authorization grant (RFC 8628).
- Агент вызывает
POST /mcp/auth/deviceсclientNameи нужными емуscopes. В ответ он получаетdevice_code, короткийuser_code,verification_uriиverification_uri_complete, в котором код уже стоит в строке запроса, а также интервал опросаintervalи срокexpires_in. Агент выводит адрес. - Человек открывает этот адрес, войдя в систему, на странице
/settings/security. Он видит, какой агент спрашивает, от чьего имени тот будет работать, до какого времени и что именно сможет делать. После этого он выбирает Разрешить или Не разрешать. - Агент опрашивает
POST /mcp/auth/tokenсdeviceCode, учитываяauthorization_pending,slow_down,expired_tokenиaccess_denied. При успехе он получает{ access_token, token_type, scope, expires_at }.
В любом случае нужен шаг человека: учётные данные наследуют права человека, поэтому человек должен решить их выдать. Ничего не выдаётся из ниоткуда. Подключение можно выключить для всей организации в /admin/security?tab=programs, и этот ответ проверяется в момент, когда кто-то разрешает подключение, а не когда агент его начинает.
Обнаружение
Неаутентифицированный POST /mcp отвечает 401 с заголовком WWW-Authenticate, который указывает на /.well-known/oauth-protected-resource (RFC 9728). Этот документ называет текущий источник сервером авторизации. Его метаданные по адресу /.well-known/oauth-authorization-server (RFC 8414) называют эндпоинты регистрации, авторизации, токенов и отзыва. Этой цепочки клиенту достаточно, как только у него есть адрес. Чтобы найти адрес по домену, агент читает /.well-known/ai-catalog.json, который указывает на Server Card по адресу /mcp/server-card (MCP Server Cards, SEP-2127). Эндпоинты device grant перечислены в документе защищённого ресурса в поле x-nessie-device-authorization.
Развёртывание может также отвечать по второму, более короткому адресу. https://nessie.works/mcp облачной версии Nessie проксируется на API. Клиенту, пришедшему таким путём, этот адрес возвращается как resource, а вход по-прежнему проходит через API. Редирект здесь не подошёл бы: клиент отклоняет метаданные, в которых назван адрес, отличный от того, что он получил. Оператор перечисляет такие источники в NESSIE_MCP_ALIAS_ORIGINS.
Области и инструменты
Области намеренно крупные: область, которую никто не может объяснить, никто и не настроит правильно. Подключение, которое охватывает несколько команд, перечисляет их через nessie_team_list, а каждый другой инструмент затем называет свой teamId.
| Область | Что открывает |
|---|---|
boards_read | Проекты, доски и задачи с их комментариями, файлами, метками, типами и чек-листами |
boards_write | Создание, правка и перемещение задач и всё, что на них есть |
documents_read | Пространства знаний, документы и таблицы |
documents_write | Составление и правка этих материалов, а также запрос на публикацию документа |
clients_read | Входящие клиентов, контакты, списки контактов, рассылки и контактные формы |
clients_write | Заметки, принятие, назначение и закрытие разговоров, списки контактов и подготовка рассылок |
clients_send | Всё, что получает клиент: ответ, первое письмо или SMS. Не включается, пока человек сам её не отметит |
website_tools | Инструменты страниц зарегистрированного сайта вашей команды, которыми управляют в размещённом браузере, где вы можете взять управление на себя. Не включается, пока человек сам её не отметит |
Вызов без нужной ему области возвращается как результат инструмента с указанием, какую область запросить. Это не ошибка транспорта. Так модель может сообщить что-то полезное, вместо того чтобы снова и снова биться о стену.
Отдельного семейства инструментов для внешних трекеров нет. Доска остаётся доской, независимо от того, создана ли задача в Nessie или зеркалирована из Linear, Jira, GitHub или Trello. Поэтому один набор инструментов покрывает оба случая, а каждая задача указывает, откуда она. Различается запись: доска, зеркалированная из источника только для чтения, отказывает в записи текстом. Она сообщает агенту, что задача принадлежит другой системе, и что повтор не поможет.
Публикация документа — это подтверждение, а не область доступа
Агенты составляют черновики, публикует человек. Для агентов Nessie это правило соблюдается тем, что агент-актор полностью отклоняется. Но выданные учётные данные действуют как человек, поэтому такой отказ их не поймает. Проверка должна стоять в другом месте.
Поэтому nessie_doc_publish не публикует. Он открывает запрос на подтверждение knowledge.page.publish, тот же, что открывает агент Nessie, и возвращает { status: "awaiting_approval", approvalId }. Опрос возвращает тот же запрос, а не открывает второй. Решение принимается так же, как любое другое решение о публикации в Nessie: человеком, по конкретному документу, в момент, когда он существует.
Области публикации намеренно нет. Раньше она была: галочка при подключении означала «этот агент может публиковать». Её убрали. Она решала один раз и на девяносто дней вопрос, который везде в продукте задаётся для каждого документа. Решала она его до того, как документ существовал, поэтому человек, поставивший галочку, не знал, что именно он разрешает публиковать. nessie_doc_publish требует documents_write, потому что запрос тоже не бесплатен: он читает страницу и выносит решение на чей-то суд. Но эта область ничего не даёт в отношении публикации.
В запросе записаны два субъекта, и различие между ними существенно. Запрашивающий — это учётные данные, а не человек, от имени которого они действуют. Обязательный подтверждающий — этот человек, и ответить может только он. Если бы в запросе запрашивающим назвали человека, правило, которое не даёт никому подтверждать собственный запрос, исключило бы единственного человека, имеющего право решать.
Просмотр и отзыв выданного
Выданные агентам доступы человека перечислены в /settings/security: у каждого есть название и кнопка отзыва. Владелец или администратор организации видит все учётные данные организации в /admin/security?tab=programs: как каждый назван, что он может делать, жив ли он и чьим аккаунтом пользуется. Любой из них можно отозвать. Личный список остаётся только для себя. Список действующих учётных данных — это список точек опоры, а не сведения для всех.
Запись, сделанная через учётные данные, приписывается человеку, чьим доступом воспользовались. В метаданных аудита стоит идентификатор учётных данных. Поэтому журнал может отличить правку самого человека от правки, которую сделал за него агент.
Что пока недоступно
Говорим прямо: разрыв между возможностями протокола и возможностями конкретного сервера — обычно то место, где документация лукавит.
- Чат, каналы, запуски и управление агентами по MCP не открыты.
- Программа готовит рассылку, а отправляет её человек. Ничто не отправляет, не планирует и не удаляет кампанию, а само письмо пишется в конструкторе писем.
- Связывание и слияние контактов, изменения схемы CRM, импорты и настройка каналов остаются за человеком в браузере.
- Переключатель подключения — это настройка на уровне организации. Переопределений на уровне команды или личных переопределений ни команда, ни человек пока задать не могут.