MCP

Gi agenten det samme API-et vi selv bruker

MCP-serveren er et tynt skall over /api/v1 — ikke en egen implementasjon som kan drive fra hverandre. Verktøylista bygges av API-ets eget OpenAPI-dokument hver gang serveren starter.

Oppsett

Koble til klienten din

Serveren kjører over stdio: klienten starter den som en prosess, og den trenger to miljøvariabler. Det er hele oppsettet.

Terminal
claude mcp add friends-of \
  --env FO_API_BASE_URL=https://friendsof.no \
  --env FO_API_KEY=fo_live_DIN_NØKKEL \
  -- fo-mcp
  • FO_API_KEY er hele nøkkelen, inkludert fo_live_-prefikset. Prefikset er en del av nøkkelen, ikke en plassholder du skal fjerne.
  • FO_API_BASE_URL er API-adressen, https://friendsof.no. Den skal ikke ha /api/v1 på slutten; serveren legger på stien selv.
  • Mangler én av dem, avslutter serveren med en melding som sier hvilken. Den gjetter ikke, og den starter ikke halvveis.

Sjekk at det virket med claude mcp list — «friends-of» skal stå som tilkoblet. Be deretter agenten kjøre fo_discover: svarer den med riktig virksomhet og rolle, virker resten også.

fo-mcp er ikke publisert på npm ennå. Pakken er privat så lenge FriendsOf er under bygging, så programmet får dere av oss sammen med nøkkelen. Kommandoene over virker straks det ligger på maskinen.

Det finnes ingen MCP-adresse å lime inn ennå. En tilkobling over HTTP, med innlogging i nettleseren i stedet for en nøkkel, står på veikartet og er ikke bygget. Vi publiserer den den dagen den svarer.

Fire ting det er verdt å vite

Hva en agent kan, og hvor grensen går

Å gi en modell skrivetilgang til kunderegisteret er ikke en liten avgjørelse. Her er nøyaktig hva den avgjørelsen innebærer.

Verktøyene er ikke skrevet for hånd

Serveren henter API-ets eget OpenAPI-dokument når den starter og bygger verktøylista av det. Et endepunkt med en operasjons-ID er et verktøy — det finnes ingen andre liste som kan komme i utakt, og en rute uten den annoteringen får serveren til å stoppe framfor å bli borte i stillhet.

Nøkkelen bestemmer hva agenten er

Én nøkkel gjelder én virksomhet, og den kan aldri gjøre mer enn rollen den ble laget under. Det finnes ingen egen MCP-tilgang å skru på ved siden av: flaten er det samme API-et, så det er den samme adgangskontrollen.

Godkjenning er fortsatt et menneske

Agenten kan skrive utkast, sette sammen segmenter og sende noe til godkjenning. Å godkjenne — det ene steget som legger ekte e-post i sendekøen — finnes ikke som verktøy i det hele tatt. Det skjer i grensesnittet, av en innlogget person, og navnet deres er det som står som «godkjent av». Det er ikke en rettighet man kan slå på: ingen nøkkel, ingen rolle og ingen scope gjør godkjenning tilgjengelig for en maskin.

Kildesystemene er fortsatt utenfor rekkevidde

Ingen verktøy skriver til Flow Retail eller Shopware, fordi ingen slik kodevei finnes. En agent kan be oss hente på nytt fra kilden; den kan ikke be oss endre noe der.

Kom i gang

Tre ting å be om første dagen

Alle tre bruker prøvekjøring eller stopper før godkjenning, fordi det er slik man blir kjent med et verktøy som skriver.

Bygg et segment

«Finn kundene som har kjøpt Sandnes Garn for over 500 kroner det siste året og har gitt e-postsamtykke. Kjør en prøve først og vis meg hvor mange det blir, før du lagrer segmentet.»

Skriv et utkast

«Lag en høstkampanje mot det segmentet, med de to strikkepakkene som har best margin. Skriv utkastet, vis meg emnelinja, og send den til godkjenning — ikke godkjenn den.»

Rydd i samtykkekartet

«Hvilke samtykkedefinisjoner fra Flow Retail er fortsatt ikke oversatt? List dem med navnene de har i kilden, så sier jeg hvilken kanal hver av dem er.»

Verktøylista

Generert, ikke skrevet

Alt agenten kan gjøre

Navnene under er operasjons-ID-ene som ligger i API-et i dag, skrevet ut av det samme OpenAPI-dokumentet serveren bygger verktøyene sine av. Ingen har tastet dem inn her, og en test går rød hvis lista og API-et skulle skille lag.

Finn fram

Det første en agent kaller. Svarer med hvem nøkkelen er, hvilken rolle og hvilke rettigheter den har, og hele endepunktlista lest rett av det samme OpenAPI-dokumentet alt annet genereres fra.

  • fo_discoverStart here: who this key is, and every endpoint on this deployment
  • fo_read_skillThe full reference document for this API, served live

