The MCP server
Endpoint
| Endpoint | https://mcp.ledger17.com/mcp |
| Transport | Streamable HTTP |
| Authorization server | https://auth2.sirktek.com |
Authorization
The server is an OAuth2 protected resource — it issues nothing and stores nothing.
Every request must carry a bearer JWT, which the server validates (issuer, signature,
expiry, audience) before any tool runs. The token is then forwarded verbatim to the
command and query APIs together with the resolved tenant as X-Tenant-ID.
Permissions are enforced upstream, exactly as for direct API calls: reads require
{orgId}:read:ledger, writes require {orgId}:write:ledger — see
Authentication. A 403 from the APIs is
surfaced to the model unchanged. The MCP server adds no permission logic of its own.
A client does not need the authorization server configured manually — it discovers it from the protected-resource metadata:
https://mcp.ledger17.com/.well-known/oauth-protected-resource
A rejected call answers 401 with a WWW-Authenticate header pointing at that same
document.
Which tenant a call acts in
The tenant is resolved from the token’s organization claim:
- One membership — a user who belongs to a single organization, or an API key (always bound to one) — is used automatically. Nothing to configure.
- Several memberships — tools then require an explicit
organizationIdargument; the assistant lists the options withorganization_listand lets you choose.
An explicitly requested organization is always forwarded as given — membership is the ledger APIs’ verdict, and their refusal is shown unchanged.
Tools
| Tool | Upstream | What it answers |
|---|---|---|
organization_list | (token only) | which ledger tenants the caller may act in |
balance_summary | query GET /balances/summary | trial balance: opening / movement / closing per account |
balance_buckets | query GET /balances/buckets | balances bucketed inside an outer period (trends) |
transaction_lines | query GET /transactions | general-ledger drill-down: individual debit/credit lines |
subledger_rollup | query GET /subledger | subledger per customer / supplier / employee / asset |
party_exposure | query GET /parties/{id}/exposure | one party’s receivable / payable / net |
report_list | query GET /reports/{domain} | which reports a domain offers |
report_generate | query GET /reports/{domain}/{name} | income statement, balance sheet, årsregnskap |
transaction_record | command POST /transactions | writes: record balanced transaction blocks |
transaction_correct | command POST /transactions/{id}/corrections | writes: compensating reversal of a recorded block |
rule_sets | command GET /rule-sets | registered domain rule sets |
The admin API is deliberately absent — an agent can keep the books, not administer the tenant.
All amounts are integer minor units (øre for NOK), debit-positive — the server’s instructions state this up front, because it is the single most damaging thing for a model to get wrong in a ledger. See Core concepts.
Freshness and timeouts
Nothing is cached — what the assistant reads is the ledger as it stands right now. Calls forwarded to the APIs time out after 10 seconds.