Skip to content
Apier
Apier.no

MCP server

Connect any MCP-compatible agent to Apier's hosted Norwegian compliance tools — per-client config, a five-minute quickstart, and the full 26-tool reference.

[Cite this as: Apier.no Docs v0.1.0 — last updated 2026-07-19]

Apier ships a hosted Model Context Protocol (MCP) server so an AI agent can answer Norwegian compliance questions — what a company must file, who may act for it, and when obligations are due — without you teaching it Norwegian law or rebuilding the Altinn / Maskinporten / Brønnøysund integration. The agent discovers a set of deterministic tools and calls them; every answer is the same shape it would get over REST, plus a justification an agent can cite back to a user.

Endpointhttps://www.apier.no/api/mcp
TransportStreamable HTTP
Registryno.apier/mcp (the official MCP registry)
npm package@apier-no/mcp (a thin local proxy for stdio-only clients)
AuthBearer API key (discovery is keyless — see below)

The same tool verdicts are available over plain REST — see the endpoint reference. MCP is the fastest path for an agent runtime; REST is the fallback for any HTTP client.

One-click install

The quickest way in: register the Apier MCP server with your client in a single click. Cursor uses a real install deep link; Claude Desktop has no install scheme yet, so the button copies a ready-to-paste config block. Both point at the same hosted endpoint — the detailed per-client configuration below is the manual alternative, and the place to add your API key.

MCP client install is available on desktop. See the connection guide

What you connect with

There are two ways to reach the server, and which one you use depends on your client:

  • Direct streamable HTTP. Clients that speak remote MCP natively (VS Code, the OpenAI Agents SDK, Azure AI Foundry, and recent Cursor builds) connect straight to https://www.apier.no/api/mcp and send the API key in an Authorization: Bearer header.
  • The @apier-no/mcp stdio proxy. Clients that only speak stdio (Claude Desktop, older Cursor) launch the published npm package via npx. It reads APIER_API_KEY from the environment, scrubs it from the child process, and forwards it as the bearer header to the same hosted endpoint. No tool logic runs locally — everything resolves server-side.

Per-client configuration

Each block below is copy-paste ready. Replace the placeholder key with a real one from your dashboard — see getting a key — and keep it out of source control.

Claude Desktop

Edit claude_desktop_config.json (Settings → Developer → Edit Config):

{
  "mcpServers": {
    "apier": {
      "command": "npx",
      "args": ["-y", "@apier-no/mcp"],
      "env": { "APIER_API_KEY": "apr_live_<your_key_here>" }
    }
  }
}

Restart Claude Desktop; the Apier tools appear in the tool menu.

Windows: if Claude Desktop reports spawn npx ENOENT, wrap the launcher — set "command": "cmd" and "args": ["/c", "npx", "-y", "@apier-no/mcp"], keeping the same env. The same applies to any stdio/npx-based client on Windows.

Cursor

Add the same server to ~/.cursor/mcp.json (global) or .cursor/mcp.json (per-project):

{
  "mcpServers": {
    "apier": {
      "command": "npx",
      "args": ["-y", "@apier-no/mcp"],
      "env": { "APIER_API_KEY": "apr_live_<your_key_here>" }
    }
  }
}

VS Code

VS Code (Copilot agent mode) speaks remote MCP natively, so it connects to the endpoint directly. Add .vscode/mcp.json:

{
  "servers": {
    "apier": {
      "type": "http",
      "url": "https://www.apier.no/api/mcp",
      "headers": { "Authorization": "Bearer apr_live_<your_key_here>" }
    }
  }
}

OpenAI Agents SDK

Wire the endpoint as a MCPServerStreamableHttp server and hand it to an Agent (Python):

import asyncio
from agents import Agent, Runner
from agents.mcp import MCPServerStreamableHttp

async def main():
    async with MCPServerStreamableHttp(
        name="apier",
        params={
            "url": "https://www.apier.no/api/mcp",
            "headers": {"Authorization": "Bearer apr_live_<your_key_here>"},
        },
    ) as apier:
        agent = Agent(
            name="Compliance assistant",
            instructions="Use Apier's tools for Norwegian compliance questions.",
            mcp_servers=[apier],
        )
        result = await Runner.run(agent, "What does org 999999999 owe?")
        print(result.final_output)

asyncio.run(main())

Azure AI Foundry

Register the endpoint as an MCP tool on a Foundry agent. Auth headers are passed per run and are not persisted by Foundry (Python):

