# MCP-server

Bruk Formstep fra Claude, Cursor og andre MCP-kompatible verktøy.

## MCP-server

Model Context Protocol-serveren (MCP) lar AI-agenter lese og redigere Formstep-skjemaene dine med rike, schemadrevne verktøy.

<h2 id="what">Hva det er</h2>
<p>
  MCP er en åpen standard for AI-verktøy som kobler til eksterne tjenester. Formstep tilbyr et hostet MCP-endepunkt som enhver
  MCP-kompatibel klient kan koble til, inkludert Claude Code, Claude desktop og Cursor.
</p>

<h2 id="connection">Tilkobling</h2>

```
URL:  https://api.formstep.io/api/mcp   (POST, streamable HTTP)
Auth: Bearer <token>
```

<p>To typer bearer-token fungerer:</p>
<ul>
  <li>
    <strong>API-token</strong> (<code>fb_...</code>) — opprettet fra <a href="/no/developers/api-tokens">API-tokens</a>. Best egnet for
    personlig bruk og rask oppsett.
  </li>
  <li>
    <strong>OAuth-tilgangstoken</strong> (<code>fbo_...</code>) — utstedt av <a href="#oauth">OAuth-flyten</a>. Best egnet for
    tredjepartsapper som kobler til på vegne av en bruker.
  </li>
</ul>
<p>
  Begge er bundet til nøyaktig ett arbeidsområde og når de samme verktøyene. Et verktøykall som navngir et annet arbeidsområde, eller et
  skjema i ett, feiler med <code>FORBIDDEN</code>. OAuth-tokens har også scopes (<code>mcp:read</code>, <code>mcp:write</code>,{' '}
  <code>offline_access</code>), men ingen verktøy er i dag låst til dem — behandle ethvert token som full tilgang innenfor sitt
  arbeidsområde.
