Skip to main content
Glama
COMR4D3B451L

fortnox-automation-mcp

by COMR4D3B451L

Fortnox Automation and MCP

A small, dependency-free Python integration for Fortnox Open API and Model Context Protocol (MCP) clients.

This repository is designed for public review. It contains no Fortnox credentials, access tokens, refresh tokens, customer data, accounting exports, or production audit logs.

What it provides

  • A typed, injectable Fortnox HTTP client.

  • OAuth authorization URL helpers.

  • Bounded read tools for invoices and customers.

  • Receipt validation and balanced voucher previews.

  • Optional write tools behind two independent gates:

    • FORTNOX_ENABLE_WRITES=1.

    • A secret FORTNOX_APPROVAL_TOKEN that matches the per-call approval token.

  • Safe-read and preview tools through a stdio MCP server.

  • Pagination, bounded max_items, refresh-token rotation, retry limits, and structured Fortnox errors.

  • Offline tests with fake transports. Tests never call Fortnox.

Related MCP server: Fortnox MCP Server

Important safety boundary

This is an integration skeleton, not an accounting authority. It does not decide Swedish tax treatment, prove a business purpose, or replace an accountant.

Financial mutations require a separate review and explicit approval of the exact proposed operation. Do not enable write tools until the deployment has secure secret storage, an audit policy, source-document handling, and a tested approval workflow.

Fortnox OAuth scopes can grant write capability. Treat every granted scope as write-capable even when the application currently exposes only reads.

Requirements

  • Python 3.11 or newer.

  • No runtime dependency outside the Python standard library.

  • Fortnox developer application credentials for live OAuth/API use.

  • An MCP-capable host, such as Hermes, for MCP use.

Local setup

python3 -m venv .venv
. .venv/bin/activate
cp .env.example .env
python -m unittest discover -s tests -v

Keep .env outside version control. Use file mode 600 for local secret files:

chmod 600 .env

For a real deployment, prefer a secret manager or an environment file outside the checkout.

Configuration

The public template contains names only. Set these out of band:

FORTNOX_CLIENT_ID
FORTNOX_CLIENT_SECRET
FORTNOX_ACCESS_TOKEN
FORTNOX_REFRESH_TOKEN
FORTNOX_COMPANY_ID
FORTNOX_BASE_URL=https://api.fortnox.se
FORTNOX_ENV_FILE=./.env
FORTNOX_AUDIT_LOG=./data/audit.jsonl
FORTNOX_ENABLE_WRITES=0
FORTNOX_APPROVAL_TOKEN=<long random secret, only when writes are deliberately enabled>

The default is fail-closed: writes are not exposed unless FORTNOX_ENABLE_WRITES=1, and every write still requires the configured approval token. There is no built-in token such as APPROVED.

CLI

The CLI can create a plan without contacting Fortnox:

PYTHONPATH=. python -m fortnox_automation.cli plan "Show unpaid customer invoices"

MCP server

The server uses newline-delimited JSON-RPC over stdin/stdout:

PYTHONPATH=. python -m fortnox_automation.mcp_server

Register it with an MCP host using an absolute path and an environment file managed by that host. Do not put credentials in the MCP registration JSON. Example shape:

{
  "mcpServers": {
    "fortnox": {
      "command": "python3",
      "args": ["-m", "fortnox_automation.mcp_server"],
      "cwd": "/path/to/fortnox-automation",
      "env": {
        "FORTNOX_ENV_FILE": "/secure/path/fortnox.env"
      }
    }
  }
}

Start a new MCP client session after changing tool schemas or environment configuration.

Read tools

Available by default:

  • fortnox_list_invoices

  • fortnox_list_customers

  • fortnox_preview_receipt

List operations require an explicit max_items bound. The preview tool validates accounting arithmetic but does not write.

Write tools

Write tools are hidden unless FORTNOX_ENABLE_WRITES=1. When exposed, each write call requires an approval token supplied by the caller and checked with a constant-time comparison. The repository does not provide that secret.

Do not use a write call as a connectivity test. Verify a safe read first, present a complete preview, obtain approval, then execute one bounded operation and verify its Fortnox identifier.

OAuth

This repository contains only authorization URL construction. A production deployment must add a server-side callback that:

  1. Generates and stores a random state.

  2. Validates state with constant-time comparison.

  3. Exchanges the code server-side over HTTPS.

  4. Stores access and refresh tokens atomically with restrictive permissions.

  5. Handles refresh-token rotation.

  6. Never returns tokens, codes, or secrets in a browser response or log.

Use the exact redirect URI registered in the Fortnox developer portal. See docs/oauth-and-deployment.md.

API behavior

  • Safe reads may retry on 429 and server errors with bounded backoff.

  • Mutating requests are not automatically retried.

  • Refresh-token rotation is persisted only to the configured local secret file.

  • List operations support pagination and a caller-supplied maximum item count.

  • Fortnox error information is preserved in FortnoxAPIError.

  • Attachment uploads are limited to 50 MiB and are not automatically retried.

Repository security

Before every public push:

python3 scripts/scan_public.py
python3 -m unittest discover -s tests -v

The scanner checks the working tree for private keys, common credential formats, token assignments, and ignored sensitive files. It is a safety net, not a replacement for secret management or code review.

Never commit:

  • .env or secret files.

  • Tokens, client secrets, passwords, private keys, or cookies.

  • Fortnox customer, invoice, voucher, bank, or audit exports.

  • Local previews, attachments, spreadsheets, or generated reports.

  • Production MCP configuration containing environment values.

If a secret was ever committed, rotate it first. Removing it from the latest commit is not enough because Git history may retain it.

Documentation

License