from azure.ai.agents.models import McpTool

apier = McpTool(
    server_label="apier",
    server_url="https://www.apier.no/api/mcp",
)
apier.update_headers("Authorization", "Bearer apr_live_<your_key_here>")

agent = project_client.agents.create_agent(
    model="gpt-4o",
    name="compliance-assistant",
    instructions="Use Apier's tools for Norwegian compliance questions.",
    tools=apier.definitions,
)

Getting a key

Sign up and open your dashboard to create a key. It is shown in full exactly once — store it securely. Issued keys are prefixed apr_test_ (synthetic data) or apr_live_ (production). See Authentication for the full key model, tiers, and rate limits.

Try it without a key

tools/call also accepts the synthetic sandbox bearer — no signup, no key provisioning:

Authorization: Bearer apier_sandbox_test_<suffix>

Always append a unique random <suffix> (for example a fresh UUID) so your session never collides with another agent's. With a sandbox bearer, the read tools get_company_summary, get_company_context, get_company_obligations, get_company_deadlines, and explain_compliance_error answer from deterministic synthetic fixtures (every response carries _meta.is_sandbox: true), and submit_vat_return runs its usual sandbox flow. Use the reserved sandbox org numbers from GET /api/v1/sandbox/fixtures — the magic scenario pool there covers a plain active AS, a bankrupt company, an ENK, and more. Tools without a sandbox route return a deterministic SANDBOX_TOOL_UNAVAILABLE error; no sandbox call ever reaches production data or a government system.

How bearer auth works

The MCP server splits a keyless discovery surface from an authenticated call surface:

  • Keyless (no key needed) — the whole handshake and discovery surface: initialize, notifications/initialized, tools/list, ping, prompts/list, prompts/get, resources/list, resources/templates/list, resources/read (public rulebook resources only), and completion/complete. A client can complete the MCP handshake and read the full catalogue before presenting any credential; the discovery response is byte-identical for every caller.
  • Authenticated (key + scope)tools/call (except the five keyless public read tools — get_public_obligations, get_public_deadlines, explain_compliance_error, get_exchange_rate, get_pricing — which execute with no credential at all, and redeem_issuance_token, which needs no API key but DOES require a credential: the one-time owner-issued issuance token in its arguments), and resources/read for company-specific resources. Otherwise a missing or invalid key returns a JSON-RPC -32001 error with a WWW-Authenticate: Bearer challenge, never a result. Each tool's required scope is in the tool reference below.

Every response also carries the negotiated MCP-Protocol-Version header — see Supported protocol versions for the full list.

The @apier-no/mcp proxy injects the bearer header for you from APIER_API_KEY; the direct-HTTP clients above set it themselves.

OAuth is coming. Today authentication is a bearer API key. An OAuth 2.1 authorization-code flow — so an agent can obtain and refresh a key without copy-pasting one — is planned for a later release. The bearer key will keep working.

Supported protocol versions

Apier negotiates the MCP protocol version during initialize and echoes the agreed value on the MCP-Protocol-Version response header. The server supports, newest first:

  • 2025-11-25 (latest) — structured tool output, truthful tool annotations, per-tool icons, and verb_noun tool naming.
  • 2025-06-18
  • 2025-03-26
  • 2024-11-05

A client that requests any of these gets it back verbatim; a client that requests an unknown version is offered the server's highest. Every newer field — title, annotations, output schema, icons — is additive, so an older client simply ignores what it does not understand.

Roadmap — not implemented. The 2026-07-28 release candidate (the Tasks extension and MCP Apps) is not supported. Its design is still changing, so Apier has deliberately not built against it; this page will be updated when that work lands.

Five-minute quickstart

  1. Add the config for your client from the section above, using a real key. (No key yet? You can still explore the tool catalogue — tools/list is keyless — but calling a tool needs one.)
  2. Restart the client so it picks up the new server.
  3. Ask a compliance question in natural language. The agent reads the tool catalogue, picks the right tool, and calls it:
You:  What does org 999999999 owe?

Agent: (calls get_company_obligations with org_number "999999999")

      Org 999999999 has these active obligations:
      • MVA (VAT) return — filed per term — Merverdiavgiftsloven § 15-1
      • A-melding — monthly employer/payroll report — A-opplysningsloven § 3
      • Årsregnskap — annual accounts — Regnskapsloven § 8-2

      Source: Brønnøysund + Apier Universal Rulebook. Each verdict
      carries its legal_reference, so I can show you exactly which
      rule applies.

