Yuki会计MCP服务

通过Model Context Protocol(MCP)将AI代理连接到Yuki会计平台,提供销售与采购发票处理、总账查询、文档归档、客户供应商管理及多账套管理等功能,适用于AI驱动的财务自动化与会计工作流。

  • by CodeMill-Solutions
  • TypeScript
  • 本地部署
  • 12 星标
  • 更新于
在 GitHub 查看 CodeMill-Solutions/yuki-mcp

使用说明

yuki-mcp

A Model Context Protocol (MCP) server that connects AI agents to Yuki accounting via Yuki's SOAP API.

Built with Node.js, TypeScript, and @modelcontextprotocol/sdk.


Installation

The quickest way is to let your MCP host run the package directly through npx — nothing to install or build. Add it to your MCP host configuration (e.g. claude_desktop_config.json):

{
  "mcpServers": {
    "yuki": {
      "command": "npx",
      "args": ["-y", "@codemill-solutions/yuki-mcp"],
      "env": {
        "YUKI_API_KEY": "your-api-key-here",
        "YUKI_DOMAIN_ID": "your-administration-guid-here",
        "YUKI_REGION": "nl"
      }
    }
  }
}

Belgian administrations must set YUKI_REGION=be. See API region.

Prefer a pinned local install? Install the package and point your host at its entry point instead:

npm install @codemill-solutions/yuki-mcp
{
  "mcpServers": {
    "yuki": {
      "command": "node",
      "args": ["/absolute/path/to/node_modules/@codemill-solutions/yuki-mcp/dist/index.js"],
      "env": {
        "YUKI_API_KEY": "your-api-key-here",
        "YUKI_DOMAIN_ID": "your-administration-guid-here",
        "YUKI_REGION": "nl"
      }
    }
  }
}

Desktop MCP hosts such as Claude Desktop do not inherit your shell's PATH. If the server only reports "Server disconnected", use absolute paths for the command (which npx / which node, e.g. /opt/homebrew/bin/npx on Apple Silicon Macs) — see Troubleshooting.


Prerequisites

  • Node.js 20+
  • A Yuki account with API access enabled
  • Your Yuki API key (Yuki → Settings → API)

Setup

1. Install dependencies

npm install

2. Configure environment variables

cp .env.example .env

Edit .env:

YUKI_API_KEY=your-api-key-here
YUKI_DOMAIN_ID=your-administration-guid-here  # optional at startup
YUKI_REGION=nl                                # nl (default) or be

YUKI_DOMAIN_ID can be left empty — the server starts without it. Call get_administrations to discover the correct GUID, then pass it via the administrationId parameter on individual tools.

3. Build

npm run build

4. Connect to an MCP host

Add to your MCP host configuration (e.g. claude_desktop_config.json):

{
  "mcpServers": {
    "yuki": {
      "command": "node",
      "args": ["/absolute/path/to/yuki-mcp/dist/index.js"],
      "env": {
        "YUKI_API_KEY": "your-api-key-here",
        "YUKI_DOMAIN_ID": "your-administration-guid-here"
      }
    }
  }
}

API region

Yuki runs a separate API host per region, and an administration only exists on the host for its own region. A key for a Belgian administration authenticates successfully against the Dutch host but then fails on every data call with:

SOAP Fault: Domain has no active database

Set the region to match your administration:

Variable Values Default Purpose
YUKI_REGION nl, be nl Selects a known regional API host
YUKI_BASE_URL full URL — Overrides YUKI_REGION entirely; use for hosts not listed above
YUKI_REGION=be

YUKI_BASE_URL takes precedence when both are set, so a new or private host can be reached without waiting on a release:

YUKI_BASE_URL=https://api.yukiworks.be/ws/

Defaults are unchanged — omitting both keeps the previous api.yukiworks.nl behaviour.

Per administration

YUKI_REGION / YUKI_BASE_URL set the server-wide default. When one server serves administrations in more than one region, pin the exceptions in the keys file:

{
  "<dutch-administration-id>": "api-key-nl",
  "<belgian-administration-id>": { "apiKey": "api-key-be", "region": "be" }
}

When a call still lands on the wrong host, the Domain has no active database fault names the host that was called and the regions to try.


Multi-administration support

If you manage multiple Yuki administrations (each with its own API key), you can supply a JSON file that maps every administrationId to its corresponding API key. The server then authenticates per administration automatically — no single shared key required. Administrations in different regions can share one server: give an entry its own region.

Keys file format