Medlemmer

Å lese medlemslista, og å skrive folk inn i den fra et annet system. Importstegene krever rettigheten import:write; lesingen gjør ikke.

  • fo_list_profilesList profiles (the member list), keyset-paginated
  • fo_create_profile SkriverImport one person, with their consents and tags
  • fo_search_profilesFind profiles by a rule set — the full filter vocabulary
  • fo_get_profileRead one profile
  • fo_list_profile_pointsA profile's point balance and ledger
  • fo_get_profile_consentA profile's current consent, per channel
  • fo_list_profile_tagsThe tags on one profile, with where each came from
  • fo_add_profile_tags SkriverPut tags on one profile by hand
  • fo_remove_profile_tag SkriverTake one tag off one profile
  • fo_import_profiles SkriverImport up to 500 people in one call

Segmenter

Den samme regeldefinisjonen skjermbildet bruker. Ordforrådet kan spørres om, regelsettet kan telles opp før det lagres, og medlemslista kan leses etterpå — så en agent kan lage et segment et menneske kan åpne og forstå.

  • fo_list_segmentsList segments, keyset-paginated
  • fo_create_segment SkriverCreate a segment
  • fo_preview_segmentCount who a rule set reaches, without saving anything
  • fo_get_segment_vocabularyEverything needed to write a rule: the fields, and this tenant's own options
  • fo_get_segmentRead one segment
  • fo_update_segment SkriverUpdate a segment
  • fo_evaluate_segment SkriverRecompute a dynamic segment's membership now
  • fo_list_segment_membersList who is in a segment right now, keyset-paginated
  • fo_add_segment_members SkriverAdd people to a static segment
  • fo_remove_segment_member SkriverTake one person out of a static segment
  • fo_suggest_segment SkriverTurn a sentence into a rule set, previewed and ready to save

Kampanjer

Hele godkjenningsløpet, fra produktsøk til godkjent eller avvist. Alle skrivestegene lager utkast — ett eneste av dem sender.

  • fo_search_campaign_productsThe campaign product picker: search by SKU or name, filter by brand and product group
  • fo_list_campaignsList campaigns, keyset-paginated
  • fo_create_campaign SkriverCreate a campaign (DRAFT — composing, approving and sending are a different surface)
  • fo_get_campaignRead one campaign
  • fo_delete_campaign SkriverDelete a draft that was never approved. Anything approved or sent stays as history
  • fo_get_campaign_contentRead a campaign's composed content, its resolved recipient count and its send progress
  • fo_update_campaign_content SkriverReplace a campaign's subject and block body — the edit half of edit-then-approve
  • fo_compose_campaign SkriverAsk Claude to draft this campaign's copy (§5.13). Writes a DRAFT — sends nothing, schedules nothing
  • fo_submit_campaign SkriverHand a draft to a human for approval. Sends nothing
  • fo_reject_campaign SkriverReject a campaign awaiting a human decision. A reason is required
  • fo_suggest_campaigns SkriverPropose campaigns from what this business actually sells and to whom
  • fo_preview_campaignRender this campaign exactly as one real recipient will receive it
  • fo_check_campaign SkriverRun the pre-send checks. A campaign cannot be approved until they pass
  • fo_test_send_campaign SkriverQueue a test copy of this campaign to named addresses or the preview list
  • fo_cancel_campaign SkriverStop a campaign that is queued or already SENDING. A reason is required
  • fo_get_campaign_resultsWhat one campaign actually did — deliverability, attributed revenue, and the online/in-store split

Maler

Malen e-posten settes inn i: seksjonene, fargene og den som er standard for nye kampanjer.

  • fo_list_layoutsList this tenant's e-mail layouts
  • fo_create_layout SkriverCreate an e-mail layout
  • fo_archive_layout SkriverArchive an e-mail layout
  • fo_set_default_layout SkriverMake this the default layout for new campaigns
  • fo_get_layoutRead one e-mail layout
  • fo_update_layout SkriverUpdate an e-mail layout

Skjemaer

Påmeldingsskjemaene på nettsiden: å lage og redigere utkast, pause dem og lese tallene. Å publisere et skjema gjør en innlogget person i grensesnittet — ikke en nøkkel.

  • fo_list_formsList this tenant's sign-up forms
  • fo_create_form SkriverCreate a sign-up form as a draft, from a template or a definition
  • fo_list_form_templatesList the built-in sign-up form templates
  • fo_get_form_statsWhat a sign-up form did: shown, submitted, confirmed, members and consents, per day
  • fo_set_form_status SkriverPause, archive or resume a sign-up form
  • fo_get_formRead one sign-up form: its draft and what is published
  • fo_update_form SkriverEdit a sign-up form's draft

