KRONENWERK exposes a Model Context Protocol (MCP) server at https://kronenwerk.org/api/extern/mcp. An AI agent that speaks MCP over Streamable HTTP authenticates with OAuth 2.1 (Claude, ChatGPT and other hosted assistants) or with the same Bearer API key as the REST API, reads one company's customers, invoices, transactions and reports, and creates drafts, transactions and notes. Whether it may also issue an invoice, send it or record a payment is the company's decision: by default those remain a person's acts, and the company can allow them under Settings → AI assistants up to an amount limit it sets, with every act on the record. The MCP server is part of the Enterprise plan.
What MCP is
The Model Context Protocol is an open specification, published at modelcontextprotocol.io, that defines how an AI application (the client) discovers and calls tools offered by an external system (the server). Messages are JSON-RPC 2.0. The server publishes a list of tools with names, descriptions and JSON input schemas; the model reads those descriptions, decides what to call, and the client sends a tools/call request on its behalf. The result comes back as content the model can read and, optionally, as structured JSON.
Two transports are standardised: stdio for a server launched as a local subprocess, and Streamable HTTP for a server reached over the network. KRONENWERK is a network service, so it implements Streamable HTTP only. The protocol is versioned by date. As of 2026-09-03 the specification names 2026-07-28 as the current revision; earlier revisions (2025-11-25, 2025-06-18, 2025-03-26) open a connection with an initialize handshake, while the current revision carries the protocol version and client identity in a _meta object on every request and adds a server/discover method. A server may serve more than one revision on the same endpoint, and KRONENWERK does.
Endpoint, transport and authentication
Everything happens on one address with one HTTP method: POST https://kronenwerk.org/api/extern/mcp, body application/json, one JSON-RPC message per request.
- Authentication
- Either
Authorization: Bearer greif_oauth_…— an OAuth 2.1 access token, obtained through authorization code + PKCE at/oauth/authorizeand/oauth/token, with dynamic client registration at/oauth/registerand discovery under/.well-known/oauth-authorization-server(this is how Claude and ChatGPT connect: the company's owner signs in and consents once) — orAuthorization: Bearer greif_live_…/greif_test_…, the same API key the REST API accepts, created in KRONENWERK's settings and bound to exactly one company. A request without a valid credential is answered401withWWW-Authenticate: Bearer resource_metadata="…/.well-known/oauth-protected-resource", which MCP clients follow to start the sign-in. See authentication. - Tenant
- The company an agent reaches is the key's company. There is no organisation parameter anywhere in the protocol surface, so an agent cannot select or enumerate companies.
- Session
- None. The server keeps no session and issues no
MCP-Session-Id. Every request carries its own credential and its own protocol version.GET(the optional server-to-client stream) andDELETE(session termination) answer405 Method Not Allowed, which the specification designates for servers that do not offer them. - Protocol versions
2026-07-28(per-request_meta), and the handshake-based2025-11-25,2025-06-18and2025-03-26. A request that names another version receives JSON-RPC error-32022with the supported list indata.supported. A request that names no version at all is served as a legacy client.- Methods
server/discover,initialize,tools/list,tools/call,ping. No resources, prompts, sampling or notifications; the server never opens a stream. Batches are not accepted; each request is one message.- Origin
- A request carrying an
Originheader from another site is refused with403, as the specification requires against DNS rebinding. Non-browser clients send noOriginand are unaffected. - Rate limit
- The MCP endpoint sits inside the same filter chain as the REST API, so the key's budget of 240 requests, refilling at about two per second, applies to both together. Over budget, the answer is
429withRetry-After. See rate limits. - Plan
- A valid key whose company is not on the Enterprise plan receives
403with application error code1402and a sentence saying so. Plans are described on the pricing page.
For clients on the current revision, the server also checks the mirrored headers the specification introduced — MCP-Protocol-Version, Mcp-Method and, on tools/call, Mcp-Name — against the body, and answers a mismatch with error -32020 and HTTP 400. Clients built on a maintained MCP SDK do this automatically.
The tools
Twenty-two tools exist, in three classes: read tools, which change nothing; draft tools, which create something inert that a person still has to act on; and acting tools, which make a financial fact true and are offered only to companies that chose the level Act within a limit. tools/list returns only the tools the credential's scopes and the company's level permit; a key with invoices:read alone sees the invoice readers and nothing else, and a company at the default level never sees an acting tool. The scopes are the same ones the REST API uses.
| Tool | Class | What it does | Scope |
|---|---|---|---|
search_customers | read | Find customers by part of a name, number, town or VAT ID; exact containment, never fuzzy. | customers:read |
get_customer | read | One customer's master data: name, contact, address, VAT ID, currency. | customers:read |
list_invoices | read | Issued invoices, newest first, with totals, due dates and payment state; filters by state and issue date. | invoices:read |
get_invoice | read | One issued invoice by number or identifier: totals, outstanding amount, overdue and cancelled flags. | invoices:read |
get_invoice_draft | read | A draft as the books hold it: every line with the quantity, unit price, tax category and rate that were stored, plus the totals the issuing engine computes — or why it cannot be issued yet. | invoices:read |
list_transactions | read | The company's transactions (jobs and orders) with stage, customer and due date. | transactions:read |
get_transaction | read | One transaction by number or identifier. | transactions:read |
list_receivables | read | What customers owe, aged into overdue buckets, reconciling with the receivables control account. | reports:read |
list_payables | read | What the company owes suppliers, aged the same way. | reports:read |
get_profit_and_loss | read | Profit and loss for a period from the ledger, with an is_final flag that is true only when every period in the window is closed. | reports:read |
get_balance_sheet | read | Balance sheet on a date from the ledger. | reports:read |
get_business_attention | read | What is waiting on a person: outstanding, due soon and overdue in both directions, plus non-empty queues; books_open says whether the accounts have been opened at all. | reports:read |
create_invoice_draft | draft | A draft invoice for a customer, optionally with lines (quantity, net unit price and VAT rate as decimal strings). No number is spent, nothing is produced or sent. | invoices:write |
create_transaction | draft | A new transaction (job or order) with a title, optional customer, stage, description and due date. | transactions:write |
add_transaction_note | draft | Append a note to a transaction's timeline; changes no field. | transactions:write |
create_quote_draft | draft | A draft quote for a customer, optionally with lines, currency, the customer's reference and a transaction to list it under. A number is reserved; nothing is sent. | invoices:write |
record_bill | draft | A supplier's bill with its figures as printed — net, tax, gross, optional lines — and, optionally, the file it arrived as and a transaction to list it under. It awaits a person's confirmation; nothing is paid. | transactions:write |
attach_document | draft | Keep a file — a waybill, a purchase order, a supplier's quote — with a transaction, bill, customer or supplier. No figure changes. | transactions:write |
link_to_transaction | draft | List a quote, invoice draft, bill, expense or received document on a transaction's page. A cross-reference only. | transactions:write |
issue_invoice | act | Issue a draft: spend the number, freeze the document, create the legal record. Refused when the gross total is above the company's per-act limit; then nothing is issued and no number is spent. | invoices:write |
send_invoice | act | E-mail an issued invoice to the customer's address on record (or one given), with the payment link where the company has one. | invoices:write |
record_payment | act | Record that a customer paid an invoice: posts to the ledger and settles the receivable. Refused above the per-act limit. Accepts an idempotency key. | invoices:write |
Amounts are returned as a count of minor units with a currency code — {"minor": 105910, "currency": "EUR"} is 1,059.10 EUR — never as a formatted string. Dates are calendar days in YYYY-MM-DD. Every object a draft tool creates is recorded as created by that API key, and the company can see that attribution in the product. A model cannot pass off its work as a person's.
Levels and safety boundaries
The company, not the agent and not KRONENWERK, decides how far software may go. Under Settings → AI assistants the owner sets one of three levels, and the setting applies to every connected assistant and key alike:
- Read only — the twelve read tools. Drafts are switched off; a draft call answers with a sentence saying so.
- Read and propose (default) — read tools plus the seven draft tools. Nothing an agent does at this level is a financial fact; a person issues, sends and records.
- Act within a limit — adds
issue_invoice,send_invoiceandrecord_payment. Each single act is checked against a gross amount limit in the company's base currency, inside the act's own transaction, so an invoice that adds up to more than the limit is rolled back whole: no number spent, no record, no e-mail.
Whatever the level, no tool cancels or corrects an issued document, approves or pays a supplier bill, posts a journal, closes a period, or creates, rotates or revokes credentials and settings. Those stay with a person signed in to KRONENWERK.
When a model asks for an act the company has not allowed — record_payment at the default level, cancel_invoice at any level — the server does not answer "unknown tool", which a model reads as a naming problem and retries around. It answers with a normal result marked isError: true whose text states the rule, says that no part of the request was carried out, and names the setting a person would change. The model relays something true and stops.
Every act and every draft is written to the company's authorship record with the tool, the connection and the time, and the services beneath write their usual audit rows under the connection rather than under a person, so the trail always says which assistant did what. Other properties follow from the transport: reads are scoped by the same permissions as the equivalent screen; identifiers belonging to another company are reported as not found rather than as forbidden; internal failures are described in one fixed sentence with no stack trace, so table names and file paths never enter a model's context; and a connection can be revoked at any moment in Settings, which ends that agent's access on the next request. The same rules that govern the REST API apply — see API security.
Connecting a client
Most MCP clients accept a JSON configuration naming remote servers. A generic entry for KRONENWERK looks like this; the key names of the outer object vary by client, the inner fields do not:
{
"mcpServers": {
"kronenwerk": {
"type": "http",
"url": "https://kronenwerk.org/api/extern/mcp",
"headers": {
"Authorization": "Bearer greif_live_XXXXXXXXXXXXXXXXXXXXXXXX"
}
}
}
}
Use a greif_test_ key while building. It reaches the same endpoint in test mode for the company that created it; there is no separate sandbox host. Create the key with only the scopes the agent needs: an assistant that answers "who owes us money" needs reports:read and customers:read and nothing more.
Without a client, the endpoint can be exercised with curl. A legacy-style handshake and a tool call:
curl -s https://kronenwerk.org/api/extern/mcp \
-H "Authorization: Bearer greif_test_XXXXXXXXXXXXXXXX" \
-H "Content-Type: application/json" \
-H "MCP-Protocol-Version: 2025-11-25" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize",
"params":{"protocolVersion":"2025-11-25","capabilities":{},
"clientInfo":{"name":"curl","version":"1"}}}'
curl -s https://kronenwerk.org/api/extern/mcp \
-H "Authorization: Bearer greif_test_XXXXXXXXXXXXXXXX" \
-H "Content-Type: application/json" \
-H "MCP-Protocol-Version: 2025-11-25" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call",
"params":{"name":"list_receivables","arguments":{"as_of":"2026-09-03"}}}'
The result of the second call carries the report both as readable text in content and as JSON in structuredContent. A client on the current revision sends the same messages with the version in params._meta["io.modelcontextprotocol/protocolVersion"] and the mirrored headers; the server answers either form.
MCP or REST: which to use
Use MCP when a language model decides at run time what to ask. Use the REST API when your own code decides. The two surfaces read the same data under the same key and scopes, so nothing forces a choice, and a system may use both.
| MCP server | REST API | |
|---|---|---|
| Caller | An AI agent through an MCP client | Your application code |
| Address | /api/extern/mcp, unversioned; MCP carries its own version | /api/extern/v1 |
| Reads | Customers, invoices, transactions, receivables, payables, P&L, balance sheet, attention list | Customers, invoices, transactions, outstanding report, key info |
| Writes | Invoice draft (with lines), transaction, note; with the acting level: issue, send, record payment | Customer, invoice draft (customer only), transaction |
| Idempotency | Not applicable; the client retries under the model's control | Idempotency-Key header, required on POST |
| Events | None; the server never pushes | Signed webhooks |
The REST surface is described in the accounting API overview, the reference and the quick start. Application-level integration patterns are covered in connecting your SaaS.
How KRONENWERK handles this
SUPPORTED The MCP server is live at https://kronenwerk.org/api/extern/mcp for companies on the Enterprise plan, speaks Streamable HTTP for protocol revisions 2026-07-28, 2025-11-25, 2025-06-18 and 2025-03-26, authenticates with OAuth 2.1 or an API key, and offers the twelve read tools and seven draft tools to every company and the three acting tools to companies that allowed acting under Settings → AI assistants, each act bounded by the company's own limit and recorded. Cancelling, correcting, paying suppliers, posting, closing and configuring remain without any tool by design. The same server works with a greif_test_ key in test mode. The MCP server, the REST API, webhooks and the integration directory are part of the Enterprise plan; see pricing and the developer overview.
Frequently asked questions
Can an agent issue an invoice through the MCP server?
Only if the company allows it. At the default level it can create a draft, with lines, for a customer, and a person reviews and issues the draft in KRONENWERK, where it is validated as a structured e-invoice for the seller's country. If the owner sets the level Act within a limit, the agent may issue, send and record payments itself, each act up to the gross amount the company set; above it the request is answered with a written refusal and nothing is done.
Which MCP clients work with it?
Any client that implements Streamable HTTP and lets you set an Authorization header. The server accepts both the current per-request protocol revision and the older handshake-based ones, so clients built on either era connect.
Does the MCP server need its own API key?
No. It uses the REST API's keys and scopes. It is good practice to create a separate, narrowly scoped key per agent so that it can be revoked on its own.
Is there a sandbox?
A greif_test_ key reaches the same endpoint in test mode for the company that created it. There is no separate host.
Why does my client get 405 on GET?
The server offers no server-to-client stream and no session, so the optional GET stream and the DELETE session terminator answer 405, which the specification designates for that case. Clients fall back to plain POST responses.
What does the agent see about other companies I manage?
Nothing. A key is bound to one company; an agent with that key cannot list, select or reach any other. Multi-company setups use one key per company.
Sources
- Model Context Protocol — Versioning — read on
- Model Context Protocol — Transports (Streamable HTTP) — read on
- KRONENWERK developer documentation — read on