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/npxon 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:
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/" }
}
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)
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
Relations
Sales invoices
Purchase invoices
Transactions & bank
Accounting
Accounting info
Richer read-only views from the AccountingInfo.asmx service — not available through the standard Accounting.asmx.
Documents
Backoffice
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:
Authenticate(accessKey)→ returns a temporarysessionID- 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) onSales.asmxandPurchase.asmx;sessionID(uppercase D) onAccounting.asmx,AccountingInfo.asmx,Contact.asmx, andArchive.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
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.
📧 [email protected] 🌐 codemill.dev 💼 LinkedIn 🐙 GitHub
License
MIT — see LICENSE.