Zeno MCP Server
ch.zeno-works/zenov0.1.0
AI bookkeeper for Swiss small companies: books invoices, reconciles the bank, keeps VAT current.
context tax
~17,658 tok/turn51 tools
security
pending
cold start
hosted
freshness
Active8d ago
Our reading of Zeno, from the data
Zeno is an MCP server, listed in the official MCP Registry.
Its 51 tools add about 17,658 tokens to every model turn (measured 2026-10-19).
Maintenance is active: the last public signal was 8 days ago (2026-10-11).
It runs as a hosted endpoint, so there is nothing to install.
Install Zeno MCP server
Install in Claude Code
claude mcp add --transport http zeno https://app.zeno-works.ch/mcpInstall in Cursor
{
"mcpServers": {
"zeno": {
"url": "https://app.zeno-works.ch/mcp"
}
}
}Install in Claude Desktop
{
"mcpServers": {
"zeno": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://app.zeno-works.ch/mcp"
]
}
}
}Or add it as a custom connector under Settings → Connectors. The config above bridges via mcp-remote.
Install in VS Code
{
"servers": {
"zeno": {
"type": "http",
"url": "https://app.zeno-works.ch/mcp"
}
}
}Install in Windsurf
{
"mcpServers": {
"zeno": {
"serverUrl": "https://app.zeno-works.ch/mcp"
}
}
}Remote endpoints
- streamable-http
https://app.zeno-works.ch/mcp
Context tax
Connecting this server adds ~17,658 tokens of tool definitions to every model turn (bloated, 51 tools). Measured 2026-10-19 via its hosted endpoint; o200k tokenizer, worst case for clients that load all tools up front. How we measure
| Model | Per 1,000 turns | With prompt caching |
|---|---|---|
| Claude Opus 5.5 | $70.63 | $3.53 |
| Claude Sonnet 5.5 | $35.32 | $3.53 |
| Claude Haiku 5.5 | $1.77 | — |
Input prices as of 2026-10-06. Excludes your own prompt and output.
Tools (51)
create_gl_journal_entry1,373 tokCreate a **draft** journal entry by hand — accountant. It carries NO source document unless you name one. Only the pipeline paths stamp ``source_ref_type``/``source_ref_id`` (an ingested document is ``'item'``), so by default a booking made here cannot be traced back to what justified it. ``document_id`` is the retrofit: name an existing ``gl_document`` (create one with ``POST …/gl/documents``) and it is attached as the entry's source document, so the entry is not evidence-less. The same attach is available afterwards at ``POST …/gl/journal-entries/{id}/documents`` — including on a posted ent
create_ar_invoice1,049 tokCreate an outgoing (sales) invoice as a **draft** — nothing is booked yet (accountant role). A draft changes no ledger balance, gets no invoice number and appears in no VAT return; it exists so it can be edited. Posting happens later, at ``POST …/ar/invoices/{invoice_id}/issue``. Body: ``issue_date`` (required), ``counterparty_id`` (the customer — required before it can be issued), ``currency``/``language``, ``due_date`` (filled from the customer's payment terms when omitted), ``doc_type`` (``invoice``, the default — a ``credit_note`` draft can never be issued: credit an issued invoice with
list_items1,000 tokList/filter items, scoped to the caller's member companies (A1). Optional ``period`` (``YYYY-MM``) filters by the item's effective date (invoice date, falling back to the Europe/Zurich-local ingestion date); a malformed value returns 422 ``{"msg": "period must be YYYY-MM"}``. ``exclude_triage_status`` drops items carrying that triage verdict (NULL-safe) — the Payables/Receivables pages pass ``unrelated`` so relevance-parked items show only in the Inbox (pipeline §"Relevance gate"). Optional ``q`` is free text: a case-insensitive **partial (substring)** match over the filename and the counter
collect_api_key617 tokCollect the key once the user has approved. `status` is one of: - `pending` — nobody has approved yet, or (with `throttled: true`) we were rate-limited and learned nothing. **Wait `poll_interval_seconds`, then call again** — every answer carries it. Do not ask the user to tell you when they are done: you cannot detect it any other way, and handing the wait back to the user is how these sessions die. - `ready` — `api_key` is the credential. **Follow the `note` that comes with it before anything else**: the key does nothing until the client's configuration holds it, and calling aga
list_autopost_decisions595 tokRecent AI-accountant autonomy decisions (CompanyMember) — the activity feed. Shows what the autonomy policy posted (``posted``), would have posted in ``assist`` shadow mode (``shadow``), or held for human review (``held``). ``ai_no_match`` shadow rows (one per AI-consulted bank entry with no verdict) are feed noise and hidden by default; ``include_no_match=true`` surfaces them for audit. ``outcome`` / ``source_ref_id`` narrow server-side (ZET-75), so the held-review surfaces can see held rows beyond the newest-``limit`` window. Newest first, ordered ``(created_at DESC, id DESC)``. ``limit``
whoami498 tokWhich user this request acts as, and which company's books it is bound to (jwt or API key). Zeno serves every tenant from one host, and an API key is opaque — nothing else tells a connected agent whose books it is about to write. Call this FIRST when working over MCP: ``company_scoped: true`` means this connection's data access is confined to ``company_id`` — ``company_name`` names it (null here means the bound company no longer exists), no other tenant is reachable, and the admin endpoints are refused however privileged the owner is. ``is_admin`` is that owner's bit: worth nothing outside th
list_bank_entries464 tokList Bank Entries
post_gl_journal_entry451 tokPost a draft entry into the books — accountant. One-way. A posted entry can no longer be replaced or discarded (both refuse with 409); the only general correction is ``…/reverse``, which leaves both the original and the reversal visible. The one exception is ``PUT …/guessed-account``, which re-points a ``coding_guessed`` entry's expense account in place — and it works *only* on a posted entry. Check the draft before posting rather than after.
list_payroll_runs445 tokThe company's payroll runs — one per wage period — newest period first (CompanyMember read; the money is aggregate, no per-employee PII). A run walks ``draft`` → ``calculated`` → ``approved`` → ``posted`` → ``paid`` → ``locked`` (plus ``reversed``); ``run_kind`` separates a ``regular`` run from a correction/reversal, which are always **separate runs**, never in-place edits, and are linked by ``original_run_id`` / ``reversed_by_run_id``. ``journal_entry_id`` is the wage entry it posted (``null`` before ``posted``), and the lifecycle stamps (``calculated_at``, ``approved_at``, ``posted_at``, ``
get_financial_statements437 tokComputed balance sheet + P&L for a fiscal year, from the journal (CompanyMember). Aggregates every posted GL journal line whose **posting date** falls in the fiscal year's window, classifies each account against the chart of accounts, and reports problems (unbalanced trial balance, unclassified accounts, year-not-opened, mixed currency). **Read-only and distinct** from the uploaded balance-sheet snapshots (``GET .../balance-sheets``): nothing here reads a snapshot. ``fiscal_year`` optional → the latest FY with journal data. ``month`` (``YYYY-MM``, inside the FY — **422** otherwise) narrows th
accept_autopost_decision407 tokOne-click accept of a **held** decision (accountant) — posts the AI's proposal. A person's accept in the app replaces the soft guardrails (confidence, amount cap, risk flags). An agent's (MCP, API key, chat) is refused (403) unless the row is ``agent_acceptable``. Closed period, sealed year, unknown account: 409/422. The row flips ``held → posted`` (a second accept is a 409), with ``accepted_by_kind`` the kind of actor (``mcp`` over MCP). The optional ``body``, omitted otherwise: ``expense:receipt:item:`` needs ``employee_id`` + ``category_id``; ``autopost:capitalize:`` needs ``fixed_asset_c
list_gl_journal_entries395 tokList journal entries. ``period_ids`` narrows to specific fiscal periods, and is how a caller asks for **one fiscal year** — the year's period ids (ZET-285). Without it this route listed, and counted, every fiscal year the company has: the Journal page cut the year out of the fetched page *client-side* while ``total`` stayed company-wide, so a year whose entries sat past the first page rendered as "no entries match" with no way to reach them. The service applies the filter before the count, so ``total`` is the filtered total and the pager is the year's. ``untagged_subledger_control=true`` nar
find_operations378 tokSearch EVERY operation this server offers, including the ones not in your tool list. The tool list you can see holds only the common operations. Several hundred more — payroll, VAT returns, year-end close, fixed assets, expenses, cash book, settings, integrations, onboarding — are available but unlisted, and this is how you find them. **Search here before concluding that Zeno cannot do something.** Call with no arguments to browse the catalogue of areas (tags) with their operation counts; with ``tag`` to list one area; with ``query`` to search by keyword. **Every word contributes and matchin
request_api_key353 tokAsk the user to hand this session a Zeno API key. **Use this first** — every current Zeno user already has an account. `email` is the address on their Zeno account. **Ask them for it**; you cannot guess it, and the request is aimed by it — Zeno will only show it to somebody signed in as the owner of that address. Pass `client_name` too: it is the only thing identifying you on the approval page, and without it the person is asked to trust "an unnamed client". Returns a plain `verification_uri`. Tell the user to open it, sign in, and approve the request waiting there; we also email them a noti
issue_ar_invoice338 tokIssue a draft — **this is the booking step** (accountant role). In one transaction it assigns the next invoice number, posts the journal entry (debit the AR control account, credit revenue per line, credit output VAT per VAT code), opens the receivable open item the customer's payment will settle, freezes the invoice against further edits (``status`` → ``issued``) and renders the PDF+QR snapshot. The turnover reaches the VAT return from here — a draft never does. Only a **draft** can be issued: re-issuing an issued invoice is a 409. It is also refused when the invoice is not ready (no custom
gl_profit_and_loss322 tokRevenue − expense → net result, for one fiscal year. **Window** — same rules as the trial balance: ``fiscal_year`` defaults to the latest posted year, ``period_id`` narrows to one period of it, unknown values are 404 and a contradictory pair 422. A P&L is a **flow** statement, so no reading makes the sum of every fiscal year a company's result — hence no all-years mode (ZET-155). Opening batches post only to balance-sheet accounts, so they never enter this figure. **The cut** — by the line's stamped ``fiscal_year_id`` FK, not by posting date; the response's ``fiscal_year_cut`` says so (see t
list_gl_open_items315 tokOne page of the AR/AP subledger (member read). ``direction`` filters server-side (ZCT-577) — omit it for both. The screen used to filter the fetched page in the browser, which made the AR chip drop rows and the total count both directions. ``as_of`` (ZCT-577) reconstructs each item's open amount from posted journal lines dated on or before that date, instead of reading today's cached amount. Items are considered **regardless of their current status** — one settled since that date was open on it — so ``status`` is refused alongside it rather than silently ignored: a filter that appears to app
list_gl_periods303 tokThe company's **accounting periods** — the months a journal entry may post into (CompanyMember read). A period belongs to one fiscal year and carries ``period_no`` (1..12 within the year), ``date_from``/``date_to`` and a ``status``: ``open`` accepts postings, ``soft_closed`` still accepts them, and ``closed``/``year_closed`` refuse them — a posting date inside a closed period fails until an accountant explicitly reopens the period (``POST …/gl/periods/{period_id}/reopen``, reason required, recorded in the close-run ledger — ZET-161). ``fiscal_year_id`` narrows to one year; omitted, every year
get_bank_status299 tokComposed bank snapshot — "is my banking up to date?" (CompanyMember). "Reconciled" means entries matched, NOT ledger = statement balance (the close check ``BANK_BALANCE_DOES_NOT_TIE_OUT`` compares that). One call for what otherwise takes ``GET …/bank-accounts`` + one ``GET /bank-accounts/{id}/coverage`` per account + ``GET …/reconciliation/summary`` — and which those three still do not answer: per-account entry-status counts, the ``in_suspense`` counts, each account's latest closing balance, and the **company-level cumulative GL Suspense balance** (elsewhere reachable only through a VAT perio
get_gl_account_ledger291 tokOne account's ledger (Kontoauszug) — every booking that touched it, in date order, with Soll/Haben and the running balance after each line (CompanyMember). No fiscal-year parameter: the chart is FY-owned, so ``account_id`` already fixes the year. Posted **and** reversed entries are included (a reversal is a second posted entry, so both legs must show or the balance is wrong); drafts are not. The running balance is carried across pages by the server — ``page_opening_balance`` enters this page's first row, while ``closing_balance``/``total_debit``/``total_credit`` describe the whole window. 404
list_payroll_employees289 tokEvery 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). One row per employee, active and left alike (filter on ``status`` / ``employment_end`` yourself — no server-side filter, no paging): master data (``employee_no``, ``display_name``, address, ``ahv_number``, ``birthdate``), the Quellensteuer election (``qst_liable`` + canton/tariff), the salary ``iban``, and ``current_contract`` — the contract valid **today**, which is where the wage and workload live (``null`` when none is in force). ``readiness
list_counterparties286 tokList a company's counterparties (filter by ``kind``/``q``, paginated). ``kind`` repeats to filter on a set (``?kind=customer&kind=both``) — a role-shaped picker asks for every kind that plays the role. A single ``?kind=supplier`` keeps its original meaning.
reverse_gl_journal_entry284 tok**Storno** a posted journal entry: post its mirror image (accountant role). Nothing is deleted; the original stays, linked both ways, and the **reversal** entry is returned. The only VAT-correct storno door — VAT postings, open items and what the document spawned are all unwound. ``body.posting_date`` dates the reversal and decides its period; omitted, it reuses the original's date and period. Refusals, in evaluation order — **409** when: the entry is itself a reversal (post a new correcting entry instead); the reversal date falls in a ``filed``/``paid`` VAT period (date it into an open one
gl_balance_sheet267 tokAssets vs liabilities + equity for one fiscal year, signed by natural side. Window rules as the trial balance (``fiscal_year`` defaults to the latest posted year; no all-years mode, ZET-155). With ``period_id`` this is a *movements* aggregate over that period, not a position. ⚠️ Balances as ``assets == liabilities_and_equity``, NOT ``liabilities + equity``: the GL has no P&L→equity closing entry, so ``equity`` is the equity accounts alone and ``net_result`` is folded into ``liabilities_and_equity``. It is a *position* only for a year whose book carries its opening batch; otherwise it is that
get_ar_invoice263 tokOne authored outgoing invoice with its lines (CompanyMember read). Carries the header (customer, dates, currency, discounts), every line with its computed ``net_amount``/``vat_amount``/``gross_amount``, the document totals, and the lifecycle: ``status`` (``draft`` → ``issued`` → ``cancelled``), ``document_no`` (assigned at issue — ``null`` on a draft), ``commercial_document_id`` / ``snapshot_document_id`` (the booked document and the frozen PDF), plus the derived ``payment_status`` and ``open_amount`` read from the linked open item. This is the **authored** arm only. A receivable that arrive
global_search257 tokFull-text search across emails + items, ranked by ``ts_rank_cd``, scoped (A1). ``archive_status`` constrains item results only (the Documents → Archive page passes ``archived`` so its search box never surfaces pending/triage-parked items).
upload_intake244 tokUpload ANY file and let the system decide what it is (the unified intake queue — the same door as "Documents → Upload" in the web app). ``file_base64`` is the file's raw bytes, base64-encoded. Prefer this whenever the file is not plainly an invoice, and always for: bank statements (CAMT.053 XML, CSV, PDF or Excel exports) — these become BankStatement + BankEntry rows, matched to the registered bank account by IBAN; journal/accounting exports; and balance sheets, P&L statements and VAT filings. Mixed .zip archives are expanded and routed per file. Returns 202 with the queue row: routing happe
list_bank_accounts244 tokThe company's bank accounts — the ``bank_account_id`` every statement import, bank-entry list and reconciliation call needs (CompanyMember read). Oldest first, no filter and no paging. Each row carries the ``iban``, ``currency``, ``label``/``bank_name`` and ``ledger_account_number`` — the chart code this account posts through; **without one a bank movement cannot be coded**, and it is set with ``PUT /bank-accounts/{account_id}``. ``auto_created`` marks an account inferred from an imported statement rather than declared by a human; it starts ``ownership_status`` unconfirmed with ``needs_owner
gl_trial_balance238 tokPer-account debit/credit columns for one fiscal year, with totals. ``fiscal_year`` (year label) defaults to the latest year posted in; ``period_id`` narrows to one period of it. No all-years mode — account identity is ``(company, fiscal year, code)``, so aggregating double-counts silently (ZET-155). Unknown year/period → 404; period outside the year → 422. Cut by the line's stamped ``fiscal_year_id``, not its posting date; ``…/financial-statements`` cuts by posting date inside the same year's stored window, so the two differ only on a line dated outside the year it is stamped to. ``fiscal_ye
list_ar_invoices236 tokList authored AR invoices, newest first. Optional ``status`` / ``doc_type`` filter by lifecycle + document kind. Optional ``period`` (``YYYY-MM``) filters by the invoice's issue date (its month); a malformed value returns 422 ``{"msg": "period must be YYYY-MM"}``.
get_gl_journal_entry233 tokOne 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. A declared base line also carries the resolved code on ``lines[].vat_code``. Drafts carry empty lists. ``documents`` carries the accounting documents explicitly attached to the entry (``POST …/documents``) — the evidence for a hand-boo
start_signup229 tokCreate a Zeno account for somebody who does not have one yet. Use `request_api_key` first if they might already have an account — this tool cannot tell you, and deliberately never will. We email `email` a verification link. The user opens it, sees that you asked (by the name in `client_name`, shown as an unverified claim), chooses their own password, and decides separately whether to connect you. **You never see the link**, and you must not ask for it: it is what proves they own the mailbox, and a link you could repeat is a link anyone could. Then poll `collect_api_key` with the `claim_toke
describe_operations212 tokGet the full argument schema for operations found via ``find_operations``. Returns each operation's description and JSON-Schema arguments — everything a listed tool would have shown you. ``include_output_schema`` is the ONLY way to obtain an operation's result shape: ``tools/list`` publishes no output schema for any operation, listed or not (they are 67 % of the payload, and a schema describing a *result* is not what you need to *choose* a tool). Ask for it when you must know the shape in advance; the result itself comes back as JSON either way, so usually you do not. Up to 10 operations pe
upload_document212 tokUpload an INVOICE or RECEIPT (PDF, image, or .zip of them) for analysis. ``file_base64`` is the file's raw bytes, base64-encoded. ``direction`` is ``payable`` (incoming supplier invoice) or ``receivable`` (outgoing). Creates Item(s) and queues extraction. This door is invoices-only: it never routes a file onward by type, so a bank statement, a journal export or a balance sheet sent here is analysed and then just sits as a document. For anything that is not an invoice — or when you are not sure what the file is — use ``upload_intake``.
onboarding_guide210 tokHow 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. Discovery does not depend on search: this tool is in the tier, so it is listed for every caller, and `BASE_INSTRUCTIONS` names it. `find_operations` does reach it ("new company setup" ranks it first), but a query made only of common words is still decided by whichever tool happens to contain them. **Read this before you start a company setup**, and before you tell a user that something cannot be done here. It answers the questions the create's own schema cannot: w
reconciliation_summary175 tokReconciliation summary banner. Optional ``from``/``to`` scope the **per-entry bank-entry counts** (total / matched / missing-invoice / …) to a ``booking_date`` window — mirroring the ``/bank-entries`` grid filter — so the banner agrees with a month-filtered table. The cross-month coverage (``bank_accounts`` gaps / ``missing_periods``) stays **global**: gap detection spans statements regardless of the selected month.
get_kpi_report166 tokSnapshot KPIs + 13-week liquidity forecast, from the GL (CompanyMember). Cash/liability balances aggregate the FY window of posted/reversed journal lines; aging + forecast read ``gl_open_item``, pending salary batches and unpaid VAT periods. Read-only. Explicit non-member ``company_id`` → 403. ``fiscal_year`` optional → latest FY with journal data; FY figures as of that year's end once past. Aging and forecast anchor to today.
call_operation137 tokRun any operation by name — the ones in your tool list and the unlisted ones alike. ``name`` is an operation name from ``find_operations``; ``arguments`` is the object described by ``describe_operations`` (omit it for an operation that takes none). The result, and any error, are exactly what calling the operation directly would give you.
settle_entry133 tokPost this reconciled bank entry into the GL (clean-bank settlement). Matched → Dr Bank / Cr AR; unmatched → Dr Bank / Cr Suspense; a previously-suspensed entry now fully matched → Dr Suspense / Cr AR. Idempotent, and a no-op (``skipped``) when the GL chart is not configured for native settlement. The matcher still owns ``reconciliation_status``. See doc/architecture.md §"Posting general ledger".
customer_statement118 tokOne customer's open items + totals (the aging row's drill-down). ``as_of`` mirrors ``GET …/ar/aging`` — set, it reconstructs the items open at that date so the statement agrees with a historical aging report.
list_gl_accounts117 tokList the postable chart (CompanyMember). ``fiscal_year`` filters to one year's chart (the chart is FY-scoped, so codes repeat across years).
zeno_info78 tokWhat this server is, which MCP revision it speaks, and how to get a credential. **Needs no credential.** Call it first if a tool has just refused you: the `authentication` block names the tools that can get this session connected.
zeno_overview77 tokWhat Zeno does, in one page — the platform's capabilities and its limits. **Needs no credential.** Read this before telling a user that Zeno cannot do something; it covers VAT, payroll, reconciliation and the AI accountant.
get_intake54 tokGet one intake row by id (scoped to its company: 404/403 split).
get_company53 tokGet one company (jwt, scoped). 403 for a non-member non-admin, 404 if absent.
get_work_list45 tokEverything waiting for this user in this company, most severe first.
list_gl_vat_periods41 tokList Gl Vat Periods
list_companies39 tokList companies (jwt, scoped). Non-admins see only their member companies.
get_item38 tokItem metadata (any source).
get_bank_entry36 tokGet Bank Entry
entry_candidates34 tokEntry Candidates
Freshness
Active — last maintenance signal 8d ago. The newest of the signals below sets the band.
Last commit (default branch)
no data · GitHub
Latest release
no data · GitHub
Package published
no data · npm/PyPI
Registry entry updated
2026-10-11 · 8d ago · official registry · v0.1.0
FAQ
›How do I install the Zeno MCP server in Claude Code?
Run: claude mcp add --transport http zeno https://app.zeno-works.ch/mcp. For Cursor, VS Code, Claude Desktop and Windsurf, use the install tabs above.
›Does Zeno require an API key?
No required environment variables are declared in its published metadata.
›Can I use Zeno as a remote (hosted) MCP server?
Yes. It is a hosted MCP server; connect to its URL with any client that supports remote MCP.
›Is Zeno in the official MCP registry?
Yes, as ch.zeno-works/zeno.
›How many tokens does Zeno add to my context?
Its tool definitions total about 17,658 tokens, resent on every turn.
Sources and method
- Official MCP Registry entry
- updated 2026-10-11
- Tool list (tools/list), measured by us
- 2026-10-19
- Last checked against all sources
- 2026-10-19
The description, packages and links come from the maintainer's own published metadata. Token cost, security signals, freshness and the reading above are MCPSkillHub's own measurements; see the methodology. Editorial standards are owned by Ramachandra Vikas Chamarthi, Founder & AI HPC Architect. Spot an error? Request a correction or claim the listing.