nessie

MCP og tilkoblede verktøy

Koble Nessie til MCP-servere, og par en ekstern agent slik at den kan jobbe i Nessie som deg.

Hva MCP er

Model Context Protocol er en liten, åpen konvensjon for å beskrive verktøy til en språkmodell og kalle dem. En server oppgir hva den kan gjøre – en liste over navngitte verktøy, hver med et JSON-skjema for argumentene – og en klient finner listen og kaller verktøyene på modellens vegne. Den er bevisst kjedelig: gevinsten er at én integrasjon, skrevet én gang, fungerer med hver modell og hvert produkt som snakker den.

Nessie står på begge sider av den

Det er verdt å være presis om dette, fordi de to halvdelene er lette å forveksle, og de ligger på bevisst forskjellige stier.

  • Utgående. Nessie er en MCP-klient. Du kobler til servere som noen andre kjører, og verktøyene de viser, blir verktøy agentene dine kan bruke. Administrasjonsflaten er /api/mcp/*.
  • Innkommende. Nessie er også en MCP-server. En agent som kjører et annet sted – Claude Code eller Codex på en utviklers maskin – kan kalle inn i tavlene og dokumentene dine. Det endepunktet er POST /mcp, utenfor /api med vilje, så de to motsatte betydningene ikke står ett stisegment fra hverandre.

Koble en MCP-server til Nessie

Katalogoppføringer og instanser

Det finnes to objekter, og skillet betyr noe:

  • En katalogoppføring er definisjonen av en server – navnet, transporten og hvordan den autentiserer. Den skrives én gang og kan deles.
  • En instans er den oppføringen installert på ett nivå, med legitimasjonen som når den. Å installere samme oppføring for to forskjellige team gir to instanser.

De fleste rører aldri noen av dem direkte. Inngangen i admin er app-butikken på /admin/apps: den blar i den samme katalogen med tilkoblingsdetaljene skjult, og Connect på en oppføring oppretter en instans. Operatører som vil ha de rå objektene, bruker /api/mcp/catalog og /api/mcp/instances.

Hvis en server ikke står i katalogen ennå, tar POST /api/mcp/discover en URL og leser tilbake det serveren sier om seg selv, og POST /api/mcp/library/import henter en oppføring fra det offentlige server-biblioteket. En første tilkobling kan også lages direkte fra en URL med POST /api/apps/custom.

Transporter

Nessie kobler seg til eksterne servere over HTTP. To transporter fungerer: streambar HTTP (den nåværende MCP-transporten) og den eldre HTTP+SSE-transporten. Velg http med mindre serveren bare tilbyr sse.

Transporten stdio avvises for koblinger du selv har laget. stdio betyr at MCP-serveren er en lokal prosess som verten starter, og å kjøre vilkårlige programmer inne i Nessie-serveren er ikke noe en operatør meningsfullt kan gjennomgå. Servere som bare publiseres som en pakke som skal kjøres lokalt, fjernes fra bibliotekimporter av samme grunn. En ws-verdi finnes i protokollfeltet og er ikke implementert – en tilkobling som bruker den, feiler når Nessie prøver å åpne den.

Hvordan Nessie autentiserer mot serveren

En katalogoppføring oppgir én av fem metoder: none, api_key, bearer, basic eller oauth2.

api_key er header-injeksjon, og oppføringen sier hvilken header og hvilket prefiks som brukes. Dermed er Authorization: Bearer …, X-API-Key: … og X-Auth-Token: Token … alle samme metode, konfigurert på ulike måter. Nøkkelen limes inn én gang med POST /api/mcp/instances/:instanceId/secret og kan aldri leses igjen; ingen respons fra instans-rutene bærer en legitimasjonsreferanse.

oauth2 kommer i to former. En server med faste endepunkter har dem i katalogoppføringen. En server som publiserer standard metadata, trenger ingenting konfigurert: Nessie leser dokumentene for beskyttet ressurs og autorisasjonsserver, og lager seg selv en klient gjennom dynamisk klientregistrering eller gjennom klient-ID-metadatadokumentet som den publiserer på sin egen well-known-sti. Uansett er håndtrykket POST /api/mcp/instances/:instanceId/oauth/start, etterfulgt av leverandørens omdirigering tilbake til GET /api/mcp/oauth/callback. State-tokenet er engangsbruk, lagres i databasen og er gyldig i ti minutter, og PKCE brukes alltid.

Én regel overrasker folk: selv en organisasjonseier kan ikke knytte et personlig eller OAuth-token til noen andre. En delt legitimasjon er bare mulig for en API-nøkkel-oppføring som er validert som en slik; alt annet avvises. En legitimasjon som tilhører en person, forblir en legitimasjon som tilhører den personen.

Hvor legitimasjonen ligger

Koblingshemmeligheter og OAuth-tokenpakker lagres i din egen Postgres-database, i formålsbundne AES-256-GCM-konvolutter under installasjonens uavhengig versjonerte krypteringsnøkkelring, og adresseres bare med opake secret_*-referanser. De ligger bevisst ikke i den eksterne hemmelighetsvaulten som holder andre Nessie-hemmeligheter. En produksjonsinstallasjon som ikke har konfigurert den krypterte lagringen, nekter å starte i stedet for å fullføre et OAuth-håndtrykk som den ellers ville mistet.

Hvor en tilkobling når

En instans er installert på ett av seks nivåer: system, organization, project, team, channel eller user. Nivået er et hardt tak på hvilke kjøringer som kan bruke den, og tildelinger kan bare begrense det videre, aldri utvide det. Dermed kan en kobling du installerte med din egen konto, ikke stille og rolig bli en organisasjonsbred kobling.

Hvem som kan installere hvor, følger samme form: organisasjonens eiere og administratorer administrerer installasjoner på alle nivåer; alle andre installerer koblinger på sitt eget user-nivå, og kan se delte installasjoner de har rett til å nå.

Gi en agent et verktøy

Å koble til en server gir ikke verktøyene dens til noen. Når en instans installeres, prober Nessie den og projiserer hvert verktøy den annonserer inn i verktøyregisteret. Det er listen admin viser på /admin/advanced/tools – innebygde verktøy, MCP-verktøy, pakkeverktøy og executor-verktøy i én liste, hver med kilde og status.

Et projisert verktøy har én av tre statuser: active, pending_review eller disabled. Verktøy fra en delt installasjon kommer som pending_review og må godkjennes på den siden før en agent ser dem. Verktøy fra din egen user-installasjon er aktive med en gang, fordi den eneste personen som blir eksponert, er deg. Gjennomgangen er løpende, ikke en engangsseremoni: hvis en senere probe finner at et verktøys beskrivelse eller skjema har endret seg, går verktøyet tilbake til pending_review og tilbys ikke før noen har sett på endringen.

Hvilke agenter som da kan kalle et aktivt verktøy, avgjøres på to måter:

  • En tilkobling opprettet gjennom app-butikken krever en eksplisitt tildeling. En agent får verktøyet bare når en tildeling nevner det, og tildelingen sjekkes mot verktøyets nåværende fingeravtrykk, så et endret verktøy trenger en ny beslutning.
  • Ellers er verktøyet på som standard for agentene innenfor rekkevidde av nivået, og enkeltagenter slås av på verktøyets detaljpanel.

En tildeling peker på nøyaktig én part – en rolle eller en agent, aldri begge – og et avslag går foran en tillatelse. For en tilkobling på user-nivå oppstår ikke spørsmålet: enhver agent som personen snakker med, kan bruke den, og et eksplisitt avslag er det eneste som holder den tilbake.

Én ting er lett å lete etter på feil sted: /settings/accounts i admin er tilkoblede kontoer – Slack, e-postbokser, modellabonnementer og OAuth-koblingene til Linear, Jira, GitHub og Trello som ligger bak eksterne tavler. MCP-koblinger finnes på /admin/apps, og verktøyene de lager, finnes på /admin/advanced/tools.

Godkjenningsporter på verktøykall

Noen verktøykall stopper og venter på en person. Når ett gjør det, feiler ikke kjøringen: kallet fryses – verktøynavnet, en hash av argumentene og nok tilstand til å gjenoppta – og en godkjenningsforespørsel dukker opp i /api/approvals for noen å svare på. Å gjenoppta er idempotent, så en duplisert godkjenning kan ikke kjøre kallet to ganger. Personen som avgjør, ser et avgrenset sammendrag av hva som spørres om. De nøyaktige argumentene og den opprinnelige kjøringen forblir på serveren.

Det finnes to ulike porter, og de konfigureres forskjellig.

  • Strukturelle porter er en egenskap ved visse innebygde verktøy og kan ikke konfigureres. Å sende e-post fra en agents egen postboks eller en tilkoblet en, sende et Gmail-utkast, koble fra en e-postkonto, opprette, endre eller avlyse en kalenderhendelse, og en nettleserhandling som ville skrive til et nettsted nettleseren ikke er innlogget på, er alle styrt slik. De får et 24-timers vindu til å svare på, i stedet for de vanlige 30 minuttene.
  • Retningslinjeporter kommer fra retningslinjeregler på paret tool/invoke. En regel som tillater et kall, kan ha requiresApproval. Uten en verifisert godkjenning returnerer vurderingen APPROVAL_REQUIRED i stedet for å kjøre. Regler vurderes avslag først og sorteres etter nivå.

Hvilket admin-skjermbilde som skriver en requiresApproval-retningslinjeregel for et verktøy, har vi ikke kunnet bekrefte. Datamodellen og håndhevelsen er begge på plass.


Koble en ekstern agent til Nessie

Den andre retningen. Nessie kjører en egen MCP-server på POST /mcp, over streambar HTTP, og den er tilstandsløs: hvert kall bærer sin egen legitimasjon og finner sin egen aktør, så det finnes ingen økt å binde en klient til én replika. ChatGPT, Claude, Claude Code, Codex eller en agent på noens bærbare kan jobbe med tavlene, dokumentene og kundene dine fra innsiden av verktøyet personen allerede bruker. Trinnvis veiledning for hver app finner du under Koble til ChatGPT, Claude og andre AI-apper.

Hva en paret legitimasjon egentlig er

Dette er delen du må få riktig, fordi den åpenbare mentale modellen – en API-nøkkel med tillatelser festet på – er den gale.

En paret legitimasjon er aldri en identitet. Den navngir et menneske, og hvert verktøykall kjører som dette mennesket, mot de samme tjenestefunksjonene som HTTP-rutene kaller. En agent kan derfor ikke nå noe personen som lånte den ut, ikke kunne nå ved å klikke seg frem til selv. Omfang begrenser bare rekkevidden. Det finnes ikke noe omfang som utvider den.

Om den er i live, leses på nytt for hvert eneste kall og hentes aldri fra tokenet: tilbakekalling, utløp, brukerens tokenversjon, organisasjonsmedlemskapet og den nåværende rollen. En nedgradering, en deaktivering eller et «logg meg ut overalt» treffer agentens neste kall, i stedet for først når legitimasjonen utløper.

Tokenet er opakt, med prefikset nag1_, lagres bare som SHA-256-hash og lever i 90 dager. Det avvises med en spesifikk 403 på alle ruter utenfor MCP-endepunktet. Å presentere det andre steder er ikke en delvis suksess å prøve igjen, og det er det som gjør at «jeg lånte en agent tavlene mine» ikke betyr «jeg lånte den kontoen min».

To måter å koble til på

En app som kan åpne en nettleser – ChatGPT, Claude, Claude Code, Codex – bruker MCP-autorisasjonsprofilen: OAuth 2.1 med PKCE (S256), dynamisk klientregistrering på POST /mcp/oauth/register, fornyelsestokener og ressursindikatorer. Appen sender personen til /settings/security. Personen velger hva appen får gjøre og hvilke av teamene deres den når, og nettleseren går tilbake til appen med en kode. Tilgangstokenet varer én time og fornyes; tilkoblingen varer 90 dager.

En kommandolinjeagent kontrollerer ingen nettleser og kan ikke være vert for en callback-URL, så den kobler seg til med enhetsautorisasjonsgrant (RFC 8628) i stedet.

  1. Agenten kaller POST /mcp/auth/device med en clientName og scopes den vil ha. Den får tilbake en device_code, en kort user_code, en verification_uri og en verification_uri_complete med koden allerede i spørringen, pluss poll-interval og en expires_in. Den skriver ut URI-en.
  2. En person åpner den URI-en mens de er innlogget, på /settings/security. De ser hvilken agent som spør, hvis konto den ville virke som, til når, og nøyaktig hva den ville kunne gjøre. Så velger de Tillat eller Ikke tillat.
  3. Agenten poller POST /mcp/auth/token med deviceCode og respekterer authorization_pending, slow_down, expired_token og access_denied. Ved suksess får den { access_token, token_type, scope, expires_at }.

Uansett finnes det et menneskelig steg, fordi legitimasjonen arver en persons rettigheter, så en person må velge å låne dem ut. Ingenting her skaper tilgang fra ingenting. Tilkobling kan også slås av for en hel organisasjon på /admin/security?tab=programs, og det svaret sjekkes når noen tillater en tilkobling, ikke når en agent starter en.

Oppdagelse

En uautentisert POST /mcp svarer 401 med en WWW-Authenticate-header som peker til /.well-known/oauth-protected-resource (RFC 9728), som oppgir denne originen som autorisasjonsserver. Metadataene på /.well-known/oauth-authorization-server (RFC 8414) navngir registrerings-, autorisasjons-, token- og tilbakekallingsendepunktene. Den kjeden er alt en klient trenger når den har adressen. For å finne adressen fra et domene leser en agent /.well-known/ai-catalog.json, som peker til Server Card på /mcp/server-card (MCP Server Cards, SEP-2127). Enhetsgrantens endepunkter står oppført i dokumentet for beskyttet ressurs under x-nessie-device-authorization.

En installasjon kan også svare på en annen, kortere adresse. Nessie Clouds https://nessie.works/mcp proxyes til API-et, og en klient som kom inn på den måten, får den adressen tilbake som ressurs. Innlogging skjer fortsatt på API-et. En omdirigering ville ikke fungert, fordi en klient avviser metadata som nevner en annen adresse enn den den fikk. Operatøren lister disse originene i NESSIE_MCP_ALIAS_ORIGINS.

Omfang og verktøy

Omfangene er grove med vilje – et omfang ingen kan forklare, blir ingen som setter det riktig. En tilkobling som når mer enn ett team, lister dem med nessie_team_list, og hvert annet verktøy nevner da sin teamId.

OmfangRekkevidde
boards_readProsjekter, tavler og oppgaver, med kommentarer, filer, etiketter, typer og sjekklister
boards_writeOppretting, redigering og flytting av oppgaver, og alt som ligger på dem
documents_readKunnskapsområder, dokumenter og regneark
documents_writeUtkast og redigering av dem, og å be om å publisere et dokument
clients_readKundeinnboksen, kontakter, kontaktlister, nyhetsbrev og kontaktskjemaer
clients_writeNotater, å ta over, tildele og lukke samtaler, kontaktlister og klargjøring av nyhetsbrev
clients_sendAlt en kunde mottar: et svar, en første e-post eller en tekstmelding. Krysses aldri av med mindre personen krysser det av selv
website_toolsSideverktøyene på teamets registrerte nettsted, betjent i en nettleser i skyen du kan ta kontrollen over. Krysses aldri av med mindre personen krysser det av selv

Et kall uten omfanget det trenger, kommer tilbake som et verktøyresultat som nevner omfanget å be om, ikke som en transportfeil. Dermed kan modellen rapportere noe nyttig i stedet for å prøve på nytt inn i en mur.

Det finnes ingen egen familie av verktøy for eksterne oppgavesystemer. En tavle er en tavle enten oppgavene stammer fra Nessie eller speiles fra Linear, Jira, GitHub eller Trello, så ett verktøysett dekker begge, og hver oppgave sier hvilken den er. Det som skiller, er skriving: en tavle speilet fra en skrivebeskyttet kilde avviser en skriving med ord. Den forteller agenten at det andre systemet eier oppgaven, og at et nytt forsøk ikke hjelper.

Å publisere et dokument er en godkjenning, aldri et omfang

Agenter utarbeider; en person publiserer. Den regelen håndheves for Nessies egne agenter ved å avvise en agentaktør rett ut. En paret legitimasjon løses imidlertid som et menneske, så den avvisningen kan ikke fange den. Porten må derfor ligge et annet sted.

Så nessie_doc_publish publiserer ikke. Den åpner en knowledge.page.publish-godkjenning – den samme som en Nessie-agent åpner – og returnerer { status: "awaiting_approval", approvalId }. Å spørre igjen returnerer den samme forespørselen i stedet for å åpne en ny. Beslutningen tas slik som hver annen publiseringsbeslutning i Nessie tas: av en person, om det dokumentet, i det øyeblikket det finnes.

Det finnes bevisst ikke noe publiseringsomfang. Det var ett. Det var en avkrysning ved paringen som sa at denne agenten får publisere, og det ble trukket tilbake. Det avgjorde en gang for alle, i nitti dager, et spørsmål produktet stiller per dokument alle andre steder. Og det avgjorde det før dokumentet fantes, så personen som krysset av, ikke kunne vite hva de samtykket til å publisere. nessie_doc_publish krever documents_write, fordi å spørre ikke er gratis: den leser siden og legger en beslutning foran noen. Men det omfanget gir ingenting om publisering.

To identiteter registreres på forespørselen, og skillet er bærende. Forespørselens avsender er legitimasjonen, aldri mennesket den handler som. Den påkrevde godkjenneren er det mennesket, og bare de kan svare. Hvis forespørselen hadde navngitt personen som avsender, ville regelen som stopper noen fra å godkjenne sin egen forespørsel, diskvalifisert den ene personen som har rett til å avgjøre.

Se og tilbakekall det som er paret

En persons egne parede agenter listes på /settings/security, med en etikett og en tilbakekallingskontroll for hver. En eier eller organisasjonsadministrator ser hver legitimasjon i organisasjonen på /admin/security?tab=programs: hva hver heter, hva den får gjøre, om den er i live, og hvis konto den låner. Vedkommende kan tilbakekalle en hvilken som helst av dem. Den personlige listen er bare for en selv. En liste over levende legitimasjoner er en liste over fotfester, ikke generell lesing.

Skrivinger gjort gjennom en legitimasjon tilskrives mennesket som lånte den, og bærer legitimasjonens id i revisjonsmetadataene. Dermed kan loggen skille en persons egen redigering fra en endring agenten gjorde for dem.

Det som ikke er tilgjengelig ennå

Vi er eksplisitte her, fordi gapet mellom en protokolls evner og en gitt servers evner er der dokumentasjon vanligvis svikter:

  • Chat, kanaler, kjøringer og agenthåndtering eksponeres ikke over MCP.
  • Et program klargjør et nyhetsbrev, og en person sender det. Ingenting sender, planlegger eller sletter en kampanje, og selve e-posten skrives i designeren.
  • Sammenslåing og kobling av kontakter, endringer i CRM-skjemaet, import og kanaloppsett blir hos en person i nettleseren.
  • Bryteren for tilkobling er en innstilling på organisasjonsnivå. Overstyringer på team- og personnivå er ikke noe et team eller en person kan sette i dag.