{"slug": "aldia-self-hosted-business-engine-with-permissioned-mcp-tools-for-agents", "title": "ALdía – Self-hosted business engine with permissioned MCP tools for agents", "summary": "ALdía, a self-hosted business engine, exposes 59 permission-controlled MCP tools that let AI agents perform business operations like invoicing, payments, and inventory management with built-in idempotency, structured errors, and an immutable audit trail. The system routes every write through the same API as its web application, ensuring consistent validation and auditability, and addresses agent-specific challenges such as retries, actionable errors, and permission intersections.", "body_md": "**ALdía turns an AI assistant into a business operator you can actually trust with money.**\n\nInstead of handing an agent database access, ALdía exposes the business itself —\ninvoicing, payments, customers, vendors, inventory, checks, expenses, cash — as\n**59 permission-controlled MCP tools**, with identity, idempotency, structured\nerrors and an immutable audit trail already built in.\n\n```\nYou: \"John paid invoice #1842 with this check.\"   [photo attached]\n\n  Assistant  ── reads it, extracts the data, asks what's missing\n      ↓  MCP\n  ALdía      ── validates, executes, records\n      │        ✓ payment recorded        ✓ check added to the portfolio\n      │        ✓ customer balance updated ✓ operation audited\n      ↓\n  Browser    ── where you see exactly what happened, and fix it if needed\n```\n\nThe assistant **interprets**. ALdía **validates and executes**. The web console\n**supervises**.\n\nNo accounting rule lives inside a model's prompt, and the agent never writes SQL.\n\nMost business software is built around forms. ALdía is built around **operations**.\n\nAn agent connected to ALdía doesn't get a database. It gets a vocabulary:\n\n```\nfind_customer            find a customer\nget_customer_balance     what they owe, and since when\ncreate_invoice           issue an invoice\nrecord_payment           record a customer payment\nrecord_vendor_payment    pay a vendor\nrecord_expense           record an expense\nfind_product             check inventory\nlist_checks              checks in the portfolio\nlist_debtors             who owes money, and since when\nget_audit_log            what happened, and who did it\n```\n\nAll 59 of them are business actions, not fiscal ones: `create_invoice` means\nthe same thing in Miami as in Córdoba. What changes underneath is which rules\nthe server applies.\n\nEvery write goes through the **same code path as the web application**: the same\nvalidations, the same transaction, the same audit record. There is no second\nimplementation to drift out of sync, because the MCP server never touches the\ndatabase — it speaks HTTP to the same API your browser does.\n\nWhen an agent starts moving real money, four problems show up. All four are already solved here, and covered by tests.\n\n**🔁 Idempotency that actually holds**\nAn agent retries when it doesn't get a response — and a lost response doesn't\nmean a lost operation. Send `X-Operation-Id` and the identifier is **reserved\nbefore execution**, not recorded after it. Two simultaneous retries can't both\nget through. *(The naive version — check, execute, then save — leaves a window\nwhere both do. We wrote the test that proves it, then closed it.)*\n\n**🧠 Errors an agent can act on**\nEvery error carries a stable `codigo`, the data that filled it (` params`), and\nan `accion` from a closed set of four:\n\n```\n{ \"detail\": \"Not enough stock for 'Coca 2.25': 12 requested, 5 on hand\",\n  \"codigo\": \"STOCK_INSUFICIENTE\",\n  \"accion\": \"corregir\",\n  \"params\": { \"producto\": \"Coca 2.25\", \"pedido\": 12, \"disponible\": 5 } }\n```\n\n`reintentar` · `corregir` · `preguntar` · `abortar`. A new agent behaves\ncorrectly without knowing the whole catalogue — it just reads that field. The 32\ncodes are published at `GET /api/errores`, no authentication required, because\nan agent getting a `401` needs to be able to look it up.\n\n**🔐 Who asked, and who executed**\nAn agent can declare which person it's acting for. Permissions are the\n**intersection** of the service account and that person — never one or the\nother, so a leaked agent credential can't become a universal impersonation key.\nActing on someone's behalf is an explicit permission an administrator grants,\naccount by account.\n\n**❓ Ambiguity without redoing the work**\n*\"There are two customers named John Smith. Which one?\"* The operation is stored\nexactly as it was going to run; confirming it means \"execute what you already\ndescribed, with this clarification.\" The agent doesn't rebuild the request and\nrisk changing something else.\n\n**📋 An audit log that can't be edited**\nEvery write is recorded automatically by middleware — including the **rejected**\nattempts, which are usually the interesting ones. It lives in its own schema, so\nit survives a full database wipe, and there is no endpoint to delete or modify\nit. Not even for the administrator.\n\nALdía is a single Python process and one SQLite file. It installs on the shop's own PC and the terminals reach it over the local network.\n\n**It works with the internet down.** Nothing is loaded from a CDN — that was a\ndeliberate fix, not an accident. A store that loses connectivity keeps invoicing.\n\nIt also backs itself up: once a day at startup, keeping the last 7, using\nSQLite's backup API rather than a file copy — in WAL mode, copying the file\nsilently loses the day's most recent sales. Each copy is verified with\n`integrity_check` on the spot.\n\nBusiness operations are universal. Tax rules aren't.\n\nALdía keeps a common engine and swaps **country packs**. One configuration key\nchanges how identifiers are validated, which tax applies, and whether documents\nneed approval from an agency.\n\n|  | 🇦🇷 Argentina | 🇺🇸 United States | \n|---|---|---|\n| Tax ID | CUIT, with check digit | EIN, format + assigned prefix | \n| Sales tax | VAT — closed list of legal rates | Sales tax — any plausible rate | \n| Document authorization | CAE from ARCA (WSAA + WSFEv1) | none | \n| Currency | ARS | USD | \n| Payment methods | cash, check, transfer, cards | + ACH | \n| Vendor records | — | legal name, DBA, W-9, 1099 worksheet | \n\n**U.S. sales tax is not a compliance solution, and the system says so itself.**\nIt applies **one rate you type in**, to everything. That's correct for a\nsingle-location business with obligations in one jurisdiction. It does **not**\ndetermine jurisdiction (state + county + city + special districts), apply\norigin/destination sourcing, track economic nexus, handle exempt categories, or\nmanage resale certificates. A specialised tax provider can be plugged in\nwithout touching the core — the interface is there and tested; no integration\nships with it. `GET /api/config/pais` returns these limits as `advertencias`\nso an agent can repeat them to the user instead of hiding them.\n\nAdding a country means implementing three questions — how the tax ID validates, what tax applies, whether documents need authorization — and nothing in the core changes. Your agent keeps calling the same tools either way.\n\nEvery amount is stored as **integer cents**, with commercial rounding applied\nonce, explicitly, at the point of conversion. This isn't pedantry:\n\n```\nsum([0.10] * 10)   # 0.9999999999999999\n1234.56 * 0.21     # 259.25759999999997   ← the VAT on a real invoice\n```\n\nBalances, ledger entries and period totals reconcile to the cent, permanently.\nInvoice numbering comes from a sequence table, not `max + 1`, so voiding a\ndocument never causes its number to be reused.\n\nRequires **Python 3.10+**. Runs on **Windows, Linux and macOS** — the server is\nplain Python and SQLite, with no platform-specific dependencies.\n\nOn the machine that will act as the server:\n\n```\ngit clone https://github.com/jonalemndi2/ALdia.git\ncd ALdia\n```\n\n**Linux / macOS**\n\n```\n./instalar.sh        # creates the venv and installs dependencies\n./iniciar_web.sh     # starts the server\n```\n\n**Windows**\n\n```\ninstalar.bat\niniciar_web.bat\n```\n\nFor a production install, pin the exact verified versions instead of the\ncompatibility ranges: `./instalar.sh --lock` (or, on Windows,\n`.venv\\Scripts\\python.exe -m pip install -r backend\\requirements.lock.txt`).\n\nThen open `http://localhost:8000`. First login is `admin` / `admin123`, and the\nsystem **refuses to let you operate until you change it** — that password is\npublished in this file, so an installation that keeps it has a known way in.\n\nTo connect an assistant, see [`mcp/README.md`](/jonalemndi2/ALdia/blob/master/mcp/README.md). Give it a\n**limited-role account**, not the administrator's: a connected agent can create\ndocuments and move real money.\n\nStart ALdía first, install the MCP environment as described in\n[`mcp/README.md`](/jonalemndi2/ALdia/blob/master/mcp/README.md), and provide `ALDIA_USER`, `ALDIA_PASSWORD` and\nthe optional `ALDIA_URL` through the client's or operating system's secure\ncredential mechanism. Never put the password in a command or in the repository.\n\nThe examples below use Linux/macOS paths; on Windows use the absolute paths to\n`mcp\\.venv\\Scripts\\python.exe` and `mcp\\server.py`.\n\n```\n# Claude Code\nclaude mcp add aldia -s user -- \\\n  \"/path/to/ALdia/mcp/.venv/bin/python\" \"/path/to/ALdia/mcp/server.py\"\nclaude mcp list\n\n# OpenClaw\nopenclaw mcp add aldia \\\n  --command \"/path/to/ALdia/mcp/.venv/bin/python\" \\\n  --arg \"/path/to/ALdia/mcp/server.py\"\nopenclaw mcp probe aldia\n\n# Hermes Agent\nhermes mcp add aldia \\\n  --command \"/path/to/ALdia/mcp/.venv/bin/python\" \\\n  --args \"/path/to/ALdia/mcp/server.py\"\nhermes mcp test aldia\n```\n\nAll three clients discover the same 59 tools. For a safe first check, ask the agent to find a customer, read a balance, or show today's cash; require an explicit review before issuing a document or moving money.\n\n**HTTPS is mandatory.** Without a certificate, credentials and session tokens\ntravel in plaintext. Use a reverse proxy — [Caddy](https://caddyserver.com/)\nhandles it in a few lines.\n\nAnd declare your proxy: `ALDIA_PROXIES=127.0.0.1`. Without it the server sees\nevery request as coming from the proxy, and eight failed logins are enough to\nlock out the entire store.\n\n- Anyone with filesystem access to `backend/aldia.db` can edit it outside the\napplication. There, the protection is OS permissions and backups.\n- The audit log records writes, not reads.\n- One installation, one business. There is no multi-tenancy, by design.\n- U.S. sales tax: see the warning above.\n- No 1099 forms are generated — only a worksheet. Thresholds, exclusions and deadlines change every year, and a return filed wrong is worse than none.\n\nEvery public release passes private functional validation before publication. GitHub additionally verifies installation and dependency integrity on Windows, Linux and macOS, and rejects databases, credentials, certificates and test material from the public tree.\n\n| [`docs/AGENTES.md`](/jonalemndi2/ALdia/blob/master/docs/AGENTES.md) | What agents can do, and what must not be broken | \n| [`docs/CONVERSATIONAL-COMMERCE.md`](/jonalemndi2/ALdia/blob/master/docs/CONVERSATIONAL-COMMERCE.md) | **Not built yet** — an open design for autonomous selling, and where to start | \n| [`docs/INTERNACIONALIZACION.md`](/jonalemndi2/ALdia/blob/master/docs/INTERNACIONALIZACION.md) | How country packs work; what's done and what isn't | \n| [`docs/AFIP.md`](/jonalemndi2/ALdia/blob/master/docs/AFIP.md) | Argentine electronic invoicing setup | \n| [`mcp/README.md`](/jonalemndi2/ALdia/blob/master/mcp/README.md) | Installing and connecting the MCP server | \n| [`skills/`](/jonalemndi2/ALdia/blob/master/skills) | Task playbooks for the assistant, per country | \n| [`SECURITY.md`](/jonalemndi2/ALdia/blob/master/SECURITY.md) ·[` CONTRIBUTING.md`](/jonalemndi2/ALdia/blob/master/CONTRIBUTING.md) | Reporting a vulnerability · contributing | \n\nALdía was designed and written with AI coding assistants, and it is built to be operated by them. Both halves of that sentence are the point.\n\n**Written with** — [Claude Code](https://claude.com/claude-code), OpenAI Codex,\nGoogle Gemini, DeepSeek, in [Visual Studio Code](https://code.visualstudio.com/).\n\n**Linux and macOS support update** — implemented with\n[OpenClaw](https://github.com/openclaw) using OpenAI **GPT-5.6-sol**. This update\nadded the native `instalar.sh` and `iniciar_web.sh` scripts and the automated CI\nmatrix that verifies ALdía on Linux, macOS and Windows.\n\n**Built to be driven by** — any [MCP](https://modelcontextprotocol.io/) client.\n[OpenClaw](https://github.com/openclaw) is the assistant this engine was shaped\naround, but nothing here depends on it: the 59 tools are plain MCP, and the\nserver never assumes which client is on the other end.\n\nThat's deliberate. An engine that only works with one assistant isn't infrastructure — it's a plugin.\n\n**Apache License 2.0** — see [LICENSE](/jonalemndi2/ALdia/blob/master/LICENSE).\n\nUse it, fork it, build a product on it, ship it commercially. No copyleft obligation: you are not required to publish your changes. The license includes an explicit patent grant, which matters for software that computes taxes.\n\nIf you build something with it, I'd genuinely like to hear about it.\n\nBuilt by **Jonathan Alemandi** ([@jonalemndi2](https://github.com/jonalemndi2)).\n\nIt started as a replacement for a VB6 + Access system running a real shop in Del Campillo, Córdoba, and turned into an attempt to answer a harder question: what does a business system need before it is safe to let an AI agent operate it with real money?\n\nMost of the interesting decisions here are defensive, and the reasoning behind\nthem is written into the code rather than lost in a commit message — see\n[`backend/dinero.py`](/jonalemndi2/ALdia/blob/master/backend/dinero.py) on why money is never a float,\n[`backend/database.py`](/jonalemndi2/ALdia/blob/master/backend/database.py) on why writes take the lock up\nfront, or [`backend/idempotencia.py`](/jonalemndi2/ALdia/blob/master/backend/idempotencia.py) on why checking\nbefore writing leaves a hole big enough to bill someone twice.\n\nIssues and pull requests welcome.", "url": "https://wpnews.pro/news/aldia-self-hosted-business-engine-with-permissioned-mcp-tools-for-agents", "canonical_source": "https://github.com/jonalemndi2/ALdia", "published_at": "2026-09-09 12:57:50+00:00", "updated_at": "2026-09-09 13:14:49.218465+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "ai-infrastructure", "developer-tools"], "entities": ["ALdía"], "alternates": {"html": "https://wpnews.pro/news/aldia-self-hosted-business-engine-with-permissioned-mcp-tools-for-agents", "markdown": "https://wpnews.pro/news/aldia-self-hosted-business-engine-with-permissioned-mcp-tools-for-agents.md", "text": "https://wpnews.pro/news/aldia-self-hosted-business-engine-with-permissioned-mcp-tools-for-agents.txt", "jsonld": "https://wpnews.pro/news/aldia-self-hosted-business-engine-with-permissioned-mcp-tools-for-agents.jsonld"}}