nessie

MCP і підключені інструменти

Підключайте Nessie до MCP-серверів і спарюйте зовнішнього агента, щоб він міг працювати в Nessie від вашого імені.

Що таке MCP

Model Context Protocol — це невелика відкрита угода про те, як описувати інструменти для мовної моделі та викликати їх. Сервер оголошує, що вміє: список іменованих інструментів, кожен зі схемою JSON для своїх аргументів, — а клієнт знаходить цей список і викликає інструменти від імені моделі. Він навмисно простий і без зайвого: перевага в тому, що одна інтеграція, написана раз, працює з усіма моделями та з усіма продуктами, які нею користуються.

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 приймає URL і повертає те, що сервер сам про себе повідомляє, а POST /api/mcp/library/import підтягує запис із публічної бібліотеки серверів. Перше підключення також можна створити безпосередньо з URL через 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 читає документи protected-resource і authorization-server, а клієнта створює через динамічну реєстрацію клієнта або через документ метаданих client-ID, який публікується за його well-known-шляхом. В обох випадках рукостискання має вигляд POST /api/mcp/instances/:instanceId/oauth/start, а потім перенаправлення провайдера назад на GET /api/mcp/oauth/callback. Токен state одноразовий, зберігається в базі даних і дійсний десять хвилин, а PKCE використовується завжди.

Одне правило здивує людей: навіть власник організації не може прив’язати персональний токен або токен OAuth до іншої людини. Спільні облікові дані можливі лише для запису з API-ключем, який перевірено як такий; усе інше відхиляється. Облікові дані, що належать людині, лишаються обліковими даними цієї людини.

Де зберігаються облікові дані

Секрети конекторів і пакети токенів OAuth зберігаються у вашій власній базі даних Postgres, у envelope-шифруванні 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-маршрути. Тож агент не може дістатися до нічого, до чого людина, яка його надала, не могла б дістатися кліком. Scope лише звужують цей доступ; жоден scope його не розширює.

Актуальність перевіряється під час кожного окремого виклику, а не береться з токена: відкликання, термін дії, версія токена користувача, його членство в організації та поточна роль. Пониження, деактивація або «вийти скрізь» набирають чинності вже на наступному виклику агента, а не в момент закінчення терміну дії облікових даних.

Токен непрозорий, має префікс nag1_, зберігається лише як хеш SHA-256 і живе 90 днів. Він відхиляється з окремою відповіддю 403 на будь-якому маршруті, крім кінцевої точки MCP. Пред’явлення його деінде не є частковим успіхом, який варто повторити, і саме це не дає «я позичив агенту свої дошки» перетворитися на «я позичив йому свій обліковий запис».

Два способи підключення

Застосунок, який може відкрити браузер — ChatGPT, Claude, Claude Code, Codex, — використовує профіль авторизації MCP: OAuth 2.1 з PKCE (S256), динамічну реєстрацію клієнта за адресою POST /mcp/oauth/register, refresh-токени та resource indicators. Застосунок надсилає людину на /settings/security, вона обирає, що йому дозволено робити і до яких її команд він матиме доступ, а браузер повертається до застосунку з кодом. Його access-токен живе годину і оновлюється; підключення живе 90 днів.

Агент командного рядка не керує браузером і не може мати callback URL, тож він спарюється через грант авторизації за пристроєм (device authorization grant, RFC 8628).

  1. Агент викликає POST /mcp/auth/device з clientName і scopes, які йому потрібні. Він отримує device_code, короткий user_code, verification_uri і verification_uri_complete з кодом, уже вписаним у рядок запиту, а також інтервал опитування interval і expires_in. Агент виводить URI.
  2. Людина відкриває цей URI, увійшовши в систему, на сторінці /settings/security. Вона бачить, який агент запитує доступ, від імені якого облікового запису він працюватиме, до якого часу і що саме він зможе робити, — і обирає Дозволити або Не дозволяти.
  3. Агент опитує 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). Цей документ називає цей origin сервером авторизації, а його метадані за адресою /.well-known/oauth-authorization-server (RFC 8414) називають кінцеві точки реєстрації, авторизації, токена та відкликання. Цього ланцюжка клієнту достатньо, коли він має адресу. Щоб знайти адресу за доменом, агент читає /.well-known/ai-catalog.json, який вказує на Server Card за адресою /mcp/server-card (MCP Server Cards, SEP-2127). Кінцеві точки грантів пристрою перелічені в документі protected-resource під ключем x-nessie-device-authorization.

Розгортання також може відповідати за другою, коротшою адресою: https://nessie.works/mcp хмарної версії Nessie проксіюється до API, і клієнт, який прийшов так, отримує цю адресу назад як ресурс, тоді як вхід лишається на API. Перенаправлення тут не підійде, бо клієнт відхиляє метадані, які називають будь-яку адресу, крім тієї, яку він отримав. Оператор перелічує такі origin-и в NESSIE_MCP_ALIAS_ORIGINS.