Ordrer

Kjøpshistorikken, på variantnivå. Den kan leses, og den kan importeres fra et annet system med import:write.

  • fo_list_ordersList orders, keyset-paginated
  • fo_create_order SkriverImport one order, with its lines and discounts
  • fo_get_orderRead one order, with its lines
  • fo_import_orders SkriverImport up to 200 orders in one call

Katalog

Produktene slik de er lest inn fra kilden, med varianter rullet opp til hovedproduktet.

  • fo_list_productsSearch products by SKU/name, filter by brand and product group (subtree-aware)
  • fo_get_productRead one product

Salgskanaler

Butikkene og nettbutikkene salget kommer fra.

  • fo_list_storesList every sales channel (store)
  • fo_create_store SkriverCreate or update a sales channel from a generic source

Samtykke

Samtykkeloggen er append-only: et samtykke legges til med sitt eget tidsstempel og sin egen kilde, aldri importens.

  • fo_record_consent SkriverAppend one consent action to a person's ledger

Samtykkekart

Å oversette kjedens egne samtykkedefinisjoner til kanalene vi sender på. Ingenting kan sendes før oversettelsen finnes.

  • fo_get_consent_mapThe tenant's Flow Retail consent definitions, current mapping and unresolved sightings
  • fo_map_consent SkriverMap one Flow Retail consent id to a canonical channel
  • fo_unmap_consent SkriverRemove the mapping of one source consent id, so it is unmapped again

Koblinger

Å styre innhentingen fra Flow Retail. Skriver hos oss — aldri i kildesystemet.

  • fo_get_connector_overviewConnector status, mode and sync history
  • fo_start_connector_backfill SkriverStart (or resume) a historical sync for this connector
  • fo_cancel_connector_backfill SkriverStop the historical sync that is running
  • fo_start_connector_product_sync SkriverSync the product catalogue now

sending

  • fo_get_sending_holdIs all sending for this tenant currently held?
  • fo_set_sending_hold SkriverStop — or resume — all sending for this tenant

reports

  • fo_get_effect_reportEvery campaign sent in a period, with its attributed revenue split online vs. in-store, plus totals
  • fo_get_sales_reportSales per sales channel for a period — orders, units, gross, discount, returns and net
  • fo_get_top_products_reportTop products for a period, rolled up to the product family, filterable by channel
  • fo_get_top_brands_reportTop brands and product groups for a period, the same six figures
  • fo_get_new_vs_returning_reportNew against returning customers for a period, with guest sales as their own row
  • fo_get_store_day_seriesThe per-shop, per-day series the other four reports are built from

points

  • fo_get_points_programThe points program's settings
  • fo_update_points_program SkriverSwitch points on, pause, close, or change the program's settings
  • fo_list_earn_actionsThe actions that give points
  • fo_create_earn_action SkriverDefine an action that gives points
  • fo_update_earn_action SkriverChange or archive an action
  • fo_list_earn_rulesEarn rules — multipliers on the base rate
  • fo_create_earn_rule SkriverAdd an earn rule
  • fo_update_earn_rule SkriverChange or end an earn rule
  • fo_list_tiersThe levels, lowest first
  • fo_replace_tiers SkriverWrite the whole set of levels
  • fo_record_point_action SkriverReport an action worth points for one member
  • fo_adjust_points SkriverAdd or remove points by hand, with an internal reason
  • fo_lookup_points_memberA member's balance and level, for a shop's «Min side»
  • fo_list_member_point_entriesA member's own points log, as the customer may see it
  • fo_quote_pointsWhat a basket would earn — read-only, writes nothing
  • fo_capture_points SkriverCapture a hold when the sale completes — step 2
  • fo_refund_points SkriverGive held or spent points back
  • fo_reserve_points SkriverHold points to pay for (part of) a basket — step 1
  • fo_get_points_liabilityWhat the account owes in points, and what it is worth
  • fo_list_points_reviewsEarns held by the safety net, waiting for a person
  • fo_list_point_balancesEvery member's points balance in kroner, with the total — for the accounts

labels

  • fo_list_product_label_definitionsThe dimensions this tenant's wares are labelled on, with coverage
  • fo_create_product_label_definition SkriverDefine a dimension to label every ware on
  • fo_get_label_effectDo audiences built on product labels sell better? Campaigns split by label use
  • fo_preview_product_label SkriverTry a label question on a sample of wares, storing nothing
  • fo_update_product_label_definition SkriverChange a label dimension
  • fo_sample_product_labels_for_reviewA random handful of labels nobody has checked yet, to check
  • fo_review_product_label SkriverSay whether one product's label is right
  • fo_list_profile_label_definitions«Kundeegenskaper»: the switch, progress, and the questions asked about customers
  • fo_create_profile_label_definition SkriverDefine a question to ask about customers
  • fo_update_profile_label_definition SkriverChange a customer question

