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.
claude mcp add friends-of \
--env FO_API_BASE_URL=https://friendsof.no \
--env FO_API_KEY=fo_live_DIN_NØKKEL \
-- fo-mcpFO_API_KEYer hele nøkkelen, inkludertfo_live_-prefikset. Prefikset er en del av nøkkelen, ikke en plassholder du skal fjerne.FO_API_BASE_URLer API-adressen,https://friendsof.no. Den skal ikke ha/api/v1på 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 skrevetAlt 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 deploymentfo_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-paginatedfo_create_profileSkriverImport one person, with their consents and tagsfo_search_profilesFind profiles by a rule set — the full filter vocabularyfo_get_profileRead one profilefo_list_profile_pointsA profile's point balance and ledgerfo_get_profile_consentA profile's current consent, per channelfo_list_profile_tagsThe tags on one profile, with where each came fromfo_add_profile_tagsSkriverPut tags on one profile by handfo_remove_profile_tagSkriverTake one tag off one profilefo_import_profilesSkriverImport 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-paginatedfo_create_segmentSkriverCreate a segmentfo_preview_segmentCount who a rule set reaches, without saving anythingfo_get_segment_vocabularyEverything needed to write a rule: the fields, and this tenant's own optionsfo_get_segmentRead one segmentfo_update_segmentSkriverUpdate a segmentfo_evaluate_segmentSkriverRecompute a dynamic segment's membership nowfo_list_segment_membersList who is in a segment right now, keyset-paginatedfo_add_segment_membersSkriverAdd people to a static segmentfo_remove_segment_memberSkriverTake one person out of a static segmentfo_suggest_segmentSkriverTurn 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 groupfo_list_campaignsList campaigns, keyset-paginatedfo_create_campaignSkriverCreate a campaign (DRAFT — composing, approving and sending are a different surface)fo_get_campaignRead one campaignfo_delete_campaignSkriverDelete a draft that was never approved. Anything approved or sent stays as historyfo_get_campaign_contentRead a campaign's composed content, its resolved recipient count and its send progressfo_update_campaign_contentSkriverReplace a campaign's subject and block body — the edit half of edit-then-approvefo_compose_campaignSkriverAsk Claude to draft this campaign's copy (§5.13). Writes a DRAFT — sends nothing, schedules nothingfo_submit_campaignSkriverHand a draft to a human for approval. Sends nothingfo_reject_campaignSkriverReject a campaign awaiting a human decision. A reason is requiredfo_suggest_campaignsSkriverPropose campaigns from what this business actually sells and to whomfo_preview_campaignRender this campaign exactly as one real recipient will receive itfo_check_campaignSkriverRun the pre-send checks. A campaign cannot be approved until they passfo_test_send_campaignSkriverQueue a test copy of this campaign to named addresses or the preview listfo_cancel_campaignSkriverStop a campaign that is queued or already SENDING. A reason is requiredfo_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 layoutsfo_create_layoutSkriverCreate an e-mail layoutfo_archive_layoutSkriverArchive an e-mail layoutfo_set_default_layoutSkriverMake this the default layout for new campaignsfo_get_layoutRead one e-mail layoutfo_update_layoutSkriverUpdate 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 formsfo_create_formSkriverCreate a sign-up form as a draft, from a template or a definitionfo_list_form_templatesList the built-in sign-up form templatesfo_get_form_statsWhat a sign-up form did: shown, submitted, confirmed, members and consents, per dayfo_set_form_statusSkriverPause, archive or resume a sign-up formfo_get_formRead one sign-up form: its draft and what is publishedfo_update_formSkriverEdit 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-paginatedfo_create_orderSkriverImport one order, with its lines and discountsfo_get_orderRead one order, with its linesfo_import_ordersSkriverImport 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_storeSkriverCreate 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_consentSkriverAppend 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 sightingsfo_map_consentSkriverMap one Flow Retail consent id to a canonical channelfo_unmap_consentSkriverRemove 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 historyfo_start_connector_backfillSkriverStart (or resume) a historical sync for this connectorfo_cancel_connector_backfillSkriverStop the historical sync that is runningfo_start_connector_product_syncSkriverSync the product catalogue now
sending
fo_get_sending_holdIs all sending for this tenant currently held?fo_set_sending_holdSkriverStop — 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 totalsfo_get_sales_reportSales per sales channel for a period — orders, units, gross, discount, returns and netfo_get_top_products_reportTop products for a period, rolled up to the product family, filterable by channelfo_get_top_brands_reportTop brands and product groups for a period, the same six figuresfo_get_new_vs_returning_reportNew against returning customers for a period, with guest sales as their own rowfo_get_store_day_seriesThe per-shop, per-day series the other four reports are built from
points
fo_get_points_programThe points program's settingsfo_update_points_programSkriverSwitch points on, pause, close, or change the program's settingsfo_list_earn_actionsThe actions that give pointsfo_create_earn_actionSkriverDefine an action that gives pointsfo_update_earn_actionSkriverChange or archive an actionfo_list_earn_rulesEarn rules — multipliers on the base ratefo_create_earn_ruleSkriverAdd an earn rulefo_update_earn_ruleSkriverChange or end an earn rulefo_list_tiersThe levels, lowest firstfo_replace_tiersSkriverWrite the whole set of levelsfo_record_point_actionSkriverReport an action worth points for one memberfo_adjust_pointsSkriverAdd or remove points by hand, with an internal reasonfo_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 itfo_quote_pointsWhat a basket would earn — read-only, writes nothingfo_capture_pointsSkriverCapture a hold when the sale completes — step 2fo_refund_pointsSkriverGive held or spent points backfo_reserve_pointsSkriverHold points to pay for (part of) a basket — step 1fo_get_points_liabilityWhat the account owes in points, and what it is worthfo_list_points_reviewsEarns held by the safety net, waiting for a personfo_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 coveragefo_create_product_label_definitionSkriverDefine a dimension to label every ware onfo_get_label_effectDo audiences built on product labels sell better? Campaigns split by label usefo_preview_product_labelSkriverTry a label question on a sample of wares, storing nothingfo_update_product_label_definitionSkriverChange a label dimensionfo_sample_product_labels_for_reviewA random handful of labels nobody has checked yet, to checkfo_review_product_labelSkriverSay whether one product's label is rightfo_list_profile_label_definitions«Kundeegenskaper»: the switch, progress, and the questions asked about customersfo_create_profile_label_definitionSkriverDefine a question to ask about customersfo_update_profile_label_definitionSkriverChange 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.