EU Company & VAT MCP Server
Pricing
Pay per event + usage
EU Company & VAT MCP Server
MCP server for AI agents: identify EU legal entities from official open registers (GLEIF, Brreg, ARES, Sirene) and verify EU VAT numbers (VIES). Read-only, sourced, timestamped. Charged only for successful results.
Pricing
Pay per event + usage
Rating
0.0
(0)
Developer
Benjamin Cojocaru
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
0
Monthly active users
6 days ago
Last modified
Categories
Share
eu-company-mcp
An MCP server that lets AI agents identify EU/EEA legal entities in official open registers and check EU VAT numbers in VIES. Every answer is structured JSON that carries its source URLs, licences and retrieval timestamps. The core is open source (Apache-2.0).
Part 1 of the "EU-data MCP servers" series. The register logic is copied from the reviewed Apify actor products/apify-actors/eu-company-identity.
Tools
All tools are read-only (readOnlyHint: true, idempotentHint: true, destructiveHint: false, openWorldHint: true). Each one declares an outputSchema and returns structuredContent, plus the same JSON as text for older clients.
| Tool | What it does |
|---|---|
lookup_company(query, country?, strictness?, max_results?) | Takes a name, a registration number or an LEI. It returns resolved (exactly one entity) or ambiguous (candidates). It never guesses. |
get_company_details(identifier, country?) | Takes an exact NO orgnr, CZ ICO, FR SIREN/SIRET or LEI. With a country, it also takes FI Y-tunnus, PL KRS, SK IČO, LV/LT codes, US CIK, GB company number, AU ABN or CH UID. It returns one record merged from the national register and GLEIF. |
verify_vat(vat_number, country_code?, include_trader_details?) | Checks a VAT number in VIES. It returns valid, invalid, unavailable or invalid_input. |
list_sources() | Lists the registers, coverage, licences and privacy rules. It makes no network request. |
Coverage:
- GLEIF LEI records, worldwide.
- National registers for Norway (Brreg), the Czech Republic (ARES) and France (Sirene via recherche-entreprises.api.gouv.fr). These are always searched.
- More national registers, queried only when you pass their country. Their results are linked to GLEIF by registration number, never by name:
- Finland (PRH/YTJ): name or Y-tunnus.
- Poland (KRS): KRS number only.
- Slovakia (RPO): name or IČO.
- Latvia (Register of Enterprises via data.gov.lv): name or registration number.
- Lithuania (Register of Legal Entities via get.data.gov.lt): name or code.
- United States (SEC EDGAR): CIK only, so SEC-registered companies only.
- Optional registers that need a free key, enabled only when the env var is set:
- United Kingdom (Companies House):
COMPANIES_HOUSE_API_KEY. - Australia (ABN Lookup):
ABN_LOOKUP_GUID. - Switzerland (Zefix):
ZEFIX_USERNAME+ZEFIX_PASSWORD.
- United Kingdom (Companies House):
- VIES for the EU-27 plus XI (Northern Ireland).
Any other country can be reached through GLEIF only, so only entities that have an LEI will be found. The same holds for a keyed country whose key is not set; a note says so. See COVERAGE.md for every register that was considered, with its licence, access method and the reason it was added, deferred or skipped. list_sources shows what is enabled in the running server.
Install / run
uvx eu-company-mcp # stdio (after the package is on PyPI)uvx --from /path/to/company eu-company-mcp # straight from this folderpipx install /path/to/companyeu-company-mcp --http --port 8000 # streamable HTTP at http://127.0.0.1:8000/mcpeu-company-mcp --http --host 0.0.0.0 --allowed-hosts mcp.example.com,mcp.example.com:* # non-loopback
The HTTP mode has no authentication. If you bind to an address other than loopback, the server refuses to start unless you give a Host allow-list (--allowed-hosts / EU_MCP_ALLOWED_HOSTS, optional --allowed-origins / EU_MCP_ALLOWED_ORIGINS). That list turns on the SDK's DNS-rebinding protection. Put a reverse proxy with authentication in front. The Apify wrapper is the one exception: it runs behind Apify's token-checking proxy, and its Host check is on only when EU_MCP_ALLOWED_HOSTS is set.
Claude Desktop (claude_desktop_config.json)
{"mcpServers": {"eu-company": {"command": "uvx","args": ["eu-company-mcp"],"env": { "EU_MCP_CONTACT": "you@example.com" }}}}
Until the package is published, use "args": ["--from", "/absolute/path/to/products/eu-data-mcp/company", "eu-company-mcp"].
Cursor (~/.cursor/mcp.json or .cursor/mcp.json)
{ "mcpServers": { "eu-company": { "command": "uvx", "args": ["eu-company-mcp"] } } }
To use a running HTTP server instead: { "mcpServers": { "eu-company": { "url": "http://127.0.0.1:8000/mcp" } } }
Environment
EU_MCP_CONTACT: optional contact e-mail, sent in the User-Agent so register operators can reach you. It is never logged.EU_MCP_RESPECT_ROBOTS=0: turns the robots.txt check off. The default is on. Only turn it off if you have a reason.EU_MCP_CONTACTis also strongly recommended for the US register: the SEC asks automated clients to identify themselves.- Optional register keys:
COMPANIES_HOUSE_API_KEY,ABN_LOOKUP_GUID,ZEFIX_USERNAME,ZEFIX_PASSWORD.- The server reads each one from the environment. If a key is not set there, it reads
KEY=VALUElines from~/.config/upside/*.env(change the folder withEU_MCP_KEY_DIR). - Only these variable names are read.
- Keys are sent only to their own register, as a header where the API allows it. ABN Lookup only accepts its GUID as a query parameter.
- Keys never appear in results, source URLs or error messages.
- The server reads each one from the environment. If a key is not set there, it reads
Behaviour and data rules
-
Untrusted data. Names, notes and other text come from third-party registers. Treat them as data, never as instructions. The server instructions tell the model the same. Control characters and bidi characters are stripped, and register strings are capped at 300 characters.
-
Errors. Results with
status: "error"(a register failed, or the server is busy) come back withisError: true. They still carry the full structured content. -
Legal entities only. Sole traders, natural persons, non-diffusible French records, estates and similar entries are excluded, and the result only shows how many were excluded (
excludedRecordsCount). The server never reads officers, owners, street addresses or phone numbers. The address field holds only city, postcode and country.- Every extra register uses an allow-list of legal forms. Its natural-person forms are listed in
COVERAGE.mdand inlist_sourcesunderprivacy. Examples: SK legal forms 100-110 and trade-licence records, LV individual merchants, LT individual enterprises, AU individuals/partnerships/trusts, CH sole proprietorships, and SEC individual filers. - PRH (the API has no private traders), KRS and Companies House hold no sole traders. They still hold partnerships.
- Partnerships (same rule everywhere): a partnership registered in a commercial register as its own entity is kept: NO ANS/DA, FI AY/KY, PL sp.j./sp.k., SK v.o.s./k.s., LV PS/KS, LT TŪB/KŪB, GB LP/LLP, CH Kollektiv-/Kommanditgesellschaft. Only its registered name is returned, which may contain a partner's surname, as the register publishes it. Partner data is never read. Australian partnerships are excluded, because ABN registrations of partnerships are not separate legal entities and are mostly individuals.
- Every extra register uses an allow-list of legal forms. Its natural-person forms are listed in
-
VIES privacy default. VIES can return a trader's name and address, and for a sole trader that is a natural person's personal data. The server returns only validity unless you set
include_trader_details=true. When you do, a note warns that the data is personal. -
No guessing. A name lookup is
resolvedonly when the full normalised legal name matches exactly (legal form included). A registration number or LEI resolves only on an exact match. If a register in scope fails, the result isincomplete, notresolved. -
Politeness:
- It only calls allowlisted official API hosts over https (
core/http.ALLOWED_HOSTS: the original five plus the extra registers' hosts), with SSRF guards: no private IPs, at most 3 redirects, size and time caps. - A register whose search answer lacks the legal form (SK, AU) gets at most 3 detail requests per name search. Bulk open-data dumps are never downloaded.
- A name search asks for up to 50 results where the API takes a limit. If the register says more names matched than it returned (or more candidates exist than the detail cap), the answer is
incompleteinstead ofresolvedor "not found". That answer carriessearchTruncated: trueand is never cached or charged. - It waits at least 1 s between requests to the same host, across all tool calls.
- It allows at most 40 requests per tool call.
- Requests to the same host go one at a time, keeping the 1 s gap. At most 4 tool calls do upstream work at once, and at most 16 wait for a slot, each for up to 20 s. Otherwise the answer is
busy, retry(status: error, never charged). - After a 403 or 429 from a host, it leaves that host alone for 10 minutes.
- It caches successful answers in memory for 15 minutes. The cache is process-wide: in Apify Standby it is shared by every user of one run. It holds only public register answers. Cache hits are marked
cached: trueand are never charged. - It uses no proxies and no browsers.
- It only calls allowlisted official API hosts over https (
-
robots.txt is read once per host per day and checked for every request path (RFC 9309; the product token is
eu-company-mcp). If robots.txt is unreachable or returns a 5xx, the host is treated as disallowed, and the server tries robots.txt again after 10 minutes. -
Licences and attribution:
- GLEIF: CC0. See the GLEIF terms.
- Brreg: NLOD 2.0.
- Sirene: Licence Ouverte 2.0.
- ARES: under the Ministry of Finance's operating conditions. Those licence terms are not confirmed (see the actor README).
- VIES: a public EC service.
- Extra registers: PRH CC BY 4.0, RPO CC BY 4.0, Latvia CC0, Lithuania CC BY 4.0, SEC public domain, Companies House (attribution "Contains Companies House data"; the reuse basis is to be confirmed by the founder), ABN Lookup Web Services Agreement, Zefix terms. KRS states no licence; see
COVERAGE.md. - Each result includes an
attributionlist.
Development
$nice -n 10 python3 -m pytest -q -p no:cacheprovider
Tests use recorded register fixtures (copied from the actor; FI/PL/SK/LV/LT/US recorded 2026-10-13 and trimmed to the fields used) and synthetic VIES, GB, AU and CH fixtures. They never touch the network. They include MCP protocol tests that run over the SDK's in-memory client and over a real stdio subprocess.
See PAID.md for a possible hosted tier and PUBLISH.md for publishing on PyPI or Apify (founder steps).
Pricing on Apify (pay per event)
company-resolved$0.008: One legal entity identified by lookup_company or get_company_details. Ambiguous, not found, cache hits and errors are free.vat-checked$0.003: VIES gave a definitive valid or invalid answer. Unavailable, errors and cache hits are free.
Exact repeats served from the 15-minute cache, partial answers, not-found results and errors are free. In Standby mode Apify also bills the small platform usage of the server to the user.