nessie

MCP și instrumente conectate

Conectați Nessie la serverele MCP și asociați un agent extern, ca să poată lucra în Nessie în numele dvs.

Ce este MCP

Model Context Protocol este o convenție mică și deschisă pentru a descrie instrumente unui model de limbaj și pentru a le apela. Un server declară ce poate face — o listă de instrumente cu nume, fiecare cu o schemă JSON pentru argumente — iar un client descoperă lista și le invocă în numele modelului. Este intenționat simplu: valoarea constă în faptul că o integrare scrisă o singură dată funcționează cu fiecare model și cu fiecare produs care vorbește acest protocol.

Nessie se află de ambele părți

Merită să fim precizi în privința asta, pentru că cele două jumătăți se confundă ușor și trăiesc pe căi intenționat diferite.

  • Ieșire. Nessie este un client MCP. Conectați servere pe care le rulează altcineva, iar instrumentele pe care le expun devin instrumente pe care le pot folosi agenții dvs. Suprafața de administrare este /api/mcp/*.
  • Intrare. Nessie este și un server MCP. Un agent care rulează în altă parte — Claude Code sau Codex pe calculatorul unui dezvoltator — poate lucra cu tablele și documentele dvs. Acest endpoint este POST /mcp, în afara lui /api în mod intenționat, ca cele două sensuri opuse să nu ajungă să difere printr-un singur segment de cale.

Conectarea unui server MCP la Nessie

Intrările din catalog și instanțele

Există două obiecte, iar diferența contează:

  • O intrare din catalog este definiția unui server — numele său, transportul, modul în care se autentifică. Se scrie o singură dată și poate fi partajată.
  • O instanță este intrarea instalată la un anumit domeniu, cu credențialele care ajung la ea. Instalarea aceleiași intrări pentru două echipe diferite produce două instanțe.

Majoritatea oamenilor nu le ating niciodată direct. Punctul de acces din administrare este magazinul de aplicații de la /admin/apps: el răsfoiește același catalog, cu detaliile de conexiune ascunse, iar Connect pe o intrare creează o instanță. Operatorii care vor obiectele brute folosesc /api/mcp/catalog și /api/mcp/instances.

Dacă un server nu este încă în catalog, POST /api/mcp/discover primește un URL și returnează ce spune serverul despre sine, iar POST /api/mcp/library/import trage o intrare din biblioteca publică de servere. O conexiune făcută din prima rulare poate fi creată direct dintr-un URL, cu POST /api/apps/custom.

Transporturi

Nessie se conectează la serverele la distanță prin HTTP. Funcționează două transporturi: HTTP transmisibil în flux (transportul MCP actual) și transportul mai vechi HTTP+SSE. Alegeți http, cu excepția cazului în care serverul oferă doar sse.

Transportul stdio este refuzat pentru conectorii pe care îi scrieți. stdio înseamnă că serverul MCP este un proces local pornit de gazdă, iar rularea unor programe arbitrare în interiorul serverului Nessie nu este ceva ce un operator poate verifica cu adevărat. Serverele publicate doar ca pachet, pentru rulare locală, sunt excluse din importurile din bibliotecă din același motiv. Valoarea ws există în câmpul de protocol, dar nu este implementată — o conexiune care o folosește eșuează atunci când Nessie încearcă să o deschidă.

Cum se autentifică Nessie la server

O intrare din catalog declară una dintre cinci metode: none, api_key, bearer, basic sau oauth2.

api_key este injectare de antet, iar intrarea spune ce antet și ce prefix — deci Authorization: Bearer …, X-API-Key: … și X-Auth-Token: Token … sunt aceeași metodă, configurată diferit. Cheia în sine se lipește o singură dată cu POST /api/mcp/instances/:instanceId/secret și nu mai poate fi citită niciodată; niciun răspuns de la rutele instanțelor nu conține o referință la credențiale.

oauth2 există în două forme. Un server cu endpoint-uri fixe le are în intrarea din catalog. Un server care publică metadate standard nu necesită nicio configurare: Nessie citește documentele de resursă protejată și de server de autorizare și își creează singur un client, fie prin înregistrare dinamică a clientului, fie prin documentul de metadate al ID-ului de client pe care îl publică la propria cale well-known. Oricum ar fi, handshake-ul este POST /api/mcp/instances/:instanceId/oauth/start, urmat de redirecționarea furnizorului înapoi la GET /api/mcp/oauth/callback. Tokenul de stare se folosește o singură dată, este păstrat în baza de date și este valabil zece minute, iar PKCE se folosește întotdeauna.

O regulă surprinde pe mulți: nici chiar un proprietar de organizație nu poate atașa altcuiva un token personal sau OAuth. Un credențial partajat este posibil doar pentru o intrare de tip cheie API validată ca atare; orice altceva este refuzat. Un credențial care aparține unei persoane rămâne un credențial al acelei persoane.

Unde se păstrează credențialele

Secretele conectorilor și pachetele de tokenuri OAuth sunt stocate în propria dvs. bază de date Postgres, în plicuri de criptare AES-256-GCM legate de scopul lor, sub inelul de chei de criptare al implementării, versionat independent, și sunt adresate doar prin referințe opace secret_*. Ele se află, în mod intenționat, nu în seiful extern de secrete care păstrează celelalte secrete Nessie. O implementare de producție care nu a configurat magazinul criptat refuză să pornească, în loc să finalizeze un handshake OAuth pe care apoi l-ar pierde.

Unde ajunge o conexiune

O instanță este instalată la unul dintre șase domenii: system, organization, project, team, channel sau user. Domeniul este un plafon ferm pentru rulările care îl pot folosi, iar permisiunile îl pot doar restrânge, niciodată lărgi — deci un conector instalat cu propriul cont nu poate deveni, pe nesimțite, unul la nivelul întregii organizații.

Cine poate instala unde urmează aceeași formă: proprietarii și administratorii organizației gestionează instalările la orice domeniu; ceilalți instalează conectori la propriul domeniu user și pot vedea instalările partajate la care au dreptul să ajungă.

Cum primește un agent un instrument

Conectarea unui server nu îi predă instrumentele nimănui. Când o instanță este instalată, Nessie o sondează și proiectează fiecare instrument pe care îl anunță în registrul de instrumente, care este ce arată administrarea la /admin/advanced/tools — instrumente încorporate, instrumente MCP, instrumente din pachete și instrumente ale executorului, într-o singură listă, fiecare cu sursa și starea sa.

Un instrument proiectat are una dintre trei stări: active, pending_review sau disabled. Instrumentele dintr-o instalare la domeniu partajat ajung ca pending_review și trebuie aprobate pe acea pagină înainte ca un agent să le vadă; instrumentele din instalarea dvs. la domeniul user sunt active imediat, pentru că singura persoană expusă sunteți dvs. Revizuirea se repetă, nu este o formalitate unică: dacă o sondare ulterioară constată că descrierea sau schema unui instrument s-a modificat, acesta revine la pending_review și nu mai este oferit până când cineva nu verifică schimbarea.

Ce agenți pot apela apoi un instrument activ se decide în două moduri:

  • O conexiune creată din magazinul de aplicații necesită o permisiune explicită. Un agent primește instrumentul doar când o permisiune îl numește, iar permisiunea se verifică față de amprenta actuală a instrumentului, deci un instrument modificat cere o decizie nouă.
  • În rest, instrumentul este activat implicit pentru agenții aflați în raza domeniului său, iar agenții individuali se dezactivează din panoul de detalii al instrumentului.

O permisiune numește exact un singur beneficiar — un rol sau un agent, niciodată amândouă — iar o interdicție are prioritate față de o permisiune. Pentru o conexiune la domeniul user, întrebarea nu se pune: orice agent cu care vorbește acea persoană o poate folosi, iar singurul lucru care îl împiedică este o dezactivare explicită.

Un lucru ușor de căutat în locul greșit: /settings/accounts din administrare sunt conturile conectate — Slack, căsuțele de e-mail, abonamentele la modele și legăturile OAuth către Linear, Jira, GitHub și Trello, care susțin tablele externe. Conectorii MCP se află la /admin/apps, iar instrumentele pe care le produc se află la /admin/advanced/tools.

Porți de aprobare pentru apelurile de instrumente

Unele apeluri de instrumente se opresc și așteaptă o persoană. Când se întâmplă asta, rularea nu eșuează: invocarea este înghețată — numele instrumentului, o amprentă (hash) a argumentelor și suficientă stare pentru reluare — iar o cerere de aprobare apare la /api/approvals, pentru ca cineva să răspundă. Reluarea este idempotentă, deci o aprobare duplicată nu poate rula apelul de două ori. Persoana care decide vede un rezumat limitat al cererii; argumentele exacte și rularea de origine rămân pe server.

Există două porți distincte, iar ele se configurează diferit.

  • Porțile structurale sunt o proprietate a anumitor instrumente încorporate și nu se pot configura. Trimiterea e-mailului din propria căsuță a unui agent sau dintr-una conectată, trimiterea unei ciorne Gmail, deconectarea unui cont de e-mail, crearea, modificarea sau anularea unui eveniment din calendar și o acțiune în browser care ar scrie pe un site la care browserul nu este autentificat sunt toate supuse acestei porți. Au o fereastră de 24 de ore pentru a răspunde, în loc de cele 30 de minute obișnuite.
  • Porțile de politică vin din regulile de politică de pe perechea tool/invoke. O regulă care permite o invocare poate avea requiresApproval; fără o aprobare verificată, evaluarea returnează APPROVAL_REQUIRED în loc să ruleze. Regulile se evaluează cu interdicțiile întâi și sunt ordonate după domeniu.

Nu am putut confirma ce ecran de administrare scrie o regulă requiresApproval pentru un instrument. Modelul de date și aplicarea există, dar ecranul care scrie o astfel de regulă nu este confirmat.


Conectarea unui agent din exterior la Nessie

Cealaltă direcție. Nessie rulează propriul server MCP la POST /mcp, prin HTTP transmisibil în flux, și este fără stare: fiecare apel își poartă propriul credențial și își stabilește singur actorul, deci nu există o sesiune pe care s-o fixați de o anumită replică. ChatGPT, Claude, Claude Code, Codex sau un agent de pe laptopul cuiva pot lucra cu tablele, documentele și clienții dvs. din aplicația pe care persoana o folosește deja. Pașii pentru fiecare aplicație sunt în Conectarea ChatGPT, Claude și a altor aplicații AI.

Ce este de fapt un credențial asociat

Aceasta este partea care trebuie înțeleasă corect, pentru că modelul mental evident — o cheie API cu permisiuni atașate — este cel greșit.

Un credențial asociat nu este niciodată o identitate. El desemnează un om, iar fiecare apel de instrument rulează ca acel om, prin aceleași funcții de serviciu pe care le apelează rutele HTTP. Un agent nu poate ajunge deci la nimic la care persoana care i l-a împrumutat nu ar putea ajunge făcând clic. Domeniile doar restrâng această sferă; nu există niciun domeniu care s-o lărgească.

Valabilitatea este reverificată la fiecare apel, niciodată preluată din token: revocarea, expirarea, versiunea tokenului utilizatorului, apartenența la organizație și rolul actual. O retrogradare, o dezactivare sau un „deconectați-mă de peste tot” se aplică la următorul apel al agentului, nu la expirarea credențialului.

Tokenul este opac, are prefixul nag1_, este stocat doar ca hash SHA-256 și este valabil 90 de zile. Este refuzat cu un 403 specific pe orice rută în afara endpoint-ului MCP. Să-l prezinți în altă parte nu este un succes parțial care să fie reîncercat — tocmai asta face ca „i-am împrumutat unui agent tablele mele” să nu însemne „i-am împrumutat contul meu”.

Două moduri de conectare

O aplicație care poate deschide un browser — ChatGPT, Claude, Claude Code, Codex — folosește profilul de autorizare MCP: OAuth 2.1 cu PKCE (S256), înregistrare dinamică a clientului la POST /mcp/oauth/register, tokenuri de reîmprospătare și indicatori de resursă. Aplicația trimite persoana la /settings/security, unde aceasta alege ce poate face aplicația și la care dintre echipele sale ajunge, iar browserul se întoarce în aplicație cu un cod. Tokenul de acces este valabil o oră și se reîmprospătează; conexiunea rămâne activă 90 de zile.

Un agent din linia de comandă nu controlează niciun browser și nu poate găzdui un URL de callback, așa că se asociază prin acordarea de autorizare pentru dispozitiv (RFC 8628).

  1. Agentul apelează POST /mcp/auth/device cu un clientName și cu scopes pentru domeniile de acces dorite. Primește înapoi un device_code, un user_code scurt, un verification_uri și un verification_uri_complete cu codul deja în interogare, plus intervalul de sondare interval și un expires_in. Afișează URI-ul.
  2. Persoana deschide acel URI în timp ce este autentificată, la /settings/security. Vede ce agent cere acces, în al cui cont ar lucra, până când și exact ce ar putea face — și alege Permite sau Nu permite.
  3. Agentul sondează POST /mcp/auth/token cu deviceCode, respectând authorization_pending, slow_down, expired_token și access_denied. La succes primește { access_token, token_type, scope, expires_at }.

În ambele cazuri există un pas făcut de un om, pentru că un credențial moștenește drepturile unei persoane, deci o persoană trebuie să aleagă să le împrumute. Nimic aici nu creează acces din nimic. Conectarea poate fi oprită și pentru întreaga organizație, la /admin/security?tab=programs, iar acest răspuns este verificat atunci când cineva permite o conexiune, nu când un agent pornește una.

Descoperire

Un POST /mcp neautentificat răspunde cu 401 și cu un antet WWW-Authenticate care indică spre /.well-known/oauth-protected-resource (RFC 9728), care numește această origine drept server de autorizare; metadatele sale de la /.well-known/oauth-authorization-server (RFC 8414) numesc endpoint-urile de înregistrare, autorizare, token și revocare. Acest lanț este tot ce are nevoie un client odată ce are adresa. Pentru a găsi adresa unui site, un agent citește /.well-known/ai-catalog.json, care indică Server Card-ul de la /mcp/server-card (MCP Server Cards, SEP-2127). Endpoint-urile acordării pentru dispozitiv sunt listate în documentul de resursă protejată, sub x-nessie-device-authorization.

O implementare poate răspunde și la o a doua adresă, mai scurtă: adresa https://nessie.works/mcp a Nessie Cloud este trimisă prin proxy către API, iar un client care a venit pe această cale primește înapoi această adresă ca resursă, în timp ce autentificarea rămâne la API. O redirecționare nu ar ajuta, pentru că un client refuză metadatele care numesc o altă adresă decât cea cu care a fost pornit. Operatorul trece aceste origini în NESSIE_MCP_ALIAS_ORIGINS.

Domenii și instrumente

Domeniile sunt intenționat grosiere — un domeniu pe care nimeni nu îl poate explica este unul pe care nimeni nu îl setează corect. O conexiune care ajunge la mai multe echipe le listează cu nessie_team_list, iar fiecare alt instrument numește apoi teamId.

DomeniuAjunge la
boards_readProiecte, table și tichete, cu comentariile, fișierele, etichetele, tipurile și listele de verificare ale acestora
boards_writeCrearea, editarea și mutarea tichetelor și tot ce ține de ele
documents_readSpații de cunoștințe, documente și foi de calcul
documents_writeRedactarea și editarea lor și cererea de publicare a unui document
clients_readCăsuța de mesaje a clienților, contactele, listele de contacte, newsletterele și formularele de contact
clients_writeNotițe, preluarea, atribuirea și închiderea conversațiilor, listele de contacte și pregătirea newsletterelor
clients_sendOrice primește un client: un răspuns, un prim e-mail sau un mesaj text. Nu se bifează niciodată decât dacă persoana o face
website_toolsInstrumentele de pagină ale site-ului înregistrat al echipei dvs., operate într-un browser găzduit pe care aveți voie să-l controlați. Nu este niciodată bifat, decât dacă persoana îl bifează

Un apel fără domeniul necesar revine ca rezultat al instrumentului, care numește domeniul de cerut, nu ca eroare de transport. Astfel modelul poate raporta ceva util, în loc să reîncerce la nesfârșit fără rezultat.

Pentru sistemele externe de urmărire a sarcinilor nu există o familie separată de instrumente. O tablă este tablă indiferent dacă sarcinile ei provin din Nessie sau sunt oglindite din Linear, Jira, GitHub sau Trello, deci un singur set de instrumente le acoperă pe ambele, iar fiecare sarcină spune din ce sistem provine. Diferența apare la scriere: o tablă oglindită dintr-o sursă doar pentru citire refuză o scriere, explicând agentului în cuvinte că celălalt sistem deține tichetul și că a reîncerca nu ajută.

Publicarea unui document este o aprobare, niciodată un domeniu

Agenții redactează; o persoană publică. Regula aceasta este aplicată pentru agenții proprii Nessie prin refuzul total al unui agent ca actor — dar un credențial asociat se rezolvă ca o persoană, deci refuzul nu îl poate prinde. Poarta trebuie pusă în altă parte.

Așadar nessie_doc_publish nu publică. Deschide o aprobare knowledge.page.publish — aceeași pe care o deschide un agent Nessie — și returnează { status: "awaiting_approval", approvalId }. Sondarea returnează aceeași cerere, în loc să deschidă una nouă. Decizia se ia la fel ca orice altă decizie de publicare în Nessie: de o persoană, despre documentul respectiv, în momentul în care acesta există.

Intenționat, nu există domeniu de publicare. A existat unul — o bifă la asociere, prin care agentul putea publica — și a fost retras. Decidea o dată, pentru nouăzeci de zile, o întrebare pe care produsul o pune pentru fiecare document, și o decidea înainte ca documentul să existe, astfel încât persoana care bifa nu putea ști ce acceptă să publice. nessie_doc_publish are nevoie de documents_write, pentru că a cere nu este gratuit — citește pagina și pune o decizie în fața cuiva — dar acest domeniu nu acordă nimic în privința publicării.

Pe cerere se înregistrează două identități, iar distincția este esențială. Solicitantul este credențialul, niciodată omul în numele căruia acționează. Aprobatorul necesar este acel om, și doar el poate răspunde. Dacă cererea l-ar fi numit pe om drept solicitant, regula care oprește aprobarea propriei cereri ar descalifica singura persoană care are dreptul să decidă.

Vizualizarea și revocarea credențialelor asociate

Agenții asociați ai unei persoane sunt listați la /settings/security, cu o etichetă și un buton de revocare pentru fiecare. Un proprietar sau un administrator al organizației vede fiecare credențial din organizație la /admin/security?tab=programs — cum se numește fiecare, ce poate face, dacă este activ și al cărui cont împrumută — și le poate revoca pe oricare. Lista personală rămâne doar pentru titular: o listă de credențiale active este o listă de puncte de sprijin, nu o simplă lectură.

Scrierile făcute printr-un credențial sunt atribuite omului care îl împrumută și poartă identificatorul credențialului în metadatele de audit, astfel încât jurnalul poate deosebi modificarea proprie a unei persoane de cea făcută de agentul ei în locul ei.

Ce nu este încă disponibil

Spunem explicit, pentru că diferența dintre capacitățile unui protocol și cele ale unui anumit server este locul unde documentația greșește de obicei:

  • Chatul, canalele, rulările și gestionarea agenților nu sunt expuse prin MCP.
  • Un program pregătește un newsletter, iar o persoană îl trimite: nimic nu trimite, nu programează și nu șterge o campanie, iar e-mailul propriu-zis se scrie în editorul de design.
  • Legarea și unirea contactelor, modificările schemei CRM, importurile și configurarea canalelor rămân la o persoană, în browser.
  • Comutatorul de conectare este o setare la nivel de organizație. Suprascrierile la nivel de echipă și cele personale nu pot fi setate astăzi de o echipă sau de o persoană.