Scope і інструменти

Scope навмисно грубі: scope, який ніхто не може пояснити, ніхто не налаштує правильно. Підключення, що охоплює кілька команд, перелічує їх через nessie_team_list, а кожен інший інструмент тоді вказує свій teamId.

ScopeДоступ до
boards_readПроєкти, дошки та завдання з їхніми коментарями, файлами, мітками, типами та чек-листами
boards_writeСтворення, редагування та переміщення завдань і всього, що на них є
documents_readПростори знань, документи та таблиці
documents_writeСкладання та редагування їх, а також запит на публікацію документа
clients_readВхідні клієнтів, контакти, списки контактів, розсилки та форми контактів
clients_writeНотатки, взяття, призначення та закриття розмов, списки контактів і підготовка розсилок
clients_sendУсе, що отримує клієнт: відповідь, перший лист або текстове повідомлення. Не вмикається, доки людина сама її не позначить
website_toolsІнструменти сторінок зареєстрованого сайту вашої команди, з якими працюють у розміщеному браузері, яким ви можете керувати. Не вмикається, доки людина сама не позначить цей пункт

Виклик без потрібного scope повертається як результат інструмента з назвою scope, який треба запросити, а не як помилка транспорту. Тож модель може повідомити щось корисне, а не битися об стіну повторними спробами.

Окремої родини інструментів для зовнішніх трекерів немає. Дошка — це дошка незалежно від того, чи її завдання створено в Nessie, чи відтворено з Linear, Jira, GitHub або Trello, тож один набір інструментів охоплює обидва випадки, і кожне завдання вказує, звідки воно. Відрізняється лише запис: дошка, відтворена з джерела лише для читання, відхиляє запис із поясненням словами, повідомляючи агенту, що завдання належить іншій системі, і повторні спроби не допоможуть.

Публікація документа — це схвалення, а не scope

Агенти складають чернетки, а публікує людина. Це правило для агентів Nessie забезпечується тим, що агентного актора взагалі відхиляють, але спарені облікові дані ідентифікуються як людина, тож це відхилення їх не зупинить. Контрольна точка має бути в іншому місці.

Тож nessie_doc_publish нічого не публікує. Він відкриває запит на схвалення knowledge.page.publish — те саме, що відкриває агент Nessie, — і повертає { status: "awaiting_approval", approvalId }. Опитування повертає той самий запит, а не відкриває другий. Рішення ухвалюється так, як ухвалюється кожне рішення про публікацію в Nessie: людиною, щодо цього документа, у момент, коли він існує.

Навмисно немає scope для публікації. Раніше він був — галочка під час спарювання, що агент може публікувати, — і його прибрали. Він раз і на дев’яносто днів вирішував питання, яке в Nessie скрізь ставлять окремо для кожного документа, і вирішував його до того, як документ існував, тож людина, що ставила галочку, не могла знати, що саме вона погоджується опублікувати. nessie_doc_publish потребує documents_write, бо запитати — не безкоштовно: він читає сторінку і виносить рішення на розгляд людини, але цей scope нічого не дає щодо публікації.

На запиті записано дві ідентичності, і ця різниця має принципове значення. Ініціатор — це облікові дані, а не людина, від імені якої вони діють. Обов’язкова особа, що схвалює, — це ця людина, і лише вона може відповісти. Якби запит називав людину ініціатором, правило, що не дає нікому схвалювати власний запит, виключило б єдину людину, яка має право вирішувати.

Перегляд і відкликання спарених агентів

Спарені агенти людини перелічені на /settings/security з назвою та кнопкою відкликання для кожного. Власник або адміністратор організації бачить на /admin/security?tab=programs усі облікові дані в організації — як кожні з них називаються, що вони можуть робити, чи вони живі та чий обліковий запис позичають — і може відкликати будь-які. Персональний список лишається лише для самої людини: список живих облікових даних — це перелік точок опори, а не загальне читання.

Записи, зроблені через облікові дані, віднесено до людини, що їх позичила, і в метаданих аудиту вказано ідентифікатор облікових даних. Тож журнал може відрізнити власну правку людини від тієї, яку зробив її агент від її імені.

Що поки недоступно

Кажемо прямо, бо розрив між можливостями протоколу та можливостями конкретного сервера — саме те місце, де документація найчастіше вводить в оману:

  • Чат, канали, запуски та керування агентами через MCP недоступні.
  • Програма готує розсилку, а відправляє її людина: ніщо не надсилає, не планує й не видаляє кампанію, а сам лист пишеться в редакторі.
  • Зв’язування та злиття контактів, зміни схеми CRM, імпорти та налаштування каналів лишаються за людиною в браузері.
  • Перемикач підключення — це налаштування на рівні організації. Команда чи людина не можуть задати перевизначення на рівні команди або особисте сьогодні.