XBRL filings index MCP β wraps the filings.xbrl.org JSON:API index run by
Copy the AI prompt to install this server into Claude Code, Cursor, or another agent β or use 1-click editor setup below.
One-click editor setup isnβt available for this listing yet β we donβt have a confirmed install command, and weβd rather show nothing than point your editor at the wrong package or host. Follow the projectβs own setup instructions, linked above.
XBRL filings index MCP β published company annual reports from the filings.xbrl.org index run by XBRL International, plus the IFRS financial facts inside each one. Keyless.
Part of Pipeworx β an MCP gateway connecting AI agents to 1684+ live data sources.
The European counterpart to sec-xbrl: same idea (accounting facts straight out of a regulator's XBRL), different filing regime.
esef_search_filings(entity_name?, country?, regime?, year?, period_end?, period_end_from?, period_end_to?, with_errors?, sort?, limit?, page?) β search the index. Returns company name, LEI or national identifier, country, regime, period end, XBRL validation error/warning counts, report language and links to the xBRL-JSON, HTML report, viewer and package. Any argument not in this list is rejected with a user_error naming the accepted set β it is never silently dropped (see GOTCHA 1).esef_entity_filings(entity, limit?) β every filing for one company, resolving a name, LEI or national registration number. Returns what it resolved to and how, plus filings grouped into distinct reports (see "Language editions" below).esef_filing_facts(fxo_id? | entity?, year?, concept?, include_dimensioned?, limit?) β the second hop. Opens the filing's xBRL-JSON report and returns named IFRS facts with value, currency, period and concept.esef_search_filings and esef_entity_filings prove a filing exists; only esef_filing_facts returns money.
25,640 filings under exactly two reporting regimes:
| Regime | Filings | Countries |
|---|---|---|
ESEF | ~15,996 | AT BE CY CZ DK ES FI FR GB GR IS IT LT NL NO PL PT RO SE |
UAIFRS | ~9,644 | UA |
The pack name says ESEF; the source is broader than ESEF. Both country and regime are first-class filters so a caller can pin the scope they meant. See GOTCHA 2 below β this is not a cosmetic detail.
None. No key, no account, no rate-limit documentation published.
filings.xbrl.org is a nonprofit index, so the pack is deliberately quiet with it: one index request per call, at most one report fetch, no probing or fan-out, and a real contactable User-Agent (Pipeworx/1.0 (+https://pipeworx.io; support@pipeworx.io)).
This is the dangerous one and it is why filter construction lives in exactly one helper.
The API takes JSON:API filters in two forms:
The second is the entire unfiltered index wearing a successful filtered query's clothes. Same status code, same envelope, same field names β nothing to catch.
Two defences, both in src/index.ts:
?filter=[{"name":"country","op":"eq","val":"FI"}] β a single unbracketed param, so there is no bracket encoding to get wrong, and a bad attribute name is rejected loudly (HTTP 400 "FilingSchema has no attribute bogus") instead of ignored. Only page[size] / page[number] are bracketed, and they go through buildUrl(), which uses URLSearchParams (which percent-encodes brackets).verifyFilters() re-checks the returned rows against what was asked for. Every esef_search_filings response carries filters_applied, filters_verified and filter_mismatches, so a filter that somehow failed to bite shows up in the payload rather than quietly widening the answer.The same failure mode existed one layer up, on our own tool boundary, and got fixed the same way (fleet #2317). esef_search_filings never declared period_end_from/period_end_to, so a caller who guessed those (a plausible name for a date range) had the arguments silently dropped by the handler β no schema error, filters_requested echoed them as null, and the response still claimed filters_verified: true. A date-bounded query for Portugal returned the identical 128-row unfiltered total as a query with no date at all. Fixed two ways: period_end_from/period_end_to are now real, declared range filters (op: 'ge'/'le' against period_end, verified by verifyFilters() like every other filter), and rejectUnknownArgs() throws a user_error naming the accepted argument list for anything else the caller might guess β so an undeclared argument can no longer be dropped silently, whatever its name.
An unfiltered probe's first record is Ukrainian: EDRPOU-32033791-2020-12-31-UAIFRS-UA-0. 38% of the index is UAIFRS. Describing the pack as "European filings" while it can return Ukraine is the resolver-grain trap β the caller gets a confident answer at the wrong grain.
So: every tool description names both regimes out loud, regime is a filter, every returned row states its own country and regime, and search responses carry a scope_note. If you edit a description, keep the scope sentence in it.
Citycon's FY2022 appears twice β β¦-ESEF-FI-1 (Finnish) and β¦-ESEF-FI-0 (English) β identical figures, different fxo_id. Counting index rows as reports inflates a company's filing history: Citycon has 11 filings but 6 distinct financial years.
esef_entity_filings therefore returns both filing_count (index rows) and report_count (distinct years), and groups editions under a preferred_edition β English when available, since the narrative facts are then readable. esef_filing_facts picks the English edition when resolving from a company name.
Note this is not what the language dimension inside a report does. Each xBRL-JSON document is single-language; the language variance is one level up, across filings.
Citycon's FY2022 ifrs-full:ProfitLoss of EUR 5,100,000 appears three times with byte-identical dimensions (primary statement, notes, equity reconciliation), plus further copies broken down by ComponentsOfEquityAxis. Returned naively that is one profit figure looking like six different ones.
esef_filing_facts groups on every dimension except language, collapses identical values, and reports occurrences (how many taggings backed the value) and dedup.repeat_taggings_collapsed. Axis-dimensioned breakdowns are excluded by default (include_dimensioned: false) and counted in dedup.dimensioned_facts_excluded. Genuinely contradictory values for one dimension set surface in conflicting_values rather than being silently picked between.
json_url can be nullAbout 1.5% of index rows have no machine-readable report, and in the sampled cases report_url and viewer_url were null too β the row is metadata only (e.g. Cloetta AB 549300CSLHPO6Y1AZN37-2021-12-31-ESEF-SE-1, which has error_count: 1 and only a package zip). esef_filing_facts returns {found: false, reason: 'no_machine_readable_report'} naming whatever URL did survive, instead of throwing.
identifier, not by idA JSON:API entity record carries both id: "1597" and attributes.identifier: "549300P8N0P6KDGTJ206". Only the identifier is addressable: /api/entities/1597 returns 404.
Worse, the identifier is not always the fxo_id prefix. Ukrainian filings use EDRPOU-32033791-β¦ in the fxo_id but are addressed as plain 32033791. Joining filings to entity names on the fxo_id prefix left every Ukrainian filing with entity_name: null; the pack joins on the tail of relationships.entity.links.related instead.
An xBRL-JSON instant period of 2023-01-01T00:00:00 is the 2022-12-31 balance sheet β the instant is the start of the following day. Reading the raw string is a full year of error. describePeriod() normalises both forms, so period_end and period_label ("as at 2022-12-31", "2022-01-01 to 2022-12-31") are already corrected.
DisclosureOfShareCapitalReservesAndOtherEquityInterestExplanatory in Citycon's FY2022 report is 2,500 characters of prose. Text values are clipped at 600 characters with value_truncated / value_length set, and within a period measured figures sort ahead of narrative, so concept: "Equity" leads with the EUR 2,310,300,000 balance rather than pages of note text.
This is not a bug in the pack, but it is the gotcha that actually costs users, and it went undocumented for a while (fleet #2317 β an external developer independently measured these same counts and rejected the pack over it). filings.xbrl.org is a voluntary community index, not a real-time regulator feed: a company's annual report typically lands weeks to months after the reporting period closes and after XBRL International (or a national collector) has processed it.
Measured live 2026-09-23 via esef_search_filings with the year filter:
| Country | 2023 | 2024 | 2025 |
|---|---|---|---|
| PT | 7 | 7 | 0 (before CMVM β see GOTCHA 10) |
| ES | 125 | 113 | 1 (before CNMV β see GOTCHA 11) |
No reviews yet β be the first to share how this listing worked for you.
Showcase your server listing on GitHub or your project documentation. Embed this dynamic SVG badge to highlight official listing status and live engagement.
[](https://allmcps.com/mcp/esef-filings)<a href="https://allmcps.com/mcp/esef-filings"><img src="https://allmcps.com/api/badge/esef-filings?style=directory" alt="Esef Filings on AllMCPs" /></a>