site-elements

  • fo_list_site_elementsWhat the tracking tag shows on the webshop, whether each element is on the page, and what it did in 30 days

Setningen under hvert verktøynavn er API-ets egen beskrivelse, hentet ordrett fra dokumentet og derfor på engelsk. Den oversettes ikke her med vilje: en oversettelse ville være en ny tekst som kunne bli feil, og da ville denne lista være akkurat det den ikke skal være — en håndskrevet kopi.

Lista vokser etter hvert som API-et gjør det, og denne siden trenger ikke endres for det: den leses av det samme dokumentet, og en test går rød hvis de to skulle komme i utakt. I dag rekker en agent hele veien — søke fram folk med regler, telle opp et segment før det lagres, kjøre det på nytt og lese hvem som er i det, importere butikker, profiler, ordrer og samtykke fra et annet system med rettigheten import:write, be om et utkast, rette det og sende det til godkjenning, foreslå segmenter og kampanjer den ikke er blitt bedt om, og stelle malene e-posten settes inn i. Det siste steget er fortsatt et menneske med en personbundet nøkkel.

Spørsmål og svar

Det folk faktisk lurer på

Hvor får jeg API-nøkkelen og selve programmet?

Begge deler av oss, som en del av oppsettet. Nøkkelen lager vi sammen med dere og avgrenser til det agenten faktisk skal kunne; den vises én gang, og vi lagrer bare en hash av den, så den kan ikke hentes fram igjen senere. Selve fo-mcp-programmet er ikke publisert på npm så lenge FriendsOf er under bygging, så det får dere samtidig. Selvbetjent nøkkelutstedelse i grensesnittet er under bygging.

Hvorfor er det ingen adresse å lime inn?

Fordi serveren kjører over stdio, ikke over HTTP. Klienten starter den som en prosess på din egen maskin, og prosessen snakker med API-et deres over nettet. En MCP-adresse å koble til i nettleseren er en annen transport, og den er ikke bygget ennå — vi sier heller det enn å publisere en URL som ikke svarer.

Kan agenten sende e-post til kundene mine?

Den kan ikke. Det finnes ikke noe verktøy for å godkjenne — godkjenning skjer i grensesnittet, av en innlogget person som har lest utkastet. Vi hadde det som et verktøy med en nøkkel bundet til en person, og tok det bort 20. september 2026: en persons nøkkel i en maskins hender er ikke et bevis for at noen har lest noe. Endepunktet svarer 410 med begrunnelsen, og det finnes ingen nøkkel, rolle eller rettighet som åpner det igjen. Å STOPPE er en annen sak: fo_cancel_campaign og fo_set_sending_hold tar hvilken som helst nøkkel, fordi en agent bør kunne stanse en utsending den ikke kan starte. Vil dere stramme inn ytterligere, lag nøkkelen uten import:write — da avvises hele importflaten med en 403 som navngir rettigheten. Merk at verktøylista er den samme uansett hvilken nøkkel som brukes: den bygges av dokumentet, ikke av nøkkelen.

Kan agenten ødelegge noe?

Den kan ikke slette en profil og den kan ikke røre kassa eller nettbutikken; ingen av delene finnes som kodevei. Den kan endre et segment eller innholdet i et utkast. Be om en prøvekjøring først — skrivende kall tar dry_run, som kjører den ekte skrivingen inne i en transaksjon og ruller den tilbake, så du får hele resultatet uten at noe er lagret.

Hva skjer hvis API-et endrer seg mens serveren kjører?

Ingenting, før den startes på nytt. Den henter OpenAPI-dokumentet én gang ved oppstart, fordi en stdio-prosess lever like lenge som klienten. Det er et bevisst valg framfor å spørre om igjen: en verktøyliste som endrer seg midt i en samtale er verre enn en som er noen timer gammel.

Hvordan trekker jeg tilgangen tilbake?

Nøkkelen tilbakekalles, og den slutter å virke umiddelbart — for MCP-serveren og for alt annet som brukte den, siden det er den samme nøkkelen og den samme flaten.

Koster det noe?

Nei. MCP-serveren er ikke et tillegg; den er det samme API-et abonnementet allerede gir dere, pakket slik at en agent kan bruke det.

Konvensjonene verktøyene arver — omslaget, idempotens, prøvekjøring, penger som desimalstreng — står på utviklersiden.

Vil dere prøve agenten mot ekte data?

Vi setter opp en nøkkel mot et testmiljø med deres egne tall i, uten importrettigheter til å begynne med. Da ser dere hva en agent faktisk får til, og ingenting kan nå en ekte innboks: godkjenning finnes ikke på nøkkelflaten i det hele tatt.