Skip to main content
Glama
POHODA MCP Server

POHODA MCP Server

README.md
# POHODA MCP Server

![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)
![Node.js Version](https://img.shields.io/badge/node-%3E%3D22-brightgreen)
![TypeScript](https://img.shields.io/badge/TypeScript-7-blue)

MCP server for [POHODA](https://www.stormware.cz/pohoda/) (Stormware) accounting software. Manage invoices, stock, orders, bank documents, warehouse, and accounting from any MCP-compatible client.

48 tools covering all major POHODA agendas via mServer XML API, including PDF printing and a raw XML escape hatch. Every tool's XML is checked against Stormware's official XSD schema.

## Requirements

- Node.js 22+
- POHODA with mServer enabled and running
- mServer user credentials with XML communication rights

## Installation

```bash
git clone https://github.com/hlebtkachenko/pohoda-mcp.git
cd pohoda-mcp
npm ci
npm run build
```

## Configuration

### Cursor

`~/.cursor/mcp.json`

```json
{
  "mcpServers": {
    "pohoda": {
      "command": "node",
      "args": ["/path/to/pohoda-mcp/dist/index.js"],
      "env": {
        "POHODA_URL": "http://localhost:444",
        "POHODA_USERNAME": "<your-username>",
        "POHODA_PASSWORD": "<your-password>",
        "POHODA_ICO": "<your-company-ico>"
      }
    }
  }
}
```

### Claude Desktop

`claude_desktop_config.json` ([location](https://modelcontextprotocol.io/quickstart/user#1-open-your-mcp-client))

```json
{
  "mcpServers": {
    "pohoda": {
      "command": "node",
      "args": ["/path/to/pohoda-mcp/dist/index.js"],
      "env": {
        "POHODA_URL": "http://localhost:444",
        "POHODA_USERNAME": "<your-username>",
        "POHODA_PASSWORD": "<your-password>",
        "POHODA_ICO": "<your-company-ico>"
      }
    }
  }
}
```

### Claude Code

`.mcp.json` in your project root, or `~/.claude.json` globally:

```json
{
  "mcpServers": {
    "pohoda": {
      "command": "node",
      "args": ["/path/to/pohoda-mcp/dist/index.js"],
      "env": {
        "POHODA_URL": "http://localhost:444",
        "POHODA_USERNAME": "<your-username>",
        "POHODA_PASSWORD": "<your-password>",
        "POHODA_ICO": "<your-company-ico>"
      }
    }
  }
}
```

### Any MCP client (stdio)

The server uses `stdio` transport. Point your MCP client to:

```
node /path/to/pohoda-mcp/dist/index.js
```

With environment variables set for authentication (see below).

### Environment Variables

| Variable | Required | Description |
|----------|----------|-------------|
| `POHODA_URL` | Yes | mServer URL (default port: 444) |
| `POHODA_USERNAME` | Yes | POHODA user with XML rights |
| `POHODA_PASSWORD` | No | POHODA password (default: empty, for a user without a password while POHODA access rights are off) |
| `POHODA_ICO` | Yes | Company IČO (accounting unit) |
| `POHODA_TIMEOUT` | No | Request timeout in ms (default: 120000) |
| `POHODA_MAX_RETRIES` | No | Max retries of list (read) requests on timeout/408/503 (default: 2). Writes are never retried |
| `POHODA_READ_ONLY` | No | `true` registers only list, status, company info and file download tools (default: `false`) |
| `POHODA_CHECK_DUPLICITY` | No | Sends `STW-Check-Duplicity: true` (default: false). POHODA matches resends by dataPack/dataPackItem id, and every request here gets a new UUID id, so this does not catch a resent write. Protection against double writes is that writes are never retried and a timed-out write is reported as outcome unknown |
| `POHODA_LIST_LIMIT` | No | Max records a list tool returns (default: 100); larger results are truncated with a note |
| `POHODA_EXE_PATH` | No | Path to `Pohoda.exe`; with `POHODA_CONFIG_NAME` enables mServer autostart |
| `POHODA_CONFIG_NAME` | No | mServer configuration name to start and stop |

### Read-only mode

With `POHODA_READ_ONLY=true` the create, update, delete, print and raw XML tools are not registered, and the client refuses to send anything but list requests. Use it to try the server against a real POHODA without risk. Any value other than `true` or `false` stops startup.

### Write results

A write fails when POHODA reports an error at any level (response pack, item, document response or import detail); every error is listed with its errno, and warnings are shown on success too. When POHODA changes a value you sent (errno 603: replaced by a default, e.g. an unknown předkontace, normalised or truncated), create invoice, voucher, bank and internal document, and create or update address, start their answer with `Created (ID …), but POHODA did not apply values you set: …` naming each field and value; the record still exists, so the result is not an error. Fields you did not send that POHODA filled itself are listed in one `POHODA filled defaults for: …` line. Update and delete report "no record matched, or unconfirmed" when POHODA does not confirm the action. A write is never resent. When its answer is lost (timeout, connection dropped while reading, HTTP 5xx, or a 200 that is not a POHODA `responsePack`), the result says the outcome is unknown, so check with a list tool before trying again. When the connection is refused, nothing was sent and the result says `Not sent: POHODA mServer is not running or not reachable at <url>`; start mServer (on a remote host: `scripts/mserver/pohoda-mserver start <config> <port>`, see [Run mServer on demand](#run-mserver-on-demand-pohoda-on-a-remote-windows-host)) and retry.

A list fails when the list element itself is in state error (its note is shown) and when mServer answers 200 with something other than a `responsePack`, so neither looks like an empty list.

### Booking fields

`pohoda_create_invoice`, `pohoda_create_voucher`, `pohoda_create_bank` and `pohoda_create_internal_doc` accept optional booking fields. Every field is optional; an omitted field is not sent and POHODA applies its default. Reference fields take the POHODA abbreviation (sent as `typ:ids`); number series, bank accounts, cash registers, centres and activities come from `pohoda_list_settings`, VAT classifications from `pohoda_list_vat`, contracts from `pohoda_list_contracts`; předkontace abbreviations have no list tool, take them from POHODA.

| Field | Invoice | Voucher | Bank | Internal doc |
|-------|:-:|:-:|:-:|:-:|
| `numberSeries` (číselná řada) | yes | yes | no (export-only in the XSD) | yes |
| `originalDocument` (supplier document number, KH DPH) | yes | yes | no | yes (`originalDocumentNumber`) |
| `dateTax` (DUZP), `dateAccounting` | existing params | `dateTax` only | no | yes |
| `dateKHDPH` | yes | yes | no | yes |
| `dateApplicationVAT` | yes | no | no | no |
| `accounting` (předkontace), `classificationVAT` (členění DPH), `classificationKVDPH` (SK only) | yes | yes | yes | yes |
| `centre`, `activity`, `contract` | yes | yes | yes | yes |
| `partnerId` (address-book record ID) | yes | yes | yes | yes |
| `paymentType` | yes | no | no | no |
| `account` (own bank account or cash register) | yes | `cashRegister` | yes | no |
| `paymentAccountNo` + `paymentBankCode` (counterparty account, both or neither) | yes | no | yes | no |

Line items on all four accept `payVAT` and the item-level `accounting`, `classificationVAT`, `classificationKVDPH`, `centre`, `activity` and `contract`.

### mServer autostart (Windows)

With `POHODA_EXE_PATH` and `POHODA_CONFIG_NAME` set, the server checks on the first tool call whether mServer answers, and if not runs `Pohoda.exe /HTTP start "<config>"` and waits up to 30 s. On shutdown it stops mServer again, but only if it started it.

### Run mServer on demand (POHODA on a remote Windows host)

When POHODA runs on another Windows machine, `scripts/mserver/` starts mServer there over SSH only when you need it, tunnels its port to your machine, and stops it after a set time without XML requests. Stormware advises against running mServer as a Windows service or keeping it running all the time: a running mServer blocks backups and database maintenance of that company in other POHODA windows. These scripts use Task Scheduler start and stop, as Stormware recommends. The scripts are not in the npm package; take them from this repository.

**Prerequisites**

- A POHODA user with rights to the XML communication.
- An mServer configuration per company, created in POHODA: Účetní jednotky → Databáze → POHODA mServer. Note each configuration's name and port.
- OpenSSH Server on the Windows host with key login, logging in as the Windows user who runs POHODA or as an administrator. Windows PowerShell 5.1 as the SSH shell works.
- That Windows user signed in on the host (a locked screen is fine): mServer starts in their desktop session, next to their own POHODA window, and shows its monitoring window there.

**Install (once, on the Windows host)**

1. Copy `scripts/mserver/windows/` to the host.
2. In an elevated Windows PowerShell, from that folder:

   ```powershell
   Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force
   .\Install-MServerTasks.ps1 -Config '<config>','<config2>' -User 'pohoda-host\<windows-user>'
   ```

   `-User` defaults to the current user; set it when you run the installer as a different administrator. Other options: `-IdleMinutes` (default 60), `-PohodaExe` (default `C:\Program Files (x86)\STORMWARE\POHODA\Pohoda.exe`), `-LogDir` (default `C:\ProgramData\STORMWARE\POHODA\Log\HTTP`).

This registers, per configuration, the tasks `POHODA mServer start <config>` and `POHODA mServer stop <config>` (running `Pohoda.exe /HTTP start|stop "<config>"` as that user), and one task `POHODA mServer idle stop`. `-Config` lists every configuration: running the installer again replaces all `POHODA mServer *` tasks with the new set. The idle script lives in `C:\ProgramData\pohoda-mcp`, writable only by SYSTEM and Administrators; the installer stops if that folder is a junction or owned by anyone else, since SYSTEM runs the script inside.

**Use (macOS or Linux)**

`scripts/mserver/pohoda-mserver` needs bash and ssh. Set `POHODA_SSH_HOST` to an ssh host alias with key login:

```bash
export POHODA_SSH_HOST=pohoda-host
scripts/mserver/pohoda-mserver start <config> <port>
scripts/mserver/pohoda-mserver status <config> <port>
scripts/mserver/pohoda-mserver stop <config> <port>
```

`start` runs the start task (unless the port already listens), waits up to 60 s for the port, and opens an ssh tunnel from `127.0.0.1:<port>` to the host. Then point the MCP server at the tunnel: `POHODA_URL=http://127.0.0.1:<port>`. Set `POHODA_LOCAL_PORT` to use a different local port. `stop` runs the stop task and closes the tunnel. `status` shows whether the remote port listens and whether the tunnel is open. Exit codes: 0 ok, 1 remote command failed, 2 usage, 3 mServer not listening, 4 tunnel failed or closed, 5 host unreachable. State (tunnel control socket, watcher PID and log) lives in `${XDG_STATE_HOME:-~/.local/state}/pohoda-mserver/`.

**Idle stop**

The task `POHODA mServer idle stop` runs every 5 minutes as SYSTEM. It reads the tail of each configuration's mServer log (`<LogDir>\<config>.log`); the last `POST /xml` request or the last server start counts as activity, `GET /status` does not. A running configuration idle for `-IdleMinutes` or longer is stopped through its stop task, and the action is written to `C:\ProgramData\pohoda-mcp\idle-stop.log`. On your machine, `start` leaves a watcher that checks every 5 minutes whether the remote port still listens and closes the tunnel once it does not, so the host is left alone. To change the minutes, run the installer again with the same `-Config` and a new `-IdleMinutes`.

**Uninstall**

```powershell
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force
.\Install-MServerTasks.ps1 -Uninstall
```

This removes every `POHODA mServer *` task. The folder `C:\ProgramData\pohoda-mcp` (idle script and its log) stays; remove it by hand if you no longer need it.

## Tools

### System (4)

| Tool | Description |
|------|-------------|
| `pohoda_status` | Check mServer status (processing queue, idle/working) and the POHODA program version from the last XML answer |
| `pohoda_company_info` | Get accounting unit info (company name, database, period) |
| `pohoda_download_file` | Download a file from POHODA's documents folder |
| `pohoda_raw_xml` | Send any dataPackItem XML not covered by a dedicated tool; the envelope and all namespace prefixes are added automatically |

### Print (1)

| Tool | Description |
|------|-------------|
| `pohoda_print` | Print a record with a print report, or save it as PDF on the POHODA machine and optionally return the PDF in the response |

### Address Book (4)

| Tool | Description |
|------|-------------|
| `pohoda_list_addresses` | Export contacts with filters (name, IČO, code, date) |
| `pohoda_create_address` | Create a new contact in address book (`company` = firm name / Firma, `name` = contact person / Jméno) |
| `pohoda_update_address` | Update an existing contact by ID |
| `pohoda_delete_address` | Delete a contact by ID |

### Invoices (2)

| Tool | Description |
|------|-------------|
| `pohoda_list_invoices` | Export invoices of one type: issued, received, advance, proforma, credit notes, receivables, commitments |
| `pohoda_create_invoice` | Create an invoice with line items, partner, symbols, VAT and booking fields |

POHODA's XML API cannot update or delete invoices, cash vouchers, bank documents, internal documents or contracts: their XSDs have no `actionType`. These agendas are create-only through this server; make corrections in POHODA itself.

### Orders (3)

| Tool | Description |
|------|-------------|
| `pohoda_list_orders` | Export issued/received orders with filters |
| `pohoda_create_order` | Create an order with items and partner |
| `pohoda_delete_order` | Delete an order by ID and type |

### Offers (2)

| Tool | Description |
|------|-------------|
| `pohoda_list_offers` | Export issued/received offers |
| `pohoda_create_offer` | Create an offer with items |

### Enquiries (2)

| Tool | Description |
|------|-------------|
| `pohoda_list_enquiries` | Export issued/received enquiries |
| `pohoda_create_enquiry` | Create an enquiry with items |

### Contracts (2)

| Tool | Description |
|------|-------------|
| `pohoda_list_contracts` | Export contracts with filters |
| `pohoda_create_contract` | Create a new contract |

### Bank Documents (2)

| Tool | Description |
|------|-------------|
| `pohoda_list_bank` | Export bank documents (receipts/expenses) |
| `pohoda_create_bank` | Create a bank document with items and booking fields |

### Cash Vouchers (2)

| Tool | Description |
|------|-------------|
| `pohoda_list_vouchers` | Export cash register vouchers |
| `pohoda_create_voucher` | Create a cash voucher (receipt/expense) with booking fields |

### Internal Documents (2)

| Tool | Description |
|------|-------------|
| `pohoda_list_internal_docs` | Export internal accounting documents |
| `pohoda_create_internal_doc` | Create an internal document with booking fields |

### Stock / Inventory (5)

| Tool | Description |
|------|-------------|
| `pohoda_list_stock` | Export stock items with filters (code, name, store) |
| `pohoda_create_stock` | Create a new stock item |
| `pohoda_update_stock` | Update a stock item by ID or code |
| `pohoda_delete_stock` | Delete a stock item |
| `pohoda_list_stores` | List all stores (warehouses) |

### Warehouse Documents (8)

| Tool | Description |
|------|-------------|
| `pohoda_list_prijemky` | Export receiving documents (příjemky) |
| `pohoda_create_prijemka` | Create a receiving document |
| `pohoda_list_vydejky` | Export dispatch documents (výdejky) |
| `pohoda_create_vydejka` | Create a dispatch document |
| `pohoda_list_prodejky` | Export sales documents (prodejky) |
| `pohoda_create_prodejka` | Create a sales document |
| `pohoda_list_prevodky` | Export transfer documents (převodky) |
| `pohoda_create_prevodka` | Create a transfer document (items by stock code) |

### Production & Service (4)

| Tool | Description |
|------|-------------|
| `pohoda_list_vyroba` | Export production documents |
| `pohoda_create_vyroba` | Create a production document (items by stock code) |
| `pohoda_list_service` | Export service records |
| `pohoda_create_service` | Create a service record |

### Reports (4)

| Tool | Description |
|------|-------------|
| `pohoda_list_accountancy` | Export accounting journal entries |
| `pohoda_list_balance` | Export open saldo/balance records as of a date |
| `pohoda_list_movements` | Export stock movement records |
| `pohoda_list_vat` | Export the VAT classification codebook |

### Settings (1)

| Tool | Description |
|------|-------------|
| `pohoda_list_settings` | Export settings (numerical series, bank accounts, cash registers, centres, activities, payment methods, stores, storage, categories, accounting units) |

## Resources

| URI | Content |
|-----|---------|
| `pohoda://enums/vat-rates` | Allowed `rateVAT` values |
| `pohoda://enums/invoice-types` | `invoiceType` values |
| `pohoda://enums/print-agendas` | Czech agenda names for `pohoda_print` |
| `pohoda://enums/date-formats` | Accepted date formats |

## Docker

```bash
docker build -t pohoda-mcp .
docker run --rm -i \
  -e POHODA_URL=http://host.docker.internal:444 \
  -e POHODA_USERNAME=<your-username> \
  -e POHODA_PASSWORD=<your-password> \
  -e POHODA_ICO=<your-company-ico> \
  pohoda-mcp
```

Multi-stage build, runs as non-root `node` user.

## Security

- Credentials via environment variables only
- `STW-Authorization` Basic auth per POHODA mServer specification
- `STW-Application: pohoda-mcp` header for audit trail in POHODA monitoring
- `STW-Check-Duplicity` header support to prevent duplicate imports
- XML escaping handled by xmlbuilder2 for all user-provided values
- Path traversal prevention for file downloads (normalize + reject `..` prefixed paths)
- Input validation via Zod on all tool parameters

## Development

```bash
npm test            # build + unit and end-to-end tests against a fake mServer
npm run check:xsd   # validate every tool's XML against the official XSD (needs xmllint and internet on first run)
```

Code layout and design notes: [ARCHITECTURE.md](ARCHITECTURE.md).

## POHODA mServer Setup

1. Open POHODA → Settings → mServer
2. Create a new mServer configuration
3. Set the listening port (default: 444)
4. Start the mServer
5. Ensure the user has XML communication rights (Settings → Access Rights → File → Data Communication)

For internet access, use HTTPS or VPN. mServer is primarily designed for local network use.

## Tech Stack

- TypeScript 7
- `@modelcontextprotocol/sdk`
- Zod (schema validation)
- xmlbuilder2 (XML generation)
- fast-xml-parser (XML parsing)
- iconv-lite (Windows-1250 encoding)
- Native `fetch`

## API Reference

- [POHODA XML Documentation](https://www.stormware.cz/xml)
- [POHODA Developer Guide](https://www.stormware.cz/pohoda/xml/obecny-obchod/pro-vyvojare/)

## Credits

`pohoda_print`, `pohoda_raw_xml`, mServer autostart and the enum resources follow ideas from [dg/pohoda-mcp](https://github.com/dg/pohoda-mcp).

## License

[MIT](LICENSE)

TDQS

B3/5.0

Scored across 48 tools

Disambiguation4/5

Most tools are clearly distinct by resource type (e.g., address, invoice, stock) and action (create, list, delete, update), but some Czech document types like převodka, příjemka, prodejka, výdejka, výroba may be ambiguous to non-Czech speakers without clearer descriptions of their specific purposes within the ERP system.

Naming Consistency5/5

All tools follow a consistent 'pohoda_verb_noun' pattern with snake_case, using clear verbs like create, list, delete, update, and download. This predictability makes it easy for agents to understand and navigate the toolset.

Tool Count2/5

With 48 tools, the count is excessive for typical MCP server purposes, likely overwhelming agents and indicating poor scoping. While POHODA is a comprehensive ERP system, many tools could be consolidated or omitted to reduce complexity and improve usability.

Completeness4/5

The toolset provides broad coverage for POHODA's ERP domain, including CRUD operations for key resources like addresses, invoices, orders, and stock, plus listing exports and status checks. Minor gaps exist, such as missing update tools for some document types (e.g., invoices, offers) and limited delete operations, but core workflows are well-supported.