Jahresabschluss.io
DESign inRegister →

Codex + MCP

Bookkeeping with Codex: your German books over MCP

OpenAI Codex connects to the jahresabschluss.io MCP server with one entry in ~/.codex/config.toml: a [mcp_servers.buchhaltung] table with the server URL and bearer_token_env_var naming the environment variable that holds your company key. According to OpenAI's documentation, the Codex CLI, the IDE extension and the ChatGPT desktop app share that file, so one setup covers all three. Codex then reads the ledger, books receipts and bank lines and prepares VAT returns for you to file in the app — and with one setting it asks before every tool that changes something.

Last reviewed: 2026-10-19

Where Codex reads its configuration

Codex keeps MCP servers in config.toml — by default ~/.codex/config.toml, optionally a project-scoped .codex/config.toml in trusted projects. OpenAI states that the ChatGPT desktop app, the Codex CLI and the IDE extension share this configuration. ChatGPT on the web does not read local Codex files; it uses remote MCP tools through plugins.

Our server speaks Streamable HTTP and signs in with a static key per company. Codex sends that key as an Authorization header, read from an environment variable — nothing is installed locally, and the key never appears in the file.

Connect with one command

Put the key in your shell profile, then register the server:

# ~/.zshrc or ~/.bashrc — never in a repository
export JAB_API_KEY="jab_live_…"

codex mcp add buchhaltung \
  --url https://api.jahresabschluss.io/functions/v1/bookkeeping-mcp \
  --bearer-token-env-var JAB_API_KEY

codex mcp list

The key comes from the app: Bookkeeping → Settings → “AI agent & API”, created by an administrator and confirmed with the second factor. It belongs to exactly one company, carries the rights you tick — read always, write, send and finalize only if you choose them — and expires after 30 days, 90 days or one year.

Or write config.toml by hand

The same server, with approvals and a longer tool timeout:

# ~/.codex/config.toml
[mcp_servers.buchhaltung]
url = "https://api.jahresabschluss.io/functions/v1/bookkeeping-mcp"
bearer_token_env_var = "JAB_API_KEY"
# ask before every tool that is not marked read-only
default_tools_approval_mode = "writes"
# optional — the default is 60 seconds for each call
tool_timeout_sec = 120

# Alternative to bearer_token_env_var — our server also accepts X-API-Key:
# env_http_headers = { "X-API-Key" = "JAB_API_KEY" }

url and bearer_token_env_var are all a Streamable HTTP server needs. env_http_headers maps header names to environment variables; http_headers would hold static values, which is the wrong place for a key. enabled = false switches the server off without deleting the entry.

Approvals: Codex asks, the server checks

With default_tools_approval_mode = "writes", Codex prompts for every tool that is not marked read-only. Our server marks each tool with MCP annotations: reading tools carry readOnlyHint, tools that book, issue, lock or prepare a filing do not. A balance query runs straight through; a booking waits for your click. A per-tool approval_mode under [mcp_servers.buchhaltung.tools.<tool>] overrides the default for single tools.

The server adds its own guardrails whatever the client does. Its tool descriptions tell the agent to confirm recipient and amount before issuing an invoice; locking a period and confirming tax questions require confirm=true, to be set only after your explicit yes. Every write runs through the same booking core as the app — debit equals credit, reversal instead of deletion, no postings into locked periods — and text from receipts or bank references is treated as data, never as an instruction.

One server per job

Separate keys and allow lists keep each Codex setup narrow:

# Analysis only: a key created with read alone
[mcp_servers.buchhaltung_lesen]
url = "https://api.jahresabschluss.io/functions/v1/bookkeeping-mcp"
bearer_token_env_var = "JAB_READ_KEY"

