MCP och kopplade verktyg
Koppla Nessie till MCP-servrar och para ihop en extern agent så att den kan arbeta i Nessie åt dig.
Vad MCP är
Model Context Protocol är en liten, öppen konvention för att beskriva verktyg för en språkmodell och anropa dem. En server deklarerar vad den kan göra – en lista över namngivna verktyg, var och ett med ett JSON-schema för sina argument – och en klient upptäcker listan och anropar verktygen på modellens uppdrag. Den är medvetet tråkig. Poängen är att en integration som skrivs en gång fungerar med varje modell och varje produkt som talar protokollet.
Nessie står på båda sidorna
Det är värt att vara exakt med det, eftersom de två halvorna är lätta att blanda ihop och de ligger på medvetet olika sökvägar.
- Utgående. Nessie är en MCP-klient. Du kopplar servrar som någon annan kör, och verktygen de exponerar blir verktyg dina agenter kan använda. Hanteringsytan är
/api/mcp/*. - Inkommande. Nessie är också en MCP-server. En agent som körs någon annanstans – Claude Code eller Codex på en utvecklares dator – kan anropa in i dina tavlor och dokument. Den endpointen är
POST /mcp, medvetet utanför/api, så att de två motsatta betydelserna inte bara ligger ett sökvägssegment ifrån varandra.
Koppla en MCP-server till Nessie
Katalogposter och instanser
Det finns två objekt, och skillnaden spelar roll:
- En katalogpost är definitionen av en server – dess namn, dess transport och hur den autentiserar. Den skrivs en gång och kan delas.
- En instans är den posten installerad på en omfattning, med de inloggningsuppgifter som når den. Installerar du samma post för två olika team får du två instanser.
De flesta rör aldrig något av dem direkt. Ingången i admin är appbutiken på /admin/apps: den bläddrar i samma katalog med anslutningsuppgifterna dolda, och Connect på en listning skapar en instans. Operatörer som vill ha de råa objekten använder /api/mcp/catalog och /api/mcp/instances.
Om en server inte finns i katalogen ännu tar POST /api/mcp/discover en URL och läser tillbaka vad servern säger om sig själv, och POST /api/mcp/library/import hämtar en post från det publika serverbiblioteket. En första koppling kan också skapas direkt från en URL med POST /api/apps/custom.
Transporter
Nessie ansluter till fjärrservrar över HTTP. Två transporter fungerar: strömmande HTTP (den nuvarande MCP-transporten) och den äldre HTTP+SSE-transporten. Välj http om inte servern bara erbjuder sse.
Transporten stdio nekas för kopplingar du själv skriver. stdio innebär att MCP-servern är en lokal process som värden startar, och att köra godtyckliga program inne i Nessie-servern är inget en operatör meningsfullt kan granska. Servrar som bara publiceras som ett paket för att köras lokalt tas bort från biblioteksimporter av samma skäl. Ett värde ws finns i protokollfältet men är inte implementerat – en koppling som använder det misslyckas när Nessie försöker öppna den.
Så autentiserar Nessie mot servern
En katalogpost deklarerar en av fem metoder: none, api_key, bearer, basic eller oauth2.
api_key sätter en header, och posten anger vilken header och vilket prefix som gäller. Så Authorization: Bearer …, X-API-Key: … och X-Auth-Token: Token … är alla samma metod, konfigurerad på olika sätt. Nyckeln klistras in en gång med POST /api/mcp/instances/:instanceId/secret och kan aldrig läsas igen. Inget svar från instansrutterna bär en referens till autentiseringsuppgiften.
oauth2 finns i två former. En server med fasta endpoints har dem i sin katalogpost. En server som publicerar standardmetadata behöver ingenting konfigurerat alls: Nessie läser dokumenten för skyddad resurs och auktoriseringsserver, och skapar själv en klient genom dynamisk klientregistrering eller genom det klient-ID-metadatadokument den publicerar på sin egen well-known-sökväg. Hur som helst är handskakningen POST /api/mcp/instances/:instanceId/oauth/start, följt av leverantörens omdirigering tillbaka till GET /api/mcp/oauth/callback. State-token är engångs, lagras i databasen och gäller i tio minuter, och PKCE används alltid.
En regel överraskar folk: inte ens en organisationsägare kan koppla en personlig eller OAuth-token till någon annan. En delad autentiseringsuppgift är bara möjlig för en API-nyckelpost som validerats som en sådan, och allt annat nekas. En autentiseringsuppgift som tillhör en person förblir en autentiseringsuppgift som tillhör den personen.
Var inloggningsuppgifterna finns
Hemligheter för kopplingar och OAuth-tokenpaket lagras i din egen Postgres-databas, i ändamålsbundna AES-256-GCM-kuvert under driftsättningens oberoende versionerade krypteringsnyckelring. De adresseras bara med ogenomskinliga secret_*-referenser. De ligger medvetet inte i det externa hemlighetsvalv som håller andra Nessie-hemligheter. En produktionsdriftsättning som inte konfigurerat den krypterade lagringen vägrar starta i stället för att slutföra en OAuth-handskakning som den sedan skulle tappa bort.
Var en koppling når
En instans installeras på en av sex omfattningar: system, organization, project, team, channel eller user. Omfattningen är ett hårt tak för vilka körningar som kan använda den, och tilldelningar kan bara begränsa den ytterligare, aldrig vidga den. En koppling du installerat med ditt eget konto kan alltså inte i tysthet bli en organisationsomfattande koppling.
Vem som får installera var följer samma form: organisationens ägare och administratörer hanterar installationer på alla omfattningar, och alla andra installerar kopplingar på sin egen user-omfattning och kan se de delade installationer de har rätt att nå.
Ge en agent ett verktyg
Att koppla en server ger inte dess verktyg till någon. När en instans installeras avsöker Nessie den och projicerar varje verktyg den annonserar in i verktygsregistret. Det är vad admin visar på /admin/advanced/tools: inbyggda verktyg, MCP-verktyg, bundle-verktyg och executor-verktyg i en lista, var och ett med sin källa och status.
Ett projicerat verktyg har en av tre statusar: active, pending_review eller disabled. Verktyg från en delad installation kommer som pending_review och måste godkännas på den sidan innan en agent ser dem. Verktyg från din egen user-installation är aktiva direkt, eftersom den enda person som exponeras är du. Granskningen är återkommande och inte en engångsceremoni: om en senare avsökning visar att ett verktygs beskrivning eller schema har ändrats, går det tillbaka till pending_review och erbjuds inte förrän någon tittar på ändringen.
Vilka agenter som sedan får anropa ett aktivt verktyg avgörs på ett av två sätt:
- En koppling skapad via appbutiken kräver en uttrycklig tilldelning. En agent får verktyget bara när en tilldelning anger det, och tilldelningen kontrolleras mot verktygets nuvarande fingeravtryck, så ett ändrat verktyg behöver ett nytt beslut.
- Annars är verktyget påslaget som standard för agenterna inom räckhåll för dess omfattning, och enskilda agenter stängs av på verktygets detaljpanel.
En tilldelning anger exakt en part – en roll eller en agent, aldrig båda – och ett nekande beslut väger tyngre än ett tillåtande. För en koppling på user-omfattning uppstår inte frågan: vilken agent personen än pratar med kan använda den, och en uttrycklig avstängning är det enda som håller tillbaka den.
En sak som är lätt att leta efter på fel ställe: /settings/accounts i admin är anslutna konton – Slack, e-postlådor, modellprenumerationer och OAuth-länkarna till Linear, Jira, GitHub och Trello som ligger bakom externa tavlor. MCP-kopplingar finns på /admin/apps, och verktygen de producerar finns på /admin/advanced/tools.
Godkännandespärrar för verktygsanrop
Vissa verktygsanrop stannar och väntar på en person. När ett gör det misslyckas inte körningen. Anropet fryses – verktygsnamnet, en hash av dess argument och tillräckligt med state för att återuppta – och en godkännandebegäran dyker upp i /api/approvals för någon att besvara. Återupptagningen är idempotent, så ett dubbelt godkännande kan inte köra anropet två gånger. Personen som beslutar ser en avgränsad sammanfattning av vad som begärs. De exakta argumenten och den ursprungliga körningen stannar på servern.
Det finns två skilda spärrar, och de konfigureras olika.
- Strukturella spärrar är en egenskap hos vissa inbyggda verktyg och kan inte konfigureras. Att skicka e-post från en agents egna brevlåda eller från en kopplad sådan, skicka ett Gmail-utkast, koppla bort ett e-postkonto, skapa, ändra eller avboka en kalenderhändelse, och en webbläsaråtgärd som skulle skriva till en sajt där webbläsaren inte är inloggad, spärras alla på det här sättet. De får ett 24-timmarsfönster att besvaras i stället för de vanliga 30 minuterna.
- Policyspärrar kommer från policyregler på paret
tool/invoke. En regel som tillåter ett anrop kan bärarequiresApproval, och utan ett verifierat godkännande returnerar utvärderingenAPPROVAL_REQUIREDi stället för att köra. Regler utvärderas nekande först och sorteras efter omfattning.
Vi har inte kunnat bekräfta vilken admin-skärm som skriver en requiresApproval-policyregel för ett verktyg. Datamodellen och tillämpningen finns båda på plats.
Koppla en extern agent till Nessie
Den andra riktningen. Nessie kör en egen MCP-server på POST /mcp, över strömmande HTTP, och den är tillståndslös: varje anrop bär sin egen autentiseringsuppgift och löser sin egen aktör, så det finns ingen session att fästa en klient vid en enda replik. ChatGPT, Claude, Claude Code, Codex eller en agent på någons laptop kan arbeta med dina tavlor, dokument och kunder inifrån det verktyg personen redan använder. Steg för steg för varje app finns under Koppla ChatGPT, Claude och andra AI-appar.
Vad en parkopplad autentiseringsuppgift egentligen är
Det här är delen att få rätt, eftersom den uppenbara mentala modellen – en API-nyckel med behörigheter bifogade – är fel.
En parkopplad autentiseringsuppgift är aldrig en identitet. Den anger en människa, och varje verktygsanrop körs som den människan, mot samma tjänstefunktioner som HTTP-rutterna anropar. En agent kan därför inte nå något som personen som lånade ut den inte kunde nå genom att klicka. Omfattningar begränsar bara räckvidden. Det finns ingen omfattning som vidgar den.
Att åtkomsten fortfarande gäller kontrolleras på nytt vid varje enskilt anrop och litas aldrig på från token: återkallelse, utgång, användarens tokenversion, deras organisationsmedlemskap och deras aktuella roll. En degradering, en avaktivering eller ”logga ut mig överallt” slår igenom vid agentens nästa anrop i stället för vid autentiseringsuppgiftens utgång.
Token är ogenomskinlig, med prefixet nag1_, lagras bara som en SHA-256-hash och lever i 90 dagar. Den avvisas med en specifik 403 på alla rutter utom MCP-endpointen. Att presentera den någon annanstans är inte en delvis lyckad begäran att försöka igen, och det är vad som gör att ”jag lånade en agent mina tavlor” inte betyder ”jag lånade den mitt konto”.
Två sätt att koppla
En app som kan öppna en webbläsare – ChatGPT, Claude, Claude Code, Codex – använder MCP-auktoriseringsprofilen: OAuth 2.1 med PKCE (S256), dynamisk klientregistrering på POST /mcp/oauth/register, uppdateringstoken och resource indicators. Appen skickar personen till /settings/security, de väljer vad den får göra och vilka av deras team den når, och webbläsaren återgår till appen med en kod. Dess åtkomsttoken gäller i en timme och förnyas. Kopplingen lever i 90 dagar.
En kommandoradsagent styr ingen webbläsare och kan inte vara värd för en callback-URL, så den parkopplar i stället med device authorization grant (RFC 8628).
- Agenten anropar
POST /mcp/auth/devicemed ettclientNameoch descopesden vill ha. Den får tillbaka endevice_code, en kortuser_code, enverification_urioch enverification_uri_completemed koden redan i frågesträngen, samt pollintervalletintervaloch enexpires_in. Den skriver ut URI:n. - En person öppnar den URI:n medan de är inloggade, på
/settings/security. De ser vilken agent som frågar, vars konto den skulle arbeta som, till när, och exakt vad den skulle kunna göra – och väljer Tillåt eller Tillåt inte. - Agenten pollar
POST /mcp/auth/tokenmeddeviceCodeoch följerauthorization_pending,slow_down,expired_tokenochaccess_denied. Vid lyckat utfall får den{ access_token, token_type, scope, expires_at }.
I båda fallen finns ett mänskligt steg, eftersom autentiseringsuppgiften ärver en persons behörigheter, så en person måste välja att låna ut dem. Ingenting här skapar åtkomst från ingenting. Att koppla kan också stängas av för en hel organisation på /admin/security?tab=programs, och det svaret kontrolleras när någon godkänner en koppling, inte när en agent startar en.
Upptäckt
En oautentiserad POST /mcp svarar 401 med en WWW-Authenticate-header som pekar på /.well-known/oauth-protected-resource (RFC 9728), som anger den här origin som auktoriseringsserver. Dess metadata på /.well-known/oauth-authorization-server (RFC 8414) anger registrerings-, auktoriserings-, token- och återkallelsesendpoints. Den kedjan är allt en klient behöver när den har adressen. För att hitta adressen från en domän läser en agent /.well-known/ai-catalog.json, som pekar ut Server Card på /mcp/server-card (MCP Server Cards, SEP-2127). Device-flödets endpoints finns listade i dokumentet för skyddad resurs under x-nessie-device-authorization.
En driftsättning kan också svara på en andra, kortare adress. Nessie Clouds https://nessie.works/mcp proxas till API:t, och en klient som kom in så får den adressen tillbaka som resurs, medan inloggningen stannar på API:t. En redirect hade inte fungerat, eftersom en klient vägrar metadata som anger någon annan adress än den den fick. Operatören listar dessa origin i NESSIE_MCP_ALIAS_ORIGINS.
Omfattningar och verktyg
Omfattningarna är medvetet grova. En omfattning ingen kan förklara är en omfattning ingen ställer in rätt. En koppling som når fler än ett team listar dem med nessie_team_list, och varje annat verktyg anger sedan sitt teamId.
| Omfattning | Når |
|---|---|
boards_read | Projekt, tavlor och ärenden, med deras kommentarer, filer, etiketter, typer och checklistor |
boards_write | Skapa, redigera och flytta ärenden, och allt på dem |
documents_read | Kunskapsutrymmen, dokument och kalkylblad |
documents_write | Utkasta och redigera dem, och begära att publicera ett |
clients_read | Kundinkorgen, kontakter, kontaktlistor, nyhetsbrev och kontaktformulär |
clients_write | Anteckningar, att ta över, tilldela och stänga konversationer, kontaktlistor och förbereda nyhetsbrev |
clients_send | Allt en kund får: ett svar, ett första e-postmeddelande eller ett sms. Aldrig bockad om inte personen bockar i det |
website_tools | Sidverktygen på ditt teams registrerade webbplats, som används i en värdbaserad webbläsare som du får styra. Aldrig bockad om inte personen bockar i det |
Ett anrop utan den omfattning det behöver kommer tillbaka som ett verktygsresultat som anger vilken omfattning som ska efterfrågas, inte som ett transportfel. Modellen kan därmed rapportera något användbart i stället för att försöka igen och köra in i en vägg.
Det finns ingen separat familj av verktyg för externa ärendesystem. En tavla är en tavla oavsett om dess uppgifter kommer från Nessie eller speglas från Linear, Jira, GitHub eller Trello. En uppsättning verktyg täcker därför båda, och varje uppgift visar vilken den är. Det som skiljer är skrivning: en tavla som speglas från en skrivskyddad källa nekar en skrivning med ord och säger till agenten att det andra systemet äger ärendet och att ett nytt försök inte hjälper.
Att publicera ett dokument är ett godkännande, aldrig en omfattning
Agenter utkastar, och en person publicerar. Den regeln tillämpas för Nessies egna agenter genom att en agentaktör helt enkelt nekas. Men en parkopplad autentiseringsuppgift löses som en person, så det nekandet kan inte fånga den. Spärren måste sitta någon annanstans.
Så nessie_doc_publish publicerar inte. Den öppnar en knowledge.page.publish-godkännande – samma som en Nessie-agent öppnar – och returnerar { status: "awaiting_approval", approvalId }. Att fråga igen returnerar samma begäran i stället för att öppna en andra. Beslutet fattas på samma sätt som alla andra publiceringsbeslut i Nessie fattas: av en person, om det dokumentet, i det ögonblick det finns.
Det finns medvetet ingen publiceringsomfattning. Det fanns en. Vid parkopplingen fanns en bock som sa att agenten fick publicera, och den togs bort. Den avgjorde en fråga en gång för alla och i nittio dagar, en fråga som produkten ställer per dokument överallt annars. Dessutom avgjorde den den innan dokumentet fanns, så personen som bockade den kunde inte veta vad de godkände att publicera. nessie_doc_publish kräver documents_write, eftersom att fråga inte är gratis. Det läser sidan och lägger ett beslut framför någon. Men den omfattningen ger ingenting om publicering.
Två identiteter registreras på begäran, och skillnaden spelar roll. Begäraren är autentiseringsuppgiften, aldrig människan den agerar som. Den krävda godkännaren är den människan, och bara de får svara. Om begäran angav personen som begärare, skulle regeln som hindrar någon från att godkänna sin egen begäran diskvalificera den enda person som har rätt att besluta.
Se och återkalla vad som är parkopplat
En persons egna parkopplade agenter listas på /settings/security, med en etikett och en återkallningsknapp för varje. En ägare eller organisationsadmin ser varje autentiseringsuppgift i organisationen på /admin/security?tab=programs – vad var och en heter, vad den får göra, om den är aktiv och vems konto den lånar – och kan återkalla vilken som helst. Listan för den enskilda personen är bara för dem själva: en lista över aktiva autentiseringsuppgifter är en lista över fotfästen, inte allmän läsning.
Skrivningar som görs genom en autentiseringsuppgift tillskrivs den lånande människan och bär autentiseringsuppgiftens id i granskningsmetadata. Loggen kan därför skilja en persons egen redigering från en som hennes agent gjorde åt henne.
Det här finns inte ännu
Vi är tydliga med det, eftersom gapet mellan ett protokolls förmågor och en given servers är där dokumentation brukar slarva:
- Chatt, kanaler, körningar och agenthantering exponeras inte över MCP.
- Ett program förbereder ett nyhetsbrev och en person skickar det. Ingenting skickar, schemalägger eller tar bort en kampanj, och själva e-postmeddelandet skrivs i designern.
- Att länka och slå ihop kontakter, ändra CRM-schemat, importera och ställa in kanaler förblir hos en person i webbläsaren.
- Omkopplaren för kopplingar är en inställning på organisationsnivå. Team- och personliga undantag är något ett team eller en person inte kan ställa in idag.