agentic-firmenbuch
by jkbngb
README.md
# agentic-firmenbuch
> **Austria's entire company register, queryable by AI agents in plain language.** Official master data, annual accounts and key ratios for every firm – served over MCP, answered on real numbers instead of hallucinations.
**[Try the playground](https://www.agentic-firmenbuch.at/playground.html)** · **[Get a free key](https://www.agentic-firmenbuch.at)** · **[Quickstart ↓](#quickstart)**
A live, automated data product over the Austrian **Firmenbuch** (free EU **HVD** / High Value Datasets), served through a **multi-tenant MCP server**. A deterministic Azure pipeline pulls the published Jahresabschluss (annual financial statement) filings from the official register, parses them, consolidates per company, computes ratios/growth/trends, and serves the result to MCP clients. The whole register holds ~640k legal entities; the served slice is currently ~341k and grows as the backfill progresses. **Version 1** = facts + clean derivations only (no scoring, no third-party enrichment, no NACE, no AI summaries).
> **Also available: Germany and the wider DACH region.** Alongside this Austrian product, a unified **`agentic-register`** endpoint serves both the Austrian **Firmenbuch** and the German **Handelsregister / Unternehmensregister** over a single MCP. It is listed in the official MCP registry as **`io.github.jkbngb/handelsregister`** (spec: [`handelsregister.server.json`](handelsregister.server.json)) and reachable at `https://register.agentic-firmenbuch.at/mcp` with the same free API key. A search returns matches from both countries, tagged by `country` (`AT` / `DE`).
## Quickstart
**Use the hosted service** – query official Firmenbuch data from an MCP client that accepts an HTTP header key (Claude Code, VS Code with GitHub Copilot, Cursor, …):
1. Get a free API key at **[agentic-firmenbuch.at](https://www.agentic-firmenbuch.at)** – just a verified email.
2. Add the server. **Claude Code** (terminal), one line:
```bash
claude mcp add --scope user --transport http agentic-firmenbuch https://mcp.agentic-firmenbuch.at/mcp --header "X-API-Key: <your-key>"
```
**GitHub Copilot / VS Code**: `code --add-mcp "{\"name\":\"agentic-firmenbuch\",\"type\":\"http\",\"url\":\"https://mcp.agentic-firmenbuch.at/mcp\",\"headers\":{\"X-API-Key\":\"<your-key>\"}}"`. Any other HTTP-MCP-Header client: URL `https://mcp.agentic-firmenbuch.at/mcp`, header `X-API-Key: <your-key>`.
3. Ask in natural language, e.g. *"Aktive GmbHs in Oberösterreich mit Bilanzsumme über 5 Mio. €, sortiert nach Umsatz."* The agent calls `search_companies` / `get_company_details` and answers with official data – no SDK required.
> **Claude Cowork & claude.ai** (sandboxed clients) don't take the API-key header – they connect via `Settings → Connectors → Add custom connector` with the URL `https://mcp.agentic-firmenbuch.at/mcp` and a one-time email login (OAuth, no key). Step-by-step with screenshots: **[agentic-firmenbuch.at/cowork.html](https://www.agentic-firmenbuch.at/cowork.html)**.
Prefer to try before signing up? Use the **[playground](https://www.agentic-firmenbuch.at/playground.html)**.
**Or run the pipeline yourself** – clone, `uv sync`, `uv run pytest` (offline, no Azure). See [Develop](#develop).
## Available MCP tools
| Tool | Purpose |
|---|---|
| `search_companies` | Filter / rank Austrian companies by region, size, balance-sheet total, equity ratio, revenue, growth profile, management age, last filing year, status. Returns a compact result card per match. |
| `get_company_details` | Full served profile of one company: identity, location, founding/filing years, size class, multi-year balance sheet + P&L, 13 computed ratios, growth, management, list of filings. |
| `get_full_record` | Superset of `get_company_details`: full 317-position UGB taxonomy, passthrough unknown codes, completeness, GuV-years, signatories history, derivations registry. |
| `get_company_history` | Filing-by-filing time series of every reported position for one company. |
| `find_peers` | K-nearest peer set for a company within its size class / region. |
| `get_cohort_summary` | Aggregate statistics (counts, percentiles, distributions) for a filtered cohort. |
| `get_coverage` | Per-Bundesland / per-Rechtsform / per-size-class coverage statistics for the served dataset. |
| `list_sectors` | Available legal-form (`Rechtsform`) and Bundesland codes for use in filters. |
| `describe_fields` | Self-describing field dictionary with type + null-rules + EBIT/EBITDA definition. |
| `get_document` | Fetch the URL/blob key for an original filed annual statement (XML or PDF). |
All tools return a **processed derivative of official Austrian Firmenbuch data** (source: BMJ – Justiz, CC BY 4.0). Concretely: we ingest the published filings from the register, parse and consolidate them, compute ratios, and serve that from our own database — no web scraping, no LLM-generated summaries, no third-party data mixed in (V1). Every response carries `provenance.data_version` + `built_at` and names the original source, so the agent can attribute it correctly; for the authoritative record, the official Firmenbuch always governs.
**Currently served**: ~341,000 active legal entities across all Rechtsformen (GmbH, AG, KG, OG, EU, Genossenschaft, Privatstiftung, SE …). The full register has ~640,000 entities; the gap is companies without a published Jahresabschluss plus inactive/deleted entries, which are added step by step.
## Documentation
Full index with the versioning convention (shipped `_v1` specs vs. the forward
[`ROADMAP.md`](ROADMAP.md) + V2 design spec): **[docs/README.md](docs/README.md)**.
The headline documents:
| Doc | What it is |
|---|---|
| [docs/pipeline-step-samples.md](docs/pipeline-step-samples.md) | File format + golden sample for every pipeline stage. |
| [docs/FIELD_REFERENCE.md](docs/FIELD_REFERENCE.md) | **Served field dictionary** – every field each MCP tool returns, with type + null rules. Public page: [felder.html](https://www.agentic-firmenbuch.at/felder.html). |
| [ROADMAP.md](ROADMAP.md) | Forward plan – status/priorities and the V2 direction (banks/insurers, GISA, Ediktsdatei). |
| [docs/appendix_position_mapping.json](docs/appendix_position_mapping.json) | Full 317-entry canonical position taxonomy → copied into `products/agentic-firmenbuch/packages/core_at/…/mapping/`. |
| [docs/reference/](docs/reference/) | Official source material (API reference, JAb 4.0 XSDs/Excel). |
## Monorepo layout (`agentic-first`)
This repository is the **`agentic-first`** monorepo umbrella. It separates **source-agnostic
shared code** from **per-source products**, so another source-specific product can be added without
touching the Austrian pipeline:
```
agentic-first/ (this repo)
├── packages/ SHARED — source-agnostic, zero Firmenbuch/UGB knowledge
│ ├── core/ (fbl_core) lineage/meta + metric contracts, config, storage clients
│ └── auth/ (fbl_auth) signup, token issue/validate, metering, 00_accounts
└── products/
└── agentic-firmenbuch/ AUSTRIA product (live) — README below
├── packages/
│ ├── core_at/ (fbl_core_at) UGB taxonomy, Firmenbuch domain models, ÖNACE, FI dirs
│ ├── firmenbuch_client, 99_registry, 90_ingest, 70_parse,
│ │ 50_consolidate, 30_derive, 10_present, mcp_server, orchestration
│ └── …
└── tests/ AT integration tests + golden fixtures
```
Additional source-specific products are added in their **own separate repositories** that consume
`packages/{core,auth}` as a dependency (they are not scaffolded here). The precise
1:1 / adapt / product-local reuse boundary is the **reuse table (Appendix R)** of the technical spec, and the generic recipe is in
[docs/monorepo/ADDING_A_PRODUCT.md](docs/monorepo/ADDING_A_PRODUCT.md).
**Product READMEs:** [agentic-firmenbuch](products/agentic-firmenbuch/README.md) · shared [`core`](packages/core/README.md) · [`auth`](packages/auth/README.md)
## Pipeline (numbered layers, `90 → 10`)
```
99_registry (foundation: all companies) → 90_raw (Blob) → 70_parsed (Blob) → 50_consolidated → 30_derived → 10_presentation → MCP
(Cosmos) (Cosmos) (Cosmos)
side: 00_accounts (MCP signup) · 00_directories (register-based FI flag, OeNB) reserved for v2: 40_enriched, 20_scored
```
`90_raw` is the **immutable source of truth** (every downloaded XML/PDF, kept forever). `70_parsed`
is a **write-through cache** of the per-filing `ParsedFiling` JSON – always re-derivable from raw,
so safe to drop/rebuild; it exists so a reprocess (re-consolidate/derive after a logic change)
**skips re-parsing** all filings, and so the lineage `inputs[]` in each consolidated doc resolve to a
real parsed document. `50/30/10` are the queryable Cosmos layers; `10_presentation` is what the MCP
serves.
## LAYER_MAP – which code owns which layer
Each pipeline-stage package directory is **prefixed with its layer number** so the
owner of every data layer is obvious. (Python module names can't start with a digit, so
the importable package keeps its `fbl_*` name; the number is also exposed as a `LAYER`
constant in each stage package.)
All AT stage packages live under `products/agentic-firmenbuch/packages/` (abbreviated `…/` below).
| Layer | Package (dir) | import | Store / container | Pydantic model | Sample |
|---|---|---|---|---|---|
| `99_registry` | [`…/99_registry`](products/agentic-firmenbuch/packages/99_registry/README.md) | `fbl_registry` | Cosmos `99_registry` | `RegistryDoc` | §15a.0 doc |
| `90_raw` | [`…/90_ingest`](products/agentic-firmenbuch/packages/90_ingest/README.md) | `fbl_ingest` | Blob `90-raw` | raw `Meta` + manifest | [Stage 0](docs/pipeline-step-samples.md) |
| `70_parsed` | [`…/70_parse`](products/agentic-firmenbuch/packages/70_parse/README.md) | `fbl_parse` | Blob `70-parsed` | `ParsedFiling` | [Stage 1](docs/pipeline-step-samples.md) |
| `50_consolidated` | [`…/50_consolidate`](products/agentic-firmenbuch/packages/50_consolidate/README.md) | `fbl_consolidate` | Cosmos `50_consolidated` | `ConsolidatedCompany` | [Stage 2](docs/pipeline-step-samples.md) |
| `30_derived` | [`…/30_derive`](products/agentic-firmenbuch/packages/30_derive/README.md) | `fbl_derive` | Cosmos `30_derived` | `DerivedCompany` | [Stage 3](docs/pipeline-step-samples.md) |
| `10_presentation` | [`…/10_present`](products/agentic-firmenbuch/packages/10_present/README.md) | `fbl_present` | Cosmos `10_presentation` | `PresentedCompany` | [Stage 4](docs/pipeline-step-samples.md) |
**Un-numbered.** Shared (in `packages/`): [`core`](packages/core/README.md) (`fbl_core`,
source-agnostic lineage/meta + metric contracts, config, storage) and
[`auth`](packages/auth/README.md) (`fbl_auth`, `00_accounts`). AT-specific (in
`products/agentic-firmenbuch/packages/`): [`core_at`](products/agentic-firmenbuch/packages/core_at/README.md)
(`fbl_core_at`, UGB taxonomy + Firmenbuch domain models + ÖNACE + FI directories),
[`firmenbuch_client`](products/agentic-firmenbuch/packages/firmenbuch_client/README.md)
(`fbl_firmenbuch_client`, HVD SOAP adapter),
[`orchestration`](products/agentic-firmenbuch/packages/orchestration/README.md)
(`fbl_orchestration`, the `--mode` Job entrypoint),
[`mcp_server`](products/agentic-firmenbuch/packages/mcp_server/README.md)
(`fbl_mcp_server`, serving). Plus [`products/agentic-firmenbuch/tests/`](products/agentic-firmenbuch/tests/README.md) (fixtures),
`docs/` (incl. [API probe findings](docs/API_PROBE_FINDINGS.md)).
## Build status – Version 1 complete ✅
All ten §15 build stages are implemented, each committed in order, each with a passing
Definition of Done. **`ruff` + `mypy --strict` + `pytest` (with an 80% coverage gate) are
green** in CI. The HVD API was live-probed (§16 resolved) and the full chain
raw→present was verified on live data end-to-end.
- **Stage 10:** an auth-restricted **coverage** tool (XML vs PDF-only vs none, by
format/status – §11) and **GitHub Actions CI** (`uv sync` → ruff → ruff format →
`mypy --strict` → pytest with an 80% coverage gate, plus a Bicep-compile job).
**What's left to operate** (not code): provision Azure (`infra/setup.sh`, billable),
push the FIRMENBUCH_API_KEY to Key Vault, build/push images, then run the Initial Load
(`sync-registry → backfill-ingest → backfill-process`) and enable the daily cron.
## Develop
```bash
uv sync # create the workspace venv
uv run pytest # all fixture/unit/integration tests (offline)
uv run mypy packages products # strict types (shared + products)
uv run ruff check packages products # lint
```
**True end-to-end (live):** a separate, env-flag-guarded test runs a few real FNRs
through every layer (API → `90_raw` → … → `10_presentation` → MCP). Skipped by default.
```bash
FBL_E2E=1 uv run pytest products/agentic-firmenbuch/tests/e2e -q # needs FIRMENBUCH_API_KEY
```
See [`products/agentic-firmenbuch/tests/e2e/`](products/agentic-firmenbuch/tests/e2e/README.md). It uses in-memory stores + a tiny real pull –
**no Azure, no full backfill** (deployment is manual after review).
## License & data
Licensed under the **MIT License** (see [`LICENSE`](LICENSE)).
The data originates from the **Austrian Firmenbuch** (BMJ – Justiz), an EU High Value Dataset
licensed under **CC BY 4.0**. Any redistribution of the data must keep the attribution
*"Quelle: Österreichisches Firmenbuch / BMJ – Justiz (CC BY 4.0)"* (see [`NOTICE`](NOTICE)).
## Disclaimer – no warranty, use at your own risk
This software and any data it produces are provided **"AS IS", WITHOUT WARRANTY OF ANY KIND**,
express or implied (see the MIT License). The processed data is derived automatically from the
public Firmenbuch and is provided **without any guarantee of correctness, completeness, timeliness,
or fitness for a particular purpose**. It is **not** legal, tax, or financial advice and does **not**
replace an official Firmenbuch extract – the official register always prevails.
**Use of this software and the data is entirely at your own risk.** To the maximum extent permitted
by law, the authors and copyright holders accept **no liability** for any direct, indirect,
incidental, or consequential damages arising from its use. You are responsible for complying with
the CC BY 4.0 attribution requirement and all applicable data-protection, competition, and copyright
law when using or redistributing the data.
This server cannot be deployed
Maintenance
ActivityActive
ResponsivenessResponsive