Under the hood the agent issued a single tools/call:

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "get_company_obligations",
    "arguments": { "org_number": "999999999" }
  },
  "id": 1
}

Apier resolves it against the live Rulebook and returns the structured obligation set. 999999999 is the documentation example org; to rehearse the full authenticated surface — per-company context, dry-run filings, receipts — with zero risk and no signup, point your agent at the sandbox (it never calls a government system and tags every response _meta.is_sandbox: true).

The response envelope

tools/call returns the MCP spec CallToolResult: the tool's answer is serialized into a text content block and mirrored on structuredContent, with isError flagging failures — so the official MCP SDK client parses it without special handling:

{
  "content": [{ "type": "text", "text": "{ …the envelope below, as JSON… }" }],
  "structuredContent": {
    "result": { "...": "the normalised tool payload" },
    "justification": {
      "rules_applied": ["MVA_FILING_BIMONTHLY"],
      "source_data": ["brreg", "rulebook"],
      "valid_until": "2026-07-01T00:00:00Z"
    },
    "metadata": {
      "query_id": "…",
      "resolved_at": "2026-06-05T08:00:00Z",
      "data_sources": ["brreg", "rulebook"],
      "schema_version": "2026.6.0",
      "tool_name": "get_company_obligations"
    }
  },
  "isError": false
}

structuredContent is the same three-part envelope every tool returns, so an agent parses results uniformly. justification is the audit signal — which rules and upstream sources produced the answer, and when it goes stale. On failure, isError is true, result is null, and metadata carries a stable error_code from a closed vocabulary plus a safe error_message (never a stack trace or raw upstream body), a retryable boolean (true = repeating the same call after a short backoff can plausibly succeed; false = change the input or credential first — an identical retry fails identically), and a one-sentence remediation hint. When an upstream code had to be collapsed or renamed to keep the vocabulary closed (for example an undocumented code surfacing as UPSTREAM_UNMAPPED, or a revoked delegation surfacing as DELEGATION_REVOKED), the raw code is preserved on metadata.upstream_code.

Tool reference