</p>
<p>
  Verktøykall er begrenset til 120 per minutt per token, delt med <a href="/no/developers/rest-api">API-et</a>: bare <code>tools/call</code>{' '}
  bruker av budsjettet, mens <code>initialize</code>, <code>tools/list</code>, <code>prompts/*</code> og <code>resources/*</code> er gratis.
  Over budsjettet returnerer kallet fortsatt HTTP 200 med et mislykket verktøyresultat som bærer <code>RATE_LIMITED</code> og en{' '}
  <code>retryAfterMs</code> — poll på en timer, aldri i en løkke.
</p>

> 💡 **Hvor du får et token**
> <p>
>     Åpne <strong>OAuth og API-nøkler</strong> i arbeidsområdets sidepanel for å opprette API-tokens og se tilkoblede OAuth-apper. Se{' '}
>     <a href="/no/developers/api-tokens">API-tokens</a> for en trinn-for-trinn-guide.
>   </p>

<h2 id="core-tools">Kjerneverktøy</h2>
<p>
  Alle verktøy annonseres på <code>tools/list</code> når en klient kobler til. Klienter som laster skjemaer ved behov, som Claude Code,
  henter det fullstendige skjemaet for et verktøy når en oppgave trenger det. Tabellen nedenfor dekker kjerneverktøyene de fleste oppgaver
  starter med; <code>load_tools</code> (kataloger) og <code>load_skill</code> (domeneguider) dokumenterer resten.
</p>

<p>
  Flere innsettingsvarianter — klokkeslett, filopplasting, signatur, betaling, matrise/rutenett, rangering, bildevelger, veksleknapp,
  tabell, liste, rad, beregnet felt, skjult felt, innebygd variabel, innebygd innhold (<code>editor_insertEmbedded</code> for YouTube,
  Google Maps eller iframe-innbygging) og en betinget logikkblokk (<code>editor_insertLogic</code>) — er også på <code>tools/list</code>.
  Last <code>load_skill("question-types")</code> for hele settet, med verktøynavn og felt for hver type. Betinget logikk redigeres med{' '}
  <code>editor_setLogic</code> i <a href="#tool-catalogs">editor-actions</a>-katalogen.
</p>

<h2 id="tool-catalogs">Verktøykataloger</h2>
<p>
  Disse verktøyene er også på <code>tools/list</code>. Kjør <code>load_tools</code> med et katalognavn for å få utvidet dokumentasjon
  (intro, fullstendige skjemaer, bruksmønstre, kanttilfeller) for de grupperte verktøyene, og kall dem deretter direkte.
</p>

<p>
  Katalogen <code>request-lifecycle</code> lister alle de åtte forespørselsverktøyene fordi in-app-chatten annonserer et mindre kjernesett.
  Over dette endepunktet er alle åtte allerede på <code>tools/list</code>, så det katalogen legger til er dokumentasjonen.
</p>

<h2 id="skills">Ferdigheter (domenekunnskap)</h2>
<p>
  Ferdigheter er innebygde guider agenten kan laste via <code>load_skill</code>. De gir domenekunnskap som hjelper agenten med å ta bedre
  beslutninger — ikke verktøyskjemaer, men designråd og feltssemantikk.
</p>

<h2 id="requests">Forespørsler</h2>

<p>
  En <a href="/no/requests/overview">forespørsel</a> tildeler ett publisert skjema til én navngitt mottaker, med sin egen lenke, sine egne
  forhåndsutfylte svar, og sitt eget utfall. Det er slik en agent ber et virkelig menneske om noe og finner ut hva de svarte.
</p>

<h3 id="requests-create">Opprette en forespørsel</h3>

<p>
  Start alltid med <code>fields_list(formId)</code>. Den returnerer de adresserbare nøklene til skjemaets nåværende publiserte versjon, hver
  med en <code>usage</code>-linje som sier hvilket argument nøkkelen hører til i — synlige spørsmål går i <code>prefill</code>, skjulte felt
  i <code>context</code>. Avled aldri en nøkkel fra en spørsmålstittel, og les på nytt etter <code>form_publish</code>.
</p>

```
{
  "formId": "j57...",
  "recipient": { "email": "ada@acme.com", "name": "Ada" },
  "prefill": { "company_name": "Acme", "plan": "pro" },
  "readonly": ["company_name"],
  "context": { "crm_id": "A-42" },
  "metadata": { "run_id": "exec_918" },
  "delivery": "email",
  "expiresAt": 1780000000000,
  "callbackUrl": "https://hooks.acme.com/formstep",
  "idempotencyKey": "po-42"
}
```

<p>Resultatet inneholder lenken og klokken:</p>

```
{
  "id": "kd7...",
  "status": "pending",
  "url": "https://form.formstep.io/r/rq_...",
  "deliveryStatus": "queued",
  "expiresAt": 1780000000000,
  "createdAt": 1747000000000,
  "deduplicated": false,
  "next": "..."
}
```

<p>
  <code>delivery</code> er som standard <code>"none"</code>, som gir deg <code>url</code> til å levere selv; <code>"email"</code> sender
  invitasjonen og krever <code>recipient.email</code> på en Pro- eller Business-plan, eller en av de 10 gratis invitasjonene til en
  Gratis-konto. <code>readonly</code> låser felt mottakeren ikke kan redigere, og hver låst nøkkel må også være forhåndsutfylt.{' '}
  <code>context</code> tar bare imot nøkler for skjulte felt, mens <code>metadata</code> er ugjennomsiktig bokføring som ekkoes tilbake på{' '}
  <code>request_get</code> og i callbacken. <code>expiresAt</code> er epoke-millisekunder, standard er 30 dager og maks er 365.{' '}
  <code>idempotencyKey</code> er gyldig i 30 dager innenfor arbeidsområden: samme nøkkel med samme body returnerer den opprinnelige
  forespørselen med <code>deduplicated: true</code>, og en annen body er en konflikt. Hver respons har også en <code>next</code>-linje som
  forteller agenten hva den skal gjøre videre.
</p>
<p>
  <code>deliveryStatus</code> er <code>not_requested</code> til en invitasjon legges i kø, deretter <code>queued</code> → <code>sent</code>{' '}
  eller <code>failed</code>, og <code>bounced</code> når e-postleverandøren melder om en hard retur eller en klage. Å opprette en
  forespørsel bruker én enhet av arbeidsområdets månedlige kvote enten mottakeren svarer eller ikke; når den er brukt opp, feiler{' '}
  <code>request_create</code> med <code>MONTHLY_ALLOWANCE_REACHED</code>.
</p>

<h3 id="requests-callbacks">Callbacks eller polling</h3>

<p>
  Med en <code>callbackUrl</code> POST-er Formstep én gang per avsluttende hendelse — fullføring, utløp, kansellering — signert med
  arbeidsområdens <strong>signeringshemmelighet for forespørsler</strong>. Se <a href="/no/requests/callbacks">Callbacks og signering</a>{' '}
  for nyttelasten og verifiseringsoppskriften.
</p>

> ℹ️ **Autonome agenter bør polle**
> <p>
>     Signeringshemmeligheten for forespørsler vises kun på siden for legitimasjon i arbeidsområdet ditt — den returneres aldri over MCP eller
>     API-et. En agent som kjører på egen hånd, uten et menneske til å sette opp og konfigurere en mottaker, kan derfor ikke verifisere en
>     callback. La <code>callbackUrl</code> være tom og poll heller <code>request_get(requestId)</code> i størrelsesorden minutter snarere enn
>     sekunder, helt til <code>status</code> forlater <code>"pending"</code>. <code>expiresAt</code> setter en grense for hvor lenge det er
>     verdt å gjøre det.
>   </p>

<h3 id="requests-test-mode">Testmodus</h3>

<p>
  Send <code>test: true</code> for å øve på hele oppsettet før en ekte kjøring. Lenken åpnes fortsatt og kan fullføres, og callbacken fyrer
  med <code>"test": true</code> — men ingenting sendes på e-post uansett hva <code>delivery</code> sier, forespørselen forblir skjult fra
  Forespørsler-siden og analysetrakten, og innsendingen teller ingen steder: ingen kvote, ingen eksporter, ingen integrasjoner.
  Testforespørsler vises bare i <code>request_list</code> når du sender <code>includeTest: true</code>. Lenken lukkes innen 24 timer, og på
  Free kan et workspace opprette 10 testforespørsler per dag.
</p>

<h3 id="requests-documents">Dokumenter per forespørsel</h3>

<p>
  For å gi én mottaker en fil — et kontraktsutkast, deres eget tilbud — trenger skjemaet en <strong>dokumentblokk</strong>, som en forfatter
  eller en agent setter inn med <code>editor_insertDocumentsBlock</code>. <code>fields_list</code> rapporterer den som{' '}
  <code>type: "documents"</code>. Bytene reiser aldri gjennom et verktøy:
</p>

<ol>
  <li>
    Kall <code>document_create</code> med <code>formId</code>, <code>name</code>, <code>contentType</code>, og den eksakte <code>size</code>{' '}
    i bytes. Du får tilbake <code>{'{ id, name, contentType, size, uploadUrl, expiresAt }'}</code>. Kun PDF og bilder (ingen
    Office-dokumenter), 25 MB per fil, og 100 MB dokumenter per forespørsel.
  </li>
  <li>
    <code>PUT</code> de rå bytene til <code>uploadUrl</code> innen timen, med <code>Content-Type</code> satt til typen du oppga.
  </li>
  <li>
    Referer den fra <code>request_create</code>: <code>documents: [{'{ documentId, field?, name? }'}]</code>. <code>field</code> er
    dokumentblokkens feltnøkkel, valgfri bare når skjemaet har nøyaktig én slik blokk. <code>name</code> overstyrer visningsnavnet for denne
    forespørselen.
  </li>
</ol>

<p>
  Blokkens faste dokumenter blir stående, og dine dukker opp under dem, kun for denne mottakeren. <code>request_create</code> verifiserer
  opplastingen før forespørselen finnes, så <code>DOCUMENT_NOT_UPLOADED</code> betyr at steg 2 ble hoppet over. Én opplasting kan refereres
  av et hvilket som helst antall forespørsler, og bytene teller mot arbeidsområdens lagring.
</p>

<h3 id="requests-domains">Egendefinerte domener</h3>

<p>
  Send <code>domainId</code> til <code>request_create</code> for å utstede lenken på ett av arbeidsområdens{' '}
  <a href="/no/branding-domains/custom-domains">egendefinerte domener</a>. IDene kommer fra <code>formShareLink_list</code>, som returnerer
  dem som <code>availableCustomDomains</code>. Utelater du den, bruker lenken det domenet skjemaet allerede er publisert under.
</p>

<h3 id="requests-reading">Lese resultater</h3>

<p>
  <code>request_get</code> returnerer hele forespørselen. Når den er fullført, inneholder <code>answers</code> mottakerens verdier,
  nøkkelsatt etter feltnøkkel, <code>display</code> de samme nøklene som lesbar tekst, og <code>outcome</code> — godkjent, avvist, eller
  endringer — er deres avgjørelse når skjemaet har et <a href="/no/requests/overview">beslutningsspørsmål</a>. Et{' '}
  <code>callbackFailedAt</code>-tidsstempel betyr at leveringen gikk tom for nye forsøk og at ingenting nådde endepunktet ditt; fiks
  mottakeren, og kall deretter <code>request_replayCallback</code>, som sender den opprinnelige hendelses-IDen på nytt slik at mottakeren
  din dedupliserer i stedet for å kjøre på nytt. Etter at skjemaets oppbevaringspolicy fjerner en forespørsel, settes{' '}
  <code>dataPurgedAt</code>, og svarene er borte for godt.
</p>

<p>
  <code>request_list</code> feier gjennom mange på én gang, filtrert etter <code>status</code>, <code>outcome</code>,{' '}
  <code>externalId</code>, og <code>includeTest</code>. Sideinndel med <code>nextCursor</code>: en side kan sjelden komme tilbake med en tom{' '}
  <code>items</code> og <code>hasMore: true</code>, noe som ikke er slutten på listen — send cursoren tilbake og fortsett.
</p>

<h2 id="resources">Ressurser og prompter</h2>
<p>
  Hver ferdighet og verktøykatalog er også en MCP-ressurs på <code>skill://&lt;name&gt;</code> — <code>skill://requests</code>,{' '}
  <code>skill://editor-inserts</code>. En klient som støtter <code>resources/list</code> kan bla gjennom og lese dem uten å kalle{' '}
  <code>load_skill</code> eller <code>load_tools</code>. Serveren tilbyr også fire prompter på <code>prompts/list</code>:{' '}
  <code>identity</code>, <code>capabilities</code>, <code>data_tools</code>, og <code>editor_tools</code>.
</p>

<h2 id="confirmation">Verktøy som spør først</h2>
<p>
  Hvert verktøy bærer MCP-hintene <code>readOnlyHint</code> og <code>destructiveHint</code>, avledet fra verbet sitt. Disse verktøyene er
  merket destruktive, fordi det å angre dem krever et nytt kall eller ikke er mulig: <code>form_delete</code>, <code>form_unpublish</code>,{' '}
  <code>workspaceFolder_delete</code>, <code>editor_deleteElement</code>, <code>translationLanguage_delete</code>, og{' '}
  <code>request_cancel</code>. De fleste klienter spør brukeren før de kjører dem, men ledeteksten er klientens avgjørelse, så sjekk
  godkjenningsinnstillingene dens hvis du trenger en garantert stopp.
</p>

<h2 id="limitations">Begrensninger</h2>
<ul>
  <li>
    <strong>Ingen binæropplastinger via et verktøykall.</strong> Bilder settes med URL: covers, logoer og bildeblokker godtar{' '}
    <code>http(s)://</code> eller <code>data:image</code>-URI-er. Et dokument per forespørsel er unntaket: <code>document_create</code>{' '}
    returnerer en opplastings-URL som en klient med HTTP-tilgang kan <code>PUT</code>e filen til (se{' '}
    <a href="#requests-documents">Dokumenter per forespørsel</a>). For å gjøre en PDF eller et skjermbilde om til et skjema, bruk den{' '}
    <a href="/no/ai/ai-form-generation#files">innebygde AI-chatten</a>.
  </li>
  <li>
    <strong>Ingen arbeidsområde-AI-ferdigheter.</strong> <a href="/no/ai/ai-skills">Ferdigheter skrevet i Formstep</a> er bare tilgjengelige
    i den innebygde AI-chatten. Serverens egne ferdigheter (<code>load_skill</code>) er tilgjengelige over MCP.
  </li>
</ul>

<h2 id="api-token-clients">Koble til med et API-token</h2>
<p>
  De fleste klienter logger inn med OAuth: lim inn URL-en uten header og følg{' '}
  <a href="/no/guides/ai-agents/connect">Koble til en AI-agent</a>. En klient som ikke kan åpne en nettleser, som et skript, en CI-jobb
  eller en hodeløs agent, sender i stedet et <a href="/no/developers/api-tokens">API-token</a> som en header.
</p>
<p>Claude Code, fra kommandolinjen:</p>

```
claude mcp add --transport http formstep https://api.formstep.io/api/mcp \
  --header "Authorization: Bearer fb_YOUR_TOKEN"
```

<p>
  Eller i et prosjekts <code>.mcp.json</code>:
</p>

```
{
  "mcpServers": {
    "formstep": {
      "type": "http",
      "url": "https://api.formstep.io/api/mcp",
      "headers": {
        "Authorization": "Bearer fb_YOUR_TOKEN"
      }
    }
  }
}
```

<p>
  Cursor, i <code>.cursor/mcp.json</code> eller <code>~/.cursor/mcp.json</code>:
</p>

```
{
  "mcpServers": {
    "formstep": {
      "url": "https://api.formstep.io/api/mcp",
      "headers": {
        "Authorization": "Bearer fb_YOUR_TOKEN"
      }
    }
  }
}
```

<p>
  For å logge inn med OAuth i stedet for et token, bruk{' '}
  <a href="https://cursor.com/install-mcp?name=formstep&config=eyJ1cmwiOiJodHRwczovL2FwaS5mb3Jtc3RlcC5pby9hcGkvbWNwIn0=">
    Legg til i Cursor
  </a>
  . Den legger til server-URL-en uten header, og Cursor ber deg logge inn på Formstep.
</p>
<p>Andre klienter tar samme URL og header; se deres dokumentasjon for hvor.</p>

<h2 id="oauth">Bruke OAuth i stedet for API-tokens</h2>
<p>
  En tredjeparts-app som kobler til på vegne av en bruker, bør bruke OAuth i stedet for å be om et innlimt token. Formstep er en OAuth
  2.1-autorisasjonsserver med obligatorisk PKCE (S256) og opake tokens — ingen JWT-er, ingen implisitt grant. Claude desktop, Claude Code og
  Claude.ai-nettkobleren oppdager alt dette fra MCP-endepunktet, så det holder å lime inn URL-en uten header: 401-en peker til{' '}
  <code>/.well-known/oauth-protected-resource</code>, og klienten tar det derfra.
</p>
<p>Flyten, for en klient du skriver selv:</p>
<ol>
  <li>
    <code>GET /.well-known/oauth-protected-resource</code>, deretter <code>GET /.well-known/oauth-authorization-server</code> for
    endepunkt-URL-ene, scopene og de støttede autentiseringsmetodene.
  </li>
  <li>
    <code>POST /oauth/register</code> med dine <code>redirect_uris</code> (dynamisk klientregistrering, ingen legitimasjon nødvendig). Du
    får en <code>client_id</code>, pluss en <code>client_secret</code> hvis du ba om noe annet enn{' '}
    <code>token_endpoint_auth_method: "none"</code>. Redirect-URI-er må være HTTPS, eller HTTP på <code>localhost</code>. Registrering er
    begrenset til 20 per time per IP.
  </li>
  <li>
    Send brukeren til <code>/oauth/authorize</code> med <code>response_type=code</code>, din <code>client_id</code>, den registrerte{' '}
    <code>redirect_uri</code>, <code>scope=mcp:read mcp:write offline_access</code>, <code>state</code>, og en <code>code_challenge</code>{' '}
    med <code>code_challenge_method=S256</code>. De logger inn, velger ett arbeidsområde, og autoriserer.
  </li>
  <li>
    Bytt inn koden på <code>POST /oauth/token</code> med <code>grant_type=authorization_code</code> og din <code>code_verifier</code>, innen
    60 sekunder. Koder kan bare brukes én gang.
  </li>
  <li>
    Kall MCP-endepunktet med <code>Authorization: Bearer fbo_...</code>. Tilgangstokens varer i 1 time; refresh-tokens varer i 30 dager og
    roteres ved hver bruk. Gjenbruk av et brukt refresh-token brenner hele kjeden, så lagre det nyeste.
  </li>
</ol>
<p>
  <code>POST /oauth/revoke</code> (RFC 7009) tilbakekaller et tilgangs- eller refresh-token. En bruker kan også koble fra hele appen fra{' '}
  <strong>Tilkoblede apper</strong> på siden OAuth og API-nøkler, som dreper alle tokens den har for det arbeidsområdet.
</p>

<p>Har du allerede et token for hånden, går det samme sted som en API-nøkkel:</p>

```
{
  "mcpServers": {
    "formstep": {
      "type": "http",
      "url": "https://api.formstep.io/api/mcp",
      "headers": {
        "Authorization": "Bearer fbo_USER_OAUTH_TOKEN"
      }
    }
  }
}
```

<h3 id="connected-apps">Tilkoblede apper</h3>
<p>
  Hver OAuth-tilkobling er listet under <strong>Tilkoblede apper</strong> på siden <strong>OAuth og API-nøkler</strong>, med når den ble
  tilkoblet og sist brukt. Tilkoblinger er personlige: bare brukeren som autoriserte den, ser den, og arbeidsområdeadministratorer kan
  verken se eller tilbakekalle et annet medlems. Å koble fra trer i kraft umiddelbart. En bruker som forlater eller fjernes fra
  arbeidsområdet, mister alle sine tilkoblinger til det.
</p>

<h2 id="next-steps">Neste steg</h2>
<div class="not-prose grid gap-3 sm:grid-cols-2">
  - [Koble til en AI-agent](/no/guides/ai-agents/connect) — Trinn-for-trinn-oppsett i enhver MCP-klient
  - [Webhooks-referanse](/no/developers/webhooks-reference) — Nyttelastskjema og signering
  - [REST API](/no/developers/rest-api) — API-metoder for programmatisk tilgang
  - [Planer og priser](/no/subscription-billing/plans-pricing) — Sammenlign plan-API-tilgang og grenser
</div>
