{
  "markdown": "<p align=\"center\">\n  <a href=\"https://beel.es\">\n    <picture>\n      <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://docs.beel.es/docs-static/beel-logo-dark.svg\">\n      <img src=\"https://docs.beel.es/docs-static/beel-logo.svg\" alt=\"BeeL\" width=\"220\">\n    </picture>\n  </a>\n</p>\n\n<h1 align=\"center\">BeeL MCP server — VeriFactu e-invoicing for AI agents</h1>\n\n<p align=\"center\">\n  Servidor <strong>MCP de facturación electrónica</strong> española con <strong>VeriFactu</strong> (AEAT): crea, emite y rectifica facturas desde <strong>Claude, ChatGPT, Cursor o VS Code</strong>.<br>\n  <strong>MCP server for Spanish e-invoicing</strong> with <strong>VeriFactu</strong> compliance — issue, correct and register invoices with AEAT straight from your AI agent.<br>\n  <a href=\"https://beel.es\">beel.es</a> · <a href=\"https://docs.beel.es\">API docs</a> · <a href=\"https://docs.beel.es/mcp\">MCP guide</a> · <a href=\"https://www.npmjs.com/package/@beel_es/mcp\">npm</a>\n</p>\n\n<p align=\"center\">\n  <a href=\"https://www.npmjs.com/package/@beel_es/mcp\"><img src=\"https://img.shields.io/npm/v/@beel_es/mcp.svg\" alt=\"npm version\"></a>\n  <a href=\"https://github.com/beel-es/beel-mcp/actions/workflows/ci.yml\"><img src=\"https://github.com/beel-es/beel-mcp/actions/workflows/ci.yml/badge.svg\" alt=\"CI\"></a>\n  <a href=\"CHANGELOG.md\"><img src=\"https://img.shields.io/github/v/release/beel-es/beel-mcp?label=changelog\" alt=\"Changelog\"></a>\n  <a href=\"https://modelcontextprotocol.io\"><img src=\"https://img.shields.io/badge/Model_Context_Protocol-server-224DA9\" alt=\"MCP server\"></a>\n  <a href=\"LICENSE\"><img src=\"https://img.shields.io/badge/License-MIT-yellow.svg\" alt=\"License: MIT\"></a>\n</p>\n\n---\n\nAn [MCP](https://modelcontextprotocol.io) (Model Context Protocol) server that lets an AI\nagent issue **legally compliant Spanish electronic invoices** — **VeriFactu** registration\nwith **AEAT**, F1/F2 invoice types, R1–R5 correctives, NIF validation against the census,\nand the regime keys the regulation requires. Connect it to **Claude, ChatGPT, Cursor or\nVS Code** and your agent can handle Spanish invoicing — *facturación electrónica* and\n*factura electrónica VeriFactu* — end to end, without you writing a single API call.\n\nIt is not a generated wrapper around an API. Three things make it usable by a model:\n\n- **Tools are derived from the public OpenAPI contract**, so each tool's input schema is\n  the operation's real schema — enums, line items, regime keys and all. The surface\n  cannot drift from the API.\n- **A tool-inclusion policy** decides what an agent should actually be given. Binary\n  downloads, multipart uploads, webhook plumbing, operations only a browser session can\n  authenticate, and deprecated ones are excluded by rule, not by hand.\n- **Fiscal guardrails** travel with the tools: the invariants a generated wrapper would\n  miss, both as documentation the model reads and as pre-flight checks that stop a\n  non-compliant request before it becomes a fiscal document.\n\nOne codebase, two transports: the hosted **remote server** at\n`https://mcp.beel.es/mcp` (Streamable HTTP + OAuth — one login per user, nothing to\ninstall), and a **local stdio** server built from this repository for headless use, where\nan API key works and a browser-based login does not.\n\n## Quick start\n\nAdd **`https://mcp.beel.es/mcp`** as a connector in Claude, ChatGPT, Cursor or VS Code and\nlog in with your BeeL account. Nothing to install and no API key to handle: the server acts\nwith your own credentials, and the OAuth flow is discovered from the URL.\n\n```bash\n# Claude Code\nclaude mcp add --transport http beel https://mcp.beel.es/mcp\n```\n\nThat is the whole setup for interactive use. Read on only if you need the local server.\n\n## Running it locally\n\nUse the local server when OAuth cannot: a scheduled job that issues invoices, a CI\npipeline, or any headless process where no one is present to complete a browser login.\nIt authenticates with an API key instead.\n\nRequires Node ≥ 20.\n\n```jsonc\n// Claude Desktop / Claude Code MCP config\n{\n  \"mcpServers\": {\n    \"beel\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@beel_es/mcp\"],\n      \"env\": { \"BEEL_API_KEY\": \"beel_sk_test_xxx\" }\n    }\n  }\n}\n```\n\n```bash\n# Claude Code\nclaude mcp add beel --env BEEL_API_KEY=beel_sk_test_xxx -- npx -y @beel_es/mcp\n```\n\nKeys prefixed `beel_sk_test_` are safe to experiment with; `beel_sk_live_` issues real\nfiscal documents.\n\nReleases are published from CI through npm [trusted\npublishing](https://docs.npmjs.com/trusted-publishers), so they carry provenance: npm\nrecords the exact commit and workflow each build came from. Verify it with `npm audit\nsignatures`.\n\nEach release is also announced to the [MCP\nRegistry](https://registry.modelcontextprotocol.io) as **`es.beel/mcp`**, listing both\ntransports, so clients that browse the registry find the server without being pointed at\nit. The name is authenticated by a DNS record on `beel.es`, so it says the server comes\nfrom us and not merely from some repository.\n\nAn earlier listing under `io.github.beel-es/beel-mcp` (v0.2.2) was retired when the name\nmoved. Registry names are identities rather than labels, so a rename is a new entry rather\nthan a redirect; both point at the same npm package and the same hosted server.\n\n## What it provides\n\n- **117 API tools** derived from `openapi/public-api.yaml` — invoices, customers,\n  products, recurring invoices, series and tax configuration, NIF validation, companies.\n- **4 synthetic tools** the API has no single endpoint for: `beel_docs_search`,\n  `beel_docs_get`, `beel_docs_list` over the documentation, and\n  `beel_get_setup_status`, which reports per NIF exactly what is missing before it can\n  issue and the one next action to take.\n- **Guardrail resources** under `beel://guardrails/*` — the fiscal invariants, plus\n  `beel://guardrails/errors`, a catalogue of every error code with the action it calls\n  for. Their summaries are woven into the description of every tool they constrain.\n- **7 workflow prompts** encoding the safe order of operations for the flows where the\n  order is what makes them safe: `issue-invoice` (validate NIF → choose F1/F2 → check the\n  VeriFactu gates → issue), `fix-invoice` (void vs correct), `onboard-nif`,\n  `setup-representation`, `invite-member`, `connect-payments` and `upgrade-integration`.\n- **Inline invoice PDF viewer** ([MCP Apps](https://modelcontextprotocol.io/seps/1865-mcp-apps-interactive-user-interfaces-for-mcp)):\n  generating an invoice PDF opens it in a side panel in hosts that support it.\n\nA generated catalogue of every tool, with the scopes each requires, lives at\n[docs.beel.es/mcp/tools](https://docs.beel.es/mcp/tools) (`npm run tools:catalog`).\n\n### What is deliberately not a tool\n\nBinary downloads (PDF preview, bulk ZIP, Excel/CSV export), multipart uploads (CSV/Holded\nimport, signed-PDF submission), webhook infrastructure, operations that only a browser\nsession can authenticate, and every `deprecated` operation. An agent cannot drive them,\nand each one costs context that a usable tool needs. The rules are in\n`src/policy/tool-policy.ts`.\n\n## The fiscal guardrails\n\nSpanish e-invoicing has invariants an LLM will get wrong from the schema alone — voiding\nan invoice that should have been corrected, using R1 on a simplified invoice, editing one\nAEAT has already registered. The server addresses that in three layers, and the difference\nbetween them matters:\n\n**1. Advisory** — `src/guardrails/rules/*.md`, one Markdown file per topic: the invoice\nlifecycle, void vs rectify, invoice types, invoice lines, regime keys, series numbering,\nNIF validation, the VeriFactu gates, multi-NIF accounts. Each is exposed as an MCP\nresource under `beel://guardrails/*` and its one-line summary is appended to the\ndescription of every tool it constrains, so the constraint travels with the call.\n\n**2. Enforced** — `src/guardrails/validate.ts`, checked before the request is sent, so a\nbad payload never even consumes an idempotency key:\n\n| Check | Code |\n|---|---|\n| Exactly one pricing field per line | `LINE_UNIT_PRICE_XOR_DECLARED_TOTAL` |\n| No discount on a declared total | `LINE_DECLARED_TOTAL_FORBIDS_DISCOUNT` |\n| No IRPF withholding on a simplified (F2) invoice | `SIMPLIFICADA_FORBIDS_IRPF` |\n| Equivalence surcharge only under regime `18`, and `18` only with one | `SURCHARGE_REQUIRES_REGIME` / `REGIME_REQUIRES_SURCHARGE` |\n| Series format can tell its reset periods apart | `SERIES_ANNUAL_REQUIRES_YEAR` / `SERIES_MONTHLY_REQUIRES_MONTH_AND_YEAR` |\n| Numbering is only seeded in the call that activates the company | `NUMBERING_REQUIRES_ACTIVATION` |\n| `SUPLIDO` lines carry their source reference | checked locally |\n| Exemption text only under reason `OTRO` | checked locally |\n| Correctives go through their own operation, not `type: CORRECTIVE` | checked locally |\n\n**3. Explained** — the BeeL API already answers well: its `message` is written for a\nhuman in the caller's language, `error.details` carries the specifics, and the RFC 7807\n`type` field links to a documentation page for that exact code (around 357 of them). The\nserver relays all of that untouched, and adds only the two things a response cannot\ncarry: **the remedy as a tool call** — the docs address someone with the dashboard open\n(\"create a series in settings\"), an agent needs `beel_set_default_series` — and\n**whether retrying can possibly help**, which is what stops an agent looping on a 403\nthat needs an administrator. `src/guardrails/catalog.ts` holds only codes where one of\nthose applies; anything else passes through, because a paraphrase would be worse than the\noriginal and would drift from it. The nested `blockers[]` of `EMISSION_NOT_READY` are the\nclearest case: they arrive as bare strings with no message and no link, and each comes\nback out naming the tool that clears it.\n\n**The BeeL API is the authority on all of it.** Every enforced rule mirrors a rejection\nthe contract documents, so the pre-flight is a strict subset of what the API refuses: it\ncan only make failure faster and better explained, never permit something the API would\nreject. Rules that depend on server-side state — AEAT census matching, the €3 000 F2\nceiling, whether a series exists — stay advisory on purpose, because guessing at them\nlocally would reject valid invoices. Set `BEEL_DISABLE_PREFLIGHT=1` to bypass the local\nchecks entirely.\n\nHand-curated lists are anchored by tests: every catalogued code must still appear in the\ncontract, every checked `operationId` must still resolve to a real tool, and every\nguardrail reference must point at a guardrail that exists. An API rename fails CI instead\nof silently switching a fiscal check off.\n\n## Configuration\n\n### Local server only\n\n| Variable | Purpose |\n|---|---|\n| `BEEL_API_KEY` | API key. The prefix selects the environment: `beel_sk_test_` → Test, `beel_sk_live_` → Live. |\n| `BEEL_ENV` / `BEEL_CONFIG_DIR` | Optional. With `BEEL_API_KEY` unset, falls back to the CLI's `~/.config/beel/config.json` (`beel login`); `BEEL_ENV` (`test`/`live`, default `test`) picks which stored key. |\n\n### Shared\n\n| Variable | Purpose |\n|---|---|\n| `BEEL_BASE_URL` | API base URL. Default `https://app.beel.es/api`. |\n| `BEEL_DOCS_URL` | Documentation source for the docs tools. Default `https://docs.beel.es`. |\n| `BEEL_REQUEST_TIMEOUT_MS` | Hard ceiling on a single API call. Default `30000`. |\n| `BEEL_DISABLE_PREFLIGHT` | Set to `1` to skip the enforced guardrails. |\n\nEvery default lives in `src/shared/defaults.ts`; nothing is hardcoded twice. Remote\ndeployment variables are documented in [DEPLOY.md](./DEPLOY.md).\n\nThe server starts and lists tools with no credentials at all — it only errors when an API\ntool is actually called. POST requests carry a stable `Idempotency-Key` derived from the\nrequest itself, so an agent retrying \"create invoice\" can never mint a second invoice.\n\n## Self-hosting\n\nThe remote server runs on Cloudflare Workers. See [DEPLOY.md](./DEPLOY.md) for the KV\nnamespace, the OAuth client BeeL must have registered, and the secrets involved.\n\n## Development\n\n```bash\nnpm ci\nnpm run dev          # stdio server from source\nnpm test             # vitest\nnpm run typecheck    # both the Node and the Worker configs\nnpm run build        # single-file bundle to dist/index.js\nnpm run inspect      # MCP Inspector against the local build\nnpm run spec:verify  # the vendored contract still matches its lock\n```\n\n`openapi/public-api.yaml` is a **generated** copy of the API contract, and\n`openapi/spec.lock.json` records its version, operation count and hash. CI fails if the\ntwo disagree, which is what keeps a vendored contract honest. See\n[CONTRIBUTING.md](./CONTRIBUTING.md).\n\n## The rest of the BeeL developer ecosystem\n\nEverything below derives from the same OpenAPI contract, so the vocabulary — invoice\ntypes, regime keys, series, VeriFactu states — is identical wherever you meet it.\n\n| | |\n|---|---|\n| [**REST API**](https://docs.beel.es) | The contract itself. Everything else is a projection of it |\n| [**CLI**](https://docs.beel.es/cli) | The same surface from a terminal, sandbox by default |\n| [**n8n node**](https://docs.beel.es/tools) | Invoicing inside a no-code workflow |\n| [**Claude Code plugin**](https://docs.beel.es/claude-code) | Implement, audit and maintain a BeeL integration |\n| [**Machine-readable docs**](https://docs.beel.es/llms.txt) | `llms.txt` for agents that would rather read than guess |\n\n## FAQ\n\n**What is the BeeL MCP server?**\nAn [MCP](https://modelcontextprotocol.io) server that exposes Spanish VeriFactu e-invoicing\nas tools an AI agent can call — so Claude, ChatGPT, Cursor or VS Code can create customers,\nissue F1/F2 invoices, register them with AEAT, and post R1–R5 correctives on your behalf.\n\n**How do I connect VeriFactu invoicing to Claude / ChatGPT / Cursor?**\nAdd `https://mcp.beel.es/mcp` as a connector and log in with your BeeL account — see\n[Quick start](#quick-start). Nothing to install, and no API key to paste for interactive use.\n\n**Is it actually VeriFactu-compliant?**\nYes. Invoices are registered with AEAT under VeriFactu, numbering and series follow the\nregulation, and the [fiscal guardrails](#the-fiscal-guardrails) stop non-compliant requests\nbefore they ever become a fiscal document.\n\n**VeriFactu or TicketBAI?**\nThis server targets **VeriFactu**, the national AEAT system. TicketBAI (the Basque Country\nregime) is out of scope.\n\n**Can I use it without an AI agent?**\nYes — it is a standard MCP server, so any MCP-capable client works, and the same invoicing\nsurface is available as a [REST API, CLI and n8n node](#the-rest-of-the-beel-developer-ecosystem).\n\n## Contributing\n\nBug reports and pull requests are welcome — see [CONTRIBUTING.md](./CONTRIBUTING.md) for\nhow the project is laid out and which conventions are load-bearing, and\n[Discussions](https://github.com/beel-es/beel-mcp/discussions) for questions. Issues\nlabelled [`good first issue`](https://github.com/beel-es/beel-mcp/labels/good%20first%20issue)\nare a reasonable place to start. Everyone participating is expected to follow the\n[Code of Conduct](./CODE_OF_CONDUCT.md). Security issues go to **security@beel.es** rather\nthan a public issue; see [SECURITY.md](./SECURITY.md).\n\n## License\n\nMIT © [BeeL.](https://beel.es)\n\n<div align=\"center\">\n<br>\n<a href=\"https://beel.es\">\n  <picture>\n    <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://docs.beel.es/docs-static/beel-logo-dark.svg\">\n    <img src=\"https://docs.beel.es/docs-static/beel-logo.svg\" alt=\"BeeL\" width=\"120\">\n  </picture>\n</a>\n<p><sub><a href=\"https://beel.es\">beel.es</a> · <a href=\"https://docs.beel.es\">Documentación</a> · <a href=\"https://docs.beel.es/mcp\">Guía del MCP</a></sub></p>\n</div>\n",
  "bytes": 15710,
  "sha": "7f56ff2093ec824f7946f9010acfd68b0ca168772d84e4ed1c80cf7741f5d60c",
  "repo_slug": "beel-es/beel-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_es_beel_mcp_a24a0464/readme"
}