# Tax preparation: a bookkeeping key with write, limited to the tax tools
[mcp_servers.buchhaltung_steuern]
url = "https://api.jahresabschluss.io/functions/v1/bookkeeping-mcp"
bearer_token_env_var = "JAB_TAX_KEY"
default_tools_approval_mode = "writes"
enabled_tools = [
  "get_entity_settings", "list_tax_deadlines", "list_tax_filings",
  "get_vat_return", "get_vat_supplementary", "check_tax_filing",
  "prepare_tax_filing", "request_tax_filing_approval", "get_tax_operation",
]

The key decides what the server allows; enabled_tools decides what Codex sees. A read-only key lists only reading tools, whatever the file says. Give finalize — the right to lock periods — only to the key that should lock. Up to 10 keys can be active per company, and each one shows up separately in the app's API usage of the last 30 days.

House rules in AGENTS.md

Codex reads AGENTS.md from ~/.codex and from the project root down to the folder you work in; a few lines are enough:

# AGENTS.md — bookkeeping
- Never print, log or store the API key.
- Before any write tool: say what will be booked, on which accounts,
  and wait for my yes.
- Correct with storno_journal_entry; never try to delete anything.
- Tax filings: check_tax_filing, then prepare_tax_filing, then give me
  the approval link. Never ask for my ELSTER PIN.
- Answer in English; quote German account and field names as they are.

Instructions guide the model; the hard limits remain the key's rights and the server's checks.

What to ask Codex

  • “Show the 2026 trial balance and flag unusual balances.” — get_saldenliste, then list_journal_entries for the accounts it flags.
  • “Import the September CAMT.053 file in ./bank and match payments to open invoices.” — list_bank_accounts, import_bank_statement, list_open_items, match_bank_transaction; every write waits for your approval.
  • “Upload the PDFs in ./belege and book what the Autopilot leaves open.” — upload_receipt, get_receipt_proposal, confirm_receipt.
  • “Prepare the Q3 VAT return and give me the link.” — get_vat_return, get_vat_supplementary, check_tax_filing, prepare_tax_filing, request_tax_filing_approval.
  • “Export the 2025 journal as CSV for my tax adviser.” — export_bookkeeping with format journal.

Custom GPTs are ending: the OpenAI route to your books

OpenAI retires custom GPTs on 11 December 2026, and their Actions are not migrated, so a GPT that called our REST API stops working on that date. Custom MCP servers in ChatGPT on the web sign in with OAuth, without authentication or mixed; our server uses a static key per company and does not offer OAuth yet, so ChatGPT on the web cannot connect for now.

Codex is the OpenAI route that works with your key today — in the terminal, in the IDE extension and in the ChatGPT desktop app, all from the same config.toml. Scripts and your own agents use the REST API with the same key.

What stays in the app

  • Creating API keys, with the second factor.
  • The bank connection, company settings and the IBAN/BIC of business partners.
  • Filing with ELSTER: Codex prepares and checks, you file through the approval link with your PIN.
  • Preparing and issuing the annual financial statements.

Frequently asked questions

How do I add an MCP server to Codex?

Run codex mcp add with --url and --bearer-token-env-var, or add a [mcp_servers.<name>] table with url and bearer_token_env_var to ~/.codex/config.toml.

Do the Codex CLI and the IDE extension need separate setups?

No. According to OpenAI, the ChatGPT desktop app, the Codex CLI and the IDE extension share the same config.toml, so one entry serves all three.

How do I make Codex ask before it books anything?

Set default_tools_approval_mode = "writes" for the server. Codex then prompts for every tool not marked read-only; our server marks all reading tools that way.

Does ChatGPT on the web work with this MCP server?

Not at the moment. ChatGPT on the web connects custom MCP servers with OAuth or without authentication, and our server uses a static company key. Codex works today.

Can Codex file my VAT return?

It prepares it: figures, official ELSTER check, frozen values and an approval link. You file in the app with your second factor and ELSTER PIN.

Can I use different keys for different tasks?

Yes. Register several servers in config.toml, each with its own environment variable — for example a read-only key for analysis and a write key limited by enabled_tools to the tax tools.