API Tools
zeno-works.ch
An MCP server that lets you discover, inspect, and call Zeno API operations while handling API-key signup and authentication.
ENDPOINT 1
https://app.zeno-works.ch/mcp
MCP server metadata
- Name
- zeno
- Version
- 0.1.0+d57624b8f
This server is Zeno, an accounting platform — bookkeeping, VAT, payroll, year-end, reconciliation, invoicing and reporting for Swiss fiduciary work. HOW TO WORK IN THESE BOOKS These are real books, not a scratchpad: bookings are expected to trace back to the document that justifies them, and the pipeline is what creates that link. This server hosts many tenants and your credentials decide which of them you are in — call whoami first to see the user you act as and the company, if any, your API key is confined to. 1. Let Zeno read documents; never transcribe one. To book an invoice, receipt, statement or contract, send the FILE (upload_document for an invoice or receipt, upload_intake for anything else) and let the analyzer code it. A journal entry you compose yourself carries no document. Copying figures out of a document into create_gl_journal_entry is always the wrong move, however reliable the figures look — attach_gl_journal_entry_document files the evidence beside the booking, but nothing derives the booking FROM it, which is the whole point of sending the file. Setting a company UP works the same way: analyze_document reads a closing balance sheet into the opening snapshot, extract_open_items an OP-Liste, extract_bank_account a statement's IBAN — the figures those return are what resolve_balance, propose_bank_mapping and the create expect, and typing them in by hand is the same mistake. Start a setup with onboarding_guide: it lists what to ask the user for, which documents to request, and the order those operations feed each other in. 2. Uploading is not the end of the job. What the coder is unsure about is HELD, not posted. get_work_list shows everything waiting; list_autopost_decisions (outcome="held") lists the proposals — page it with offset until a page comes back empty, never assume one call saw them all — and accept_autopost_decision books one. When a proposal cannot be accepted because its bank movement is already matched to the wrong document, unlink_item frees the movement. A company is only done when that queue is empty. Never reach for companies_create_company to set a company up: it makes a bare tenant with no chart of accounts, no fiscal year and no opening balances, and for an administrator it SUCCEEDS, so nothing refuses you and nothing warns you. 3. create_gl_journal_entry is the exception, for bookings with genuinely no document behind them — reclassifications, provisions, accruals, year-end adjustments. When something needs booking by hand and evidence DOES exist, there is a door that keeps them together: an ingested document is coded with code_gl_item, a bank movement with manual_code_entry, opening balances with generate_opening_entries then post_opening_entries. For an established company whose prior years closed outside Zeno and whose closing balance sheet exists ONLY as figures in the predecessor system (no document to upload), create_balance_sheet takes those closing balances as structured lines and is the sanctioned prerequisite for generate_opening_entries — that, never a hand-built create_gl_journal_entry, is the door for opening balances. When an entry was ALREADY booked by hand and its document turns up afterwards, the retrofit is create_gl_document then attach_gl_journal_entry_document (it works on a posted entry, in a closed period, and makes the VAT close stop reporting the entry as undocumented). That is a repair, not a shortcut past point 1. 4. Closing is a process, and Zeno derives it for you. get_close_overview lists every closable scope — VAT windows, GL periods, fiscal years (their close AND their opening), payroll runs and payroll years — oldest first, and names the one to start with. get_close_todo gives that scope's remaining work as coded items, each carrying what it means, why it matters and the operations that move it. Work the loop: read the list, run the operations the first item names, read it again. An item is not ticked off — it DISAPPEARS when the data is right, so the list re-read is the proof the repair landed. closing_guide spells the loop out. Close oldest first: a correction booked into a month you already closed has nowhere to go. Closing itself is consequential. A closed period refuses every posting dated inside it — including documents ingested afterwards — and the only way back is reopen_gl_period: an explicit accountant action that demands a written reason and goes on the permanent close ledger. Never reopen on your own judgement — only when the user asks for THAT correction — and re-close with gl_period_close_run when it is booked. Close only when the user asks to close THAT period, never to signal that you have finished. When the list's next_action IS the close, it says so (is_close_action), and gl_period_close_checks and get_work_list remain the lower-level reads behind it (close_gl_period is the same close under another name). new_status="soft_closed" still accepts postings; leftover drafts refuse a soft close too. Sealing a year is heavier still. 5. A whole company's history does not go through this connection. These tools carry files as base64, through your context, one at a time — fine for a handful, hopeless for a year of documents. For a backlog, connect a source once (search "storage ingest source" or "email account") or ask the user to drop the folder into Documents → Upload in the app, and then work the queue in point 2. Reconcile a bank by ingesting its CAMT.053 (or a PDF/CSV/Excel export) and settling the entries — never by writing the entries yourself. 6. Your work is recorded as yours. Everything you book or accept here is stamped MCP, not as the account owner acting in person, and the app shows it that way. Act accordingly: a decision the user has not actually made is not one to make on their behalf just because the tool will let you. MOST OF THIS SERVER'S OPERATIONS ARE NOT IN THE TOOL LIST. The listed tools are the common ones; several hundred more exist behind three meta-tools: find_operations(query|tag) - search every available operation (page with offset) describe_operations(names) - get an operation's arguments call_operation(name, args) - run it No result schemas are published in the tool list at all; when you need one in advance, ask for it with describe_operations(names, include_output_schema=True). Before telling the user that something is unsupported, search for it with find_operations. Payroll, VAT returns, expenses, fixed assets, year-end close, settings and integrations are all reachable that way. ## If you have no API key yet Do not stop and ask the user to go and find one. Ask them two things in one message — **the email address on their Zeno account, and whether they already have one** — and pass your own name as `client_name`, because that is the only place the consent screen can identify you; omit it and the person is asked to approve "an unnamed client". **Ask; do not guess.** The server cannot tell you whether an address is registered (that would be an account-enumeration oracle), and the person you are talking to knows. Guessing wrong is not an error you will see — it is a request that never resolves and expires. - They **have an account** (most people do — every current user was created by an administrator): call `request_api_key`. Tell them to open the plain URL it returns, sign in, and approve the request waiting there — we also email them a notice. `collect_api_key` is the only poll, and every answer carries `poll_interval_seconds` — wait that long between calls rather than spinning. **Do not ask the user to tell you when they are done**: you cannot detect it any other way, and a session that hands the wait back to the user is a session that never finishes. When it answers `ready`, follow the `note` on it first: the key is not in use until the client's configuration holds it and the client has restarted, and collecting again issues nothing. With `scope: onboarding` the user has no company yet and the key exists to make one: call `onboarding_guide` next — it needs no key, so read it during that wait. It is the only place that says which documents to ask them for, and asking for them together is the difference between one conversation and seven. There is no code to read out and none to type. If you find yourself about to say "tell me the code", you have misread this. - The user has **no Zeno account at all**: call `start_signup` with their email address. We email them a link; they open it, see who asked, choose a password, and decide whether to connect you. Then poll `collect_api_key` exactly as above. You never see the link and never learn whether that address was already registered — both are deliberate. Somebody signing up this way has no company either, so expect `scope: onboarding` and the setup that follows it.
Known tools 51
find_operationsSearch EVERY operation this server offers, including the ones not in your tool list.
Inferred read-onlydescribe_operationsGet the full argument schema for operations found via ``find_operations``.
Inferred read-onlycall_operationRun any operation by name — the ones in your tool list and the unlisted ones alike.
Inferred read-onlywhoamiWhich user this request acts as, and which company's books it is bound to (jwt or API key).
Inferred read-onlyglobal_searchFull-text search across emails + items, ranked by ``ts_rank_cd``, scoped (A1).
Inferred read-onlyget_kpi_reportSnapshot KPIs + 13-week liquidity forecast, from the GL (CompanyMember).
Inferred read-onlylist_autopost_decisionsRecent AI-accountant autonomy decisions (CompanyMember) — the activity feed.
Inferred read-onlyaccept_autopost_decisionOne-click accept of a **held** decision (accountant) — posts the AI's proposal.
Inferred read-onlyupload_intakeUpload ANY file and let the system decide what it is (the unified intake queue — the same door as "Documents → Upload" in the web app).
Potential side effectsgl_trial_balancePer-account debit/credit columns for one fiscal year, with totals.
Inferred read-onlygl_balance_sheetAssets vs liabilities + equity for one fiscal year, signed by natural side.
Inferred read-onlyget_gl_account_ledgerOne account's ledger (Kontoauszug) — every booking that touched it, in date order, with Soll/Haben and the running balance after each line (CompanyMember).
Inferred read-onlylist_gl_periodsThe company's **accounting periods** — the months a journal entry may post into (CompanyMember read).
Potential side effectsget_gl_journal_entryOne entry + lines, plus the subledger rows it produced/touched (Gap B detail surfacing): its ``gl_vat_posting`` rows, its ``gl_vat_zero_posting`` turnover-only declarations (zero-rated/exempt output — R2-13b: these have no VAT line to pair, so without them a correctly coded 0 % line is indistinguishable from an untagged one) and referenced ``gl_open_item`` rows.
Inferred read-onlyreverse_gl_journal_entry**Storno** a posted journal entry: post its mirror image (accountant role).
Potential side effectslist_bank_accountsThe company's bank accounts — the ``bank_account_id`` every statement import, bank-entry list and reconciliation call needs (CompanyMember read).
Inferred read-onlysettle_entryPost this reconciled bank entry into the GL (clean-bank settlement).
Potential side effectsget_ar_invoiceOne authored outgoing invoice with its lines (CompanyMember read).
Potential side effectscreate_ar_invoiceCreate an outgoing (sales) invoice as a **draft** — nothing is booked yet (accountant role).
Potential side effectslist_counterpartiesList a company's counterparties (filter by ``kind``/``q``, paginated).
Inferred read-onlylist_payroll_runsThe company's payroll runs — one per wage period — newest period first (CompanyMember read; the money is aggregate, no per-employee PII).
Inferred read-onlylist_payroll_employeesEvery employee on the payroll master data, with today's contract (accountant role — this payload is personal data, so it is not a plain member read).
Inferred read-onlyget_financial_statementsComputed balance sheet + P&L for a fiscal year, from the journal (CompanyMember).
Inferred read-onlycustomer_statementOne customer's open items + totals (the aging row's drill-down).
Inferred read-onlyzeno_infoWhat this server is, which MCP revision it speaks, and how to get a credential.
Inferred read-onlyzeno_overviewWhat Zeno does, in one page — the platform's capabilities and its limits.
Inferred read-onlyonboarding_guideHow to onboard and create a NEW company: what to ask the user for, which documents to request, and the order to run the setup in.
Potential side effectsCONNECT WITH APPROVAL
Client installation
Review this server and its permissions before adding it. Secret placeholders must be set locally.
Codex
~/.codex/config.toml
[mcp_servers.zeno]
url = "https://app.zeno-works.ch/mcp"
enabled = true
Claude Code
.mcp.json
{
"mcpServers": {
"zeno": {
"type": "http",
"url": "https://app.zeno-works.ch/mcp"
}
}
}
Claude Desktop
Settings → Connectors → Add custom connector
Name: zeno
Remote MCP URL: https://app.zeno-works.ch/mcp
Add this remote URL as a custom connector in Claude Desktop. Availability depends on the user plan and workspace policy.
Cursor
.cursor/mcp.json
{
"mcpServers": {
"zeno": {
"url": "https://app.zeno-works.ch/mcp"
}
}
}
Visual Studio Code
.vscode/mcp.json
Add to Visual Studio Code{
"servers": {
"zeno": {
"type": "http",
"url": "https://app.zeno-works.ch/mcp"
}
}
}
Generic MCP
Client-specific MCP configuration
{
"name": "zeno",
"transport": "streamable-http",
"url": "https://app.zeno-works.ch/mcp"
}
MCP Inspector
Run the official MCP Inspector locally and enter the indexed Streamable HTTP endpoint.
TRUST AND VERIFICATION EVIDENCE
Loading Trust v2 evidence…
Checking the associated registrable domain. The BuiltWith key remains server-side.
Evidence is source-attributed and does not guarantee that a third-party server is safe. Risk labels are conservative metadata heuristics.