26 tools are live. Names are verb_noun, so an agent can read the intent from the name alone. 22 are read-only; four model writes — submit_vat_return (a VAT return, locked to the sandbox), the two Fullmakt Rails delegation tools request_fullmakt and revoke_fullmakt (AGT-02), which broker and revoke an agent's scoped authority to act for a company through an Altinn systembruker, and redeem_issuance_token, which converts an owner-minted one-time issuance token into the agent's own API key (a write against Apier's own account state only — no government system is touched). "Live" here means the route is callable: the Fullmakt Rails writes are mock-adapter-backed pending live partner validation and make no live Altinn PDP or execution call yet.

submit_vat_return is deliberately not listed in tools/list discovery — an agent enumerating the server sees 25 tools (22 reads plus the two Fullmakt Rails delegation writes plus the keyless issuance-token redemption), and calls submit_vat_return by name when it needs the sandbox VAT flow (it stays fully callable over tools/call). The other 25 each ship a longer description, a structured output schema, truthful behaviour hints, and worked examples in their tools/list entry — clients surface these automatically.

Company intelligence

ToolTitleWhat it doesScope
search_companiesSearch companies by nameResolve a company name to its org number — up to 10 candidates (name, org_number, org_form, municipality, status). Start here when you have a name but not a number, then call the tools below with the chosen org number.read:brreg
get_company_summaryCompany compliance summaryOne-shot identity and compliance verdict for an org number — the broadest single call.read:brreg
get_company_verificationCompany verification verdictOne verdict for an org, keyed on two facts: actively registered + signing authority visible (signaturrett, prokura, or the ENK innehaver). Not binary: unknown is returned when the status is indeterminate or no signing authority is visible — never inferred as fail. Bankruptcy (konkurs), liquidation (avvikling), dissolution (oppløst), filed accounts and MVA are surfaced alongside as transparency-only signals — they never change the verdict (a distressed company already reports a non-active status and fails on that).read:brreg
get_company_contextCompany registry factsBrønnøysund identity slice only (name, form, NACE, addresses, role codes) — no compliance verdict.read:brreg
get_company_profileCompany profile (Brønnøysund)Structured Enhetsregisteret profile: name, form, NACE, addresses, status, MVA flag, role codes.read:brreg
get_company_obligationsCompany regulatory obligationsEvery applicable obligation with its current state and legal reference, evaluated against the live Rulebook.read:brreg
get_company_deadlinesCompany filing calendarUpcoming filing dates per obligation and period — Europe/Oslo, DST- and holiday-aware — over a horizon you choose.read:brreg
get_company_authorityCompany signing-authority resolverWho can legally sign for the company, and how — sole / joint / by_role / prokura_only / no_authority / unknown, normalised from Brønnøysund's Fullmakttjenesten combined with the registered role holders. No legal citation asserted.read:brreg
get_company_accountsCompany annual accounts snapshotCurrent-snapshot årsregnskap from the open Regnskapsregisteret: filed-status (tri-state), most-recent accounts year, and that year's key figures (currency always surfaced).read:brreg
get_company_filing_historyCompany filing history (Altinn)The company's Altinn 3 filing instances, each paired with the Apier audit-log entry that produced it (filed_via_apier) — the accountant/auditor reconciliation wedge. Offset-paginated. Mock-gated until the live Altinn scope is approved.read:altinn

Three details of these tools are worth knowing before you wire them up:

  • get_company_summary payload equivalence. Its obligations[] array is byte-identical to what get_company_obligations returns (same rule evaluation, shared cache), and its deadlines[] array matches get_company_deadlines over the same horizon for Tier 1 companies — on Tier 2 the summary's deadlines[] additionally carry a definitive filing_status (filed / overdue) from compliance state. Because the summary tool takes only { org_number }, call get_company_deadlines when you need to control horizon_months, or get_company_obligations for per-obligation drill-down.
  • MOD-11 validation. For get_company_summary, get_company_context, get_company_obligations, and get_company_deadlines, a 9-digit org_number that fails the MOD-11 control-digit check is rejected up-front as VALIDATION_FAILED before any request is sent. get_company_profile is the exception: it does not run MOD-11 in its input schema, so a MOD-11 failure surfaces from the backing route as ORG_NUMBER_INVALID_CHECKSUM (HTTP 400). Either way, correct the number rather than retrying the same one.
  • get_company_profile uses POST. Its backing REST route is POST /api/v1/brreg/company-profile (the org number goes in the JSON body), whereas the sibling company tools are GET /api/v1/company/{org}/…. This is a known method inconsistency — do not assume GET when falling back to raw HTTP.

Public lookups (no org number required)

ToolTitleWhat it doesScope
get_public_obligationsObligations by entity typeBaseline obligations an entity owes by being that form (AS, ENK, NUF, …), before per-company data.read:rulebook
get_public_deadlinesNorwegian filing calendarThe universal filing calendar for a year — MVA, A-melding, årsregnskap.read:rulebook
get_exchange_rateNorges Bank exchange rateThe latest Norges Bank NOK reference rate for an ISO-4217 currency (NOK-anchored — exactly one of base/quote must be NOK; optional date).read:norgesbank
get_altinn_migration_guidanceAltinn 2 → 3 migration guidanceThe Altinn 3 equivalent of an Altinn 2 service or role code, plus the Oslo-computed post-cutoff status of the 19 June 2026 Altinn 2 deprecation (now passed — deadline_passed is true, days_remaining is 0). Gate production migration actions on verified === true. Backing route is zero-auth; the MCP tool gates on read:digdir.read:digdir

Change archive

ToolTitleWhat it doesScope
list_changesQuery the cross-source change archivePaginated, filterable read over every detected created / updated / deleted event across Apier's upstreams (Brønnøysund, Altinn schemas, DigDir policies, Norges Bank rates). Drive an incremental sync instead of re-fetching whole entities. HMAC-signed keyset cursors.read:changes

Authorisation

ToolTitleWhat it doesScope
check_authorizationAuthorisation snapshotYour delegation status on an org: granted vs missing scopes and the delegation chain.read:altinn
list_acting_capacityActing capacity for a personThe actions a person may take for an org, derived from their Altinn roles, each with a legal citation.read:altinn

Delegation (Fullmakt Rails)

ToolTitleWhat it doesScope
request_fullmaktRequest a fullmakt (broker a systembruker delegation)Write. Brokers an Altinn systembruker delegation for a company and binds the returned system_user_id onto your agent principal (write-once; a pending principal becomes active). Altinn returns a delegation_url the company's signing authority must approve before the delegation is usable. Mock-adapter-backed pending live partner validation. Supports Idempotency-Key.read:altinn
check_fullmaktCheck your fullmakt state for a companyRead. Which of your agent principals hold live delegated authority for the org — overall_status is full / partial / none, with fix_steps naming any blocker. Reports the delegation state Apier recorded when it brokered the fullmakt, not a live Altinn PDP decision. none is a valid 200, never a 404.read:altinn
revoke_fullmaktRevoke a fullmakt (retire a delegation + its principal)Write. Withdraws the delegation (append-only: a status='revoked' marker row supersedes the original) and flips the principal to terminal revoked. Idempotent — revoking an already-revoked principal is a 200 no-op. Named warning tokens distinguish partial outcomes. Supports Idempotency-Key.read:altinn

Actions

ToolTitleWhat it doesScope
validate_actionValidate a regulatory action (dry-run)Run a proposed filing through the five prerequisite checks with zero upstream side effects.read:actions
submit_vat_returnSubmit VAT return (sandbox)Sandbox-only MVA filing — preview without an approval token, or file a mock receipt with one. Never contacts a real government system. Not advertised in tools/list — call it by name.read:actions

Billing & credits

ToolTitleWhat it doesScope
get_pricingApier price list (keyless)The machine-readable price list: per-call cost in whole øre for every metered endpoint/tool, whether credit enforcement is live, the 402 recovery-contract shape, top-up bounds, and the how-to-pay guide URL. Keyless — executes without an API key, so an agent can price a workflow before it holds any credential. Call it before metered work.read:pricing
get_credit_balanceOwn prepaid credit balanceThe calling key's own remaining prepaid balance in whole øre, with updated_at and the top_up_url a human uses to fund the key. Takes no input — it can only ever return the authenticated key's own balance. Call it before a batch of metered calls to confirm the balance covers it. The backing REST route is deliberately scope-exempt; the MCP tool's read:credits is satisfied by the default read:* grant.read:credits

Onboarding

ToolTitleWhat it doesScope
redeem_issuance_tokenRedeem an owner-issued key-issuance token (keyless)Convert a one-time issuance token the account owner minted in the dashboard into the agent's own read:*-scoped API key. Keyless — the presented token IS the credential. Single-use and atomic; the plaintext_key is returned exactly once and never logged. Minting and revoking issuance tokens are deliberately dashboard-only (owner-issued security model) — there is no mint tool to look for.read:onboarding

The full headless onboarding journey for an agent that arrives over MCP with no credential: the account owner mints a one-time issuance token in the dashboard and hands it to the agent out-of-band → the agent calls redeem_issuance_token (keyless) and receives its own read:*-scoped API key exactly once → every subsequent call authenticates with Authorization: Bearer <plaintext_key> — starting with, say, get_credit_balance to confirm the account's balance before metered work (get_pricing needs no key at all).

Errors

ToolTitleWhat it doesScope
explain_compliance_errorExplain a compliance errorTurn any Apier error_code into a Norwegian-bokmål explanation with fix steps and, where applicable, a legal basis.read:rulebook

Keys carry a scopes array (default read:*, which grants any read: action). A call whose key lacks the required scope returns SCOPE_INSUFFICIENT. Reserved prefixes (write:, act:, delegate:) are not assignable — which is why submit_vat_return needs only read:actions: the human approval gate, not the agent's key, authorises the mock filing.

REST-only surfaces

The MCP server exposes the read and dry-run tools an agent needs to reason about Norwegian compliance. Some Apier products are deliberately REST-only — they have no MCP tool, by design. The two surfaces are not meant to be identical: everything an agent should call autonomously is an MCP tool; everything that needs an operator, a human decision, or a financial/legal side effect stays on REST.

  • Account management — creating and managing your own consumer account is an operator task behind a session, not an agent tool keyed on an API key.
  • Admin / key management — minting and revoking API keys is gated by an operator secret, a different trust tier from an agent's key.
  • Billing — subscription and usage endpoints are account-owner operations with financial side effects, kept off the agent surface.
  • Privacy / data-subject requests — GDPR erasure and export are identity-verified human workflows, never an autonomous agent action.
  • Sandbox rehearsal — the guided write-loop rehearsal is a stateful REST flow; agents reach the sandbox through the same read tools with a sandbox bearer instead.
  • Human-in-the-loop approval — approving or rejecting a pending binding action is the human gate that must sit outside the agent's own credentials (the reserved act: scope is never agent-assignable).

Next steps

Building with an LLM? Read llms.txt for agent-oriented integration guidance.