MIT. See LICENSE.

Available Tools

3 tools
fortnox_list_customersC

List Fortnox customers (read-only)

ParametersJSON Schema
NameRequiredDescriptionDefault
paramsNo
max_itemsNo

TDQS

C2.4/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full behavioral burden. It asserts read-only behavior, which is useful, but says nothing about pagination, the max_items cap of 500, auth requirements, or the shape of the returned list.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The single phrase is front-loaded and free of waste, but its brevity stems from under-specification rather than disciplined conciseness. It is appropriately short for a simple list tool but omits information an agent needs.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no annotations, no output schema, an undocumented nested params object, and an unexplained max_items bound, the definition leaves an agent guessing about pagination and filtering behavior for a listing tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0% and the description adds no parameter meaning. The nested 'params' object is completely opaque, and max_items is undocumented in both schema and description.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb (List) and resource (Fortnox customers), so the agent knows exactly what the tool returns. It does not distinguish itself from siblings like fortnox_list_invoices beyond the resource name, but that distinction is inherent and sufficient.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no guidance on when to use this over alternatives, nor any prerequisites, pagination context, or filtering context. The parenthetical '(read-only)' hints at safety but not usage.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fortnox_list_invoicesC

List Fortnox invoices (read-only)

ParametersJSON Schema
NameRequiredDescriptionDefault
paramsNo
max_itemsNo

TDQS

C2.6/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full behavioral burden, and it only supplies '(read-only)'. It says nothing about pagination, default page size, auth/tenant requirements, or what the listing contains, which matters for a list endpoint with a max_items cap.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The single clause is front-loaded and free of filler. It is efficient, though its brevity tips into under-specification rather than tight completeness.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with an undocumented nested 'params' object, no output schema, and no annotations, the description is far too thin. An agent cannot determine what filters 'params' accepts or what the returned invoice records look like.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0% and the description adds no parameter meaning at all. The 'params' property is a nested object with no declared properties, and 'max_items' (with a 500 ceiling) is never explained, leaving both parameters opaque to the agent.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource ('List Fortnox invoices'), which is enough to distinguish it from fortnox_list_customers and fortnox_preview_receipt by noun alone. It stops short of explicitly naming those siblings or stating scope/filtering, so it is clear but not maximally differentiated.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no statement of when to use this tool versus fortnox_preview_receipt or fortnox_list_customers, nor any prerequisites or exclusion conditions. The '(read-only)' tag hints at a safe read but is not usage guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fortnox_preview_receiptA

Validate a receipt and build a balanced voucher preview; does not write to Fortnox

ParametersJSON Schema
NameRequiredDescriptionDefault
receiptYes

TDQS

A3.6/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full behavioral burden, and it does disclose the most important trait: this call is non-mutating. However, it says nothing about what happens on validation failure (error vs. partial preview), whether a preview token/state is returned, or any rate/permission constraints.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

One tightly packed sentence that leads with the action and ends with the critical non-write guarantee. No filler, no redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The core contract (validate + preview, no side effects) is conveyed, which matters most with no annotations and no output schema. But for a tool whose only input is an opaque nested object and whose output is a "balanced voucher," the description leaves both the input shape and the preview result undefined.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The single required parameter is a nested object with 0% schema description coverage, so the description should explain the receipt structure (line items, amounts, account codes) needed for a balanced voucher. It offers no parameter detail whatsoever, leaving the agent to guess the shape of the nested payload.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names a specific verb pair ("Validate", "build a balanced voucher preview") and the resource (a receipt), and the negative clause "does not write to Fortnox" makes the scope unambiguous. This clearly distinguishes it from the sibling list tools, which are plain read queries.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The phrase "does not write to Fortnox" implies this is the pre-commit dry-run step before actually booking a voucher, which is useful context, but no alternative tool or explicit trigger condition is named. Usage must be inferred rather than being stated.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 3 tool updatesv0.1.0
    • First observedfortnox_list_customers
    • First observedfortnox_list_invoices
    • First observedfortnox_preview_receipt

TDQS

B3/5.0

Scored across 3 tools

Disambiguation5/5

The three tools have clearly distinct purposes: listing invoices, listing customers, and previewing a receipt. There is no overlap in resource or action, so an agent can easily select the right tool.

Naming Consistency5/5

All tool names follow a consistent pattern with the same prefix and verb_noun structure: fortnox_list_invoices, fortnox_list_customers, fortnox_preview_receipt. The convention is predictable and readable.

Tool Count3/5

Three tools is on the thin side for a server named 'automation', which suggests broader workflow support. It is not critically low, but the surface feels under-scoped for the implied domain.

Completeness2/5

The server offers only read-only listing and one non-writing preview, while Fortnox automation would typically require creating, updating, or deleting invoices/customers and posting vouchers. Significant lifecycle operations are missing, which would cause agent failures for common automation tasks.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    Enables MCP-capable agents to read Fintaro invoices and transactions, and upload receipts, via a scoped API key with PII-safe projections.
    6
    Apache 2.0
  • A
    license
    A
    quality
    C
    maintenance
    An MCP server for integrating with the Fortnox Swedish accounting system. Enables LLMs to manage invoices, customers, suppliers, orders, accounts, vouchers, and provides business intelligence analytics.
    51
    71 npm
    40
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables MCP-compatible agents to securely access an Invoice4U account for searching documents and customers and creating receipts linked to paid invoices, with read-only behavior by default.
    1
    MIT
  • F
    license
    A
    quality
    C
    maintenance
    Enables Codex to securely access PowerOffice Go API v2 with read-only tools and write operations that require explicit confirmation for customers and general ledger accounts.
    10
    1
    -