Each value is either the API key itself, or an object with the key plus an optional region override:

{
  "a1b2c3d4-0000-0000-0000-000000000001": "api-key-for-admin-1",
  "a1b2c3d4-0000-0000-0000-000000000002": { "apiKey": "api-key-for-admin-2", "region": "be" },
  "a1b2c3d4-0000-0000-0000-000000000003": { "apiKey": "api-key-for-admin-3", "baseUrl": "https://api.yukiworks.be/ws/" }
}
Field Required Purpose
apiKey yes The administration's API key
region no nl or be — the API host for this administration. Defaults to the server-wide YUKI_REGION
baseUrl no Full API URL for this administration; takes precedence over region

Plain string entries keep using the server-wide region, so existing files work unchanged. An unknown region is reported as an error (at startup or from reload_keys) rather than silently falling back to the default host. Each session is bound to the host that issued it, so tools need no extra parameters; reload_keys treats a changed region like a changed key and re-authenticates.

Path resolution (first match wins)

Priority Path
1 YUKI_API_KEYS_FILE environment variable (explicit path)
2 ~/.yuki/api-keys.json (default user-level location)
3 ./api-keys.json (local fallback for development)

Environment variable

YUKI_API_KEYS_FILE=/path/to/your/api-keys.json

When a keys file is present, YUKI_API_KEY becomes optional — the file keys are used for all administration-specific tool calls, and YUKI_API_KEY (if set) serves as the fallback for tools that don't target a specific administration (e.g. get_administrations).

If neither YUKI_API_KEY nor a keys file is found at startup, the server logs a warning but continues running — tools will return an error when called.

Reloading keys at runtime

When a new key is generated externally — for example by a sibling MCP server that drives the Yuki Integraties UI to create a fresh API key for a new administration — the new entry only lands in ~/.yuki/api-keys.json. By default the MCP server reads that file once at startup, so a freshly added key would require a server restart before it can be used for SOAP calls.

The reload_keys tool (see Available tools below) avoids this: it re-reads the keys file from disk and replaces the in-memory map in place. Sessions for entries whose key or region changed or that were removed are evicted from the session cache automatically; sessions for unchanged entries stay warm so subsequent calls do not pay the re-authentication cost.

Typical flow for a sibling tool that has just minted a new key:

1. (external) write the new key to ~/.yuki/api-keys.json
2. yuki-mcp.reload_keys()        → { added: [<adminId>], updated: [], removed: [], total: N }
3. yuki-mcp.get_administrations  → now works for the new admin without restart

reload_keys takes no arguments: it always reads the file resolved by the rules above. Which file holds the keys is the operator's decision — the environment variable or a default location — and an MCP client can trigger a reload but cannot choose the file. Versions up to 1.7.0 accepted an optional path argument; it is ignored since 1.7.1 (see the changelog). A file that does not parse is reported as API keys file at … is not valid JSON, without quoting its contents.


Available tools (31)

Administrations

Tool Description
get_administrations List all administrations (companies) for this API key. Run this first to find the correct administrationId.
get_administration_id Look up an administration's GUID by its exact name. Useful when you know the name but not the GUID.
reload_keys Re-read the administrationId → apiKey JSON file (including per-administration region overrides) from its configured location without restarting the server. Takes no arguments. Returns a diff of added/updated/removed IDs and invalidates affected sessions. Use after an external create_api_key flow.

Relations

Tool Key parameters Description
search_relations searchValue, searchOption?, active?, pageNumber? Search customers and suppliers by name, code, VAT number, email, etc. Returns up to 100 results per page.
upsert_contact fullName, contactCode?, contactType?, … Create or update a contact. When contactCode matches an existing record it is updated; otherwise a new contact is created.

Sales invoices

Tool Key parameters Description
get_sales_invoices dateOutstanding?, sortOrder?, includeBankTransactions? Retrieve outstanding (unpaid) sales invoices.
process_sales_invoice reference, subject, date, dueDate, contact, lines Create and book a new sales invoice. Optionally email it to the customer.

Purchase invoices

Tool Key parameters Description
get_missing_invoices types? Retrieve bank payments that still need a matching purchase invoice — equivalent to "Postbus → Ontbrekende facturen" in the Yuki web interface. Works for Dutch and Belgian administrations; the response includes typesSeen, and an unexpected <Type> label can be passed via types.
get_purchase_invoices dateOutstanding?, sortOrder?, includeBankTransactions? Retrieve outstanding (unpaid) purchase invoices.
process_purchase_invoice date, invoiceAmount, invoiceVatAmount, contact, lines Book an incoming purchase invoice. Accepts an optional PDF as base64.

Transactions & bank

Tool Key parameters Description
get_transactions glAccountCode, startDate, endDate Retrieve journal entries for a GL account (e.g. a bank account) in a date range. Use get_gl_accounts to find the right code.
get_transaction_details reference Check if an outstanding item still exists and retrieve its current status.
process_journal subject, entries[] Post a general journal entry (memoriaal). All entry amounts must sum to exactly 0. Used for bank reconciliation, corrections, and custom bookings.

Accounting

Tool Key parameters Description
get_gl_accounts date? Retrieve all GL accounts with their balance at a given date (commercial view). Use this to find account codes before calling get_transactions.
get_gl_accounts_fiscal date? Same as get_gl_accounts but including fiscal corrections. Use for balance sheets and P&L views that must match Yuki's fiscal reports.
get_net_revenue startDate, endDate, fiscal? Retrieve net revenue (netto-omzet) for a date range. Set fiscal=true to include fiscal corrections.

Accounting info

Richer read-only views from the AccountingInfo.asmx service — not available through the standard Accounting.asmx.

Tool Key parameters Description
get_gl_account_scheme — Retrieve the complete GL account scheme (rekeningschema): all codes with type, subtype, description, and active/inactive status. Use to validate GL codes or build account pickers.
get_period_table yearId Retrieve the fiscal period table for a year: period numbers, names, and date ranges. Use to translate transaction dates into human-readable period names for reports.
get_gl_transactions_detailed startDate, endDate, glAccountCode?, financialMode? Detailed transaction listing with document type, archive folder, fiscal period ID, project code, and mutation user. More complete than get_transactions. Leave glAccountCode empty for all accounts.
get_transaction_document transactionId Download the source PDF for a booked transaction as base64. Use the id or hID from get_gl_transactions_detailed.
get_start_balances yearId, financialMode? Retrieve opening balances (beginbalansen) per GL account for a fiscal year.

Documents

Tool Key parameters Description
upload_document fileName, dataBase64, folder?, amount? Upload a PDF to the Yuki archive by passing its content as a base64 string. Use get_document_folders first to find the right folder ID.
upload_document_from_path filePath, fileName?, folder?, amount? Upload a PDF from a local file path. Reads and encodes the file internally — preferred over upload_document when the file is available on disk. Validates that the file exists and is a valid PDF before uploading.
get_document_folders — List all archive folders available in the administration.
list_documents folderId List documents in a specific archive folder. Returns document IDs, file names, dates, and amounts.
search_documents searchText Full-text search across all archived documents (file names, amounts, OCR content).
get_document documentId Retrieve metadata for a single archived document by its ID (name, folder, date, amount, status).
download_document documentId Download an archived document as a base64-encoded string.
get_cost_categories — List available GL cost categories for use as the costCategory parameter in upload tools.

Backoffice

Tool Key parameters Description
get_workflow administrationId? Retrieve backoffice workflow items — documents that could not be processed automatically and are awaiting review by the accountant.
get_outstanding_questions administrationId? Retrieve outstanding questions raised by the accountant that require a response before the related documents can be processed.

Testing

Option 1 — MCP Inspector (tool-level, no LLM)

npm run inspect

Opens a browser UI at http://localhost:5173 where you can call individual tools and inspect raw responses.

Option 2 — Agent test harness (with Claude)

Runs a full agentic loop: Claude reasons about the task, calls tools, and returns a final answer — exactly as an AI agent would use this MCP.

Add your Anthropic API key to .env:

ANTHROPIC_API_KEY=sk-ant-...

Then run a scenario:

npm run agent                                              # default: get_administrations
npm run agent -- --scenario outstanding-invoices
npm run agent -- --scenario search-relations --arg "Bedrijf BV"
npm run agent -- --scenario gl-accounts
npm run agent -- --scenario bank-transactions --arg "1200"
npm run agent -- --scenario full-workflow

Available scenarios: get-administrations, search-relations, outstanding-invoices, outstanding-payables, gl-accounts, bank-transactions, full-workflow.


Architecture

src/
├── index.ts                  # Entry point — loads env + keys file, registers tools, starts stdio transport
├── yuki-client.ts            # SOAP client: per-key session cache, envelope builder, axios HTTP, fast-xml-parser
└── tools/
    ├── administrations.ts    # get_administrations, get_administration_id
    ├── relations.ts          # search_relations, upsert_contact
    ├── invoices.ts           # get_sales_invoices, get_purchase_invoices,
    │                         # process_sales_invoice, process_purchase_invoice
    ├── transactions.ts       # get_transactions, get_transaction_details, process_journal
    ├── accounting.ts         # get_gl_accounts, get_gl_accounts_fiscal, get_net_revenue
    ├── accounting-info.ts    # get_gl_account_scheme, get_period_table,
    │                         # get_gl_transactions_detailed, get_transaction_document,
    │                         # get_start_balances, get_missing_invoices
    ├── documents.ts          # upload_document, upload_document_from_path,
    │                         # get_document_folders, list_documents, search_documents,
    │                         # get_document, download_document, get_cost_categories
    └── backoffice.ts         # get_workflow, get_outstanding_questions

scripts/
└── test-agent.ts             # Agent test harness (Claude + MCP client loop)

Auth flow

Yuki uses a two-step authentication pattern:

  1. Authenticate(accessKey) → returns a temporary sessionID
  2. All subsequent calls include that sessionID

YukiClient.getSessionID(adminId?) handles this transparently. When called with an administrationId, it resolves the matching API key from the loaded keys map. Session IDs are cached per API key for the lifetime of the process — Authenticate is only called once per key, not once per tool call.

Note: Parameter casing differs across Yuki's services — sessionId (lowercase d) on Sales.asmx and Purchase.asmx; sessionID (uppercase D) on Accounting.asmx, AccountingInfo.asmx, Contact.asmx, and Archive.asmx. This is handled per-tool.

XML documents

Write tools (process_sales_invoice, process_purchase_invoice, process_journal, upsert_contact) pass structured data to Yuki as an XML string inside the xmlDoc SOAP parameter. The XmlValue wrapper ensures this XML is embedded raw (not entity-encoded) in the SOAP envelope. All user-supplied values are XML-escaped via escapeXml().


Rate limits

Yuki enforces 1,000 API requests per day (upgradeable to 5,000–10,000). Each tool call is 1 request. Session IDs are cached so Authenticate is only called once per API key per server process, not once per tool call.

Design agent workflows to fetch broad lists once and reference them from the agent's context window rather than re-fetching on every step.


Troubleshooting

Error Likely cause
Server disconnected with no further output The MCP host could not start the server. Desktop hosts don't inherit your shell's PATH, so use absolute paths (which npx / which node). With package versions up to 1.6.0, npx also fails with could not determine executable to run because the package had no bin entry — upgrade, or run node against dist/index.js
get_missing_invoices returns count: 0 while Yuki shows missing invoices Your administration reports a <Type> label the tool doesn't recognise — check typesSeen in the response and pass the bank-side labels via the types parameter
SOAP Fault: Authentication failed YUKI_API_KEY is incorrect or API access is not enabled in Yuki Settings
No API key found for administration … The administrationId passed to the tool is not in the loaded keys file — check YUKI_API_KEYS_FILE
API keys file at … is not valid JSON The keys file exists but does not parse — look for a trailing comma or stray character. The file must be a single JSON object mapping administrationId → key
SOAP Fault: Administration not found Wrong administrationId — run get_administrations to get the correct GUID
Journal entries do not balance Amounts in process_journal don't sum to 0 — check debit/credit signs
HTTP 500 from api.yukiworks.nl Usually a wrong XML namespace or malformed xmlDoc — check the WSDL at https://api.yukiworks.nl/ws/{Service}.asmx?wsdl
File does not appear to be a PDF The file at filePath does not start with the %PDF magic bytes — check you're pointing at a valid PDF
File not found filePath passed to upload_document_from_path does not exist or is inaccessible
Network error No connectivity to api.yukiworks.nl — requests time out after 30 seconds

About CodeMill Solutions

CodeMill Solutions is a Dutch software company based in the Netherlands. We build smart, scalable, and customized solutions that help organizations grow, optimize processes, and realize their digital ambitions.

Our services include:

  • Custom applications — portals, dashboards, business software, and fully tailored platforms that truly add value.
  • API integrations — connecting your application with other systems and external platforms via smart API connections.
  • Mobile apps — iOS and Android apps as a logical extension of your web application(s).

yuki-mcp is one of our open-source integrations, making Yuki's accounting platform accessible to AI agents through the Model Context Protocol.

📧 info@codemill.dev 🌐 codemill.dev 💼 LinkedIn 🐙 GitHub


License

MIT — see LICENSE.