# hub-equity-mcp **Standardized XBRL financial data for LLM agents.** A Model Context Protocol (MCP) server that exposes normalized financial facts from US SEC (EDGAR) and European ESEF filings to Claude Desktop, Cursor, and any MCP-aware client. Hub-Equity serves standardized **European ESEF** filings alongside US SEC data through one consistent hub-concept vocabulary, so an agent can ask for `REVENUE` or `TOTAL_ASSETS` and get a comparable, source-linked value whether the issuer files with the SEC or under ESEF. > **Maturity.** The public REST API behind this connector runs in production and > powers Hub-Equity's own chat. The package follows Semantic Versioning; it keeps > the Beta classifier while its install base is young. ## Why - **One vocabulary across two regimes.** SEC us-gaap and ESEF ifrs-full concepts are mapped to a single set of standardized hub codes, so cross-issuer and cross-taxonomy comparison works out of the box. - **Every number is source-linked.** Facts carry their filing, period, and provenance so an agent can cite rather than guess. - **Restatement-aware.** Explicit amendments and silent restatements (a figure a later filing reprinted differently, with no amendment filed), each change with the filing that made it. - **Read-only and closed-world.** Every tool advertises `readOnlyHint=true`, `idempotentHint=true`, `destructiveHint=false`, `openWorldHint=false` per the MCP spec, so clients can reason about safety and caching without introspection. - **No database credentials.** The published package talks only to the public REST API (`https://api.hub-equity.com`) over HTTPS. It never ships or requires a database key. ## Install ```bash pipx run hub-equity-mcp ``` `pipx run` (or `uvx hub-equity-mcp`) fetches and starts the server in an isolated environment; `pip install hub-equity-mcp` works too. Requires Python 3.12 or newer. ## Authentication and access The server talks only to the public REST API, which needs a `hubq_` key (env var `HUBEQUITY_API_KEY`). - **Free key.** Create an account and a key in a minute at https://hub-equity.com/settings/api-keys. Gives the base tool set (entity search, normalized facts, time series, segments, screener, data-quality grade, FX conversion, and more). - **Paid plan.** Unlocks the premium tools (restatement diffs, calculation trees, cross-period compare, cross-issuer compare, validation checks, extension concepts) and raises the rate limit. See the table below. The published package never reaches the database directly, only the REST API. ## Configure your client ### Claude Desktop Add to `claude_desktop_config.json` (macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`, Windows: `%APPDATA%\Claude\claude_desktop_config.json`): ```json { "mcpServers": { "hub-equity": { "command": "pipx", "args": ["run", "hub-equity-mcp"], "env": { "HUBEQUITY_API_KEY": "INSERT_YOUR_API_KEY" } } } } ``` Without `HUBEQUITY_API_KEY`, every tool call answers `HUBEQUITY_API_KEY is not set` with the link to a Free key. ### Cursor Add to `.cursor/mcp.json` (project root) or the global Cursor MCP settings: ```json { "mcpServers": { "hub-equity": { "command": "pipx", "args": ["run", "hub-equity-mcp"], "env": { "HUBEQUITY_API_KEY": "INSERT_YOUR_API_KEY" } } } } ``` ### Environment variables - `HUBEQUITY_API_KEY` (required): a `hubq_` key. A Free key opens the base tools; a paid plan opens the premium tools and the higher rate limit. - `HUBEQUITY_API_URL` (optional): defaults to `https://api.hub-equity.com`. `https://` is enforced whenever a key is set (the server refuses to send the Bearer key over plaintext to a non-loopback host). ## Capabilities | Type | What | |---|---| | Tools | Read-only tools: discovery, facts, time series, segments, forensic checks (table below) | | Resources | Hub catalog, statement schema, tier catalog, usage guides, coverage snapshot | | Prompts | Analytical templates (list below) | | Completion API | `{hub_code}` autocomplete | ## Tools The machine-readable tier catalog is served as a resource (`hub-equity://catalog/tool-tiers`). Free tools cover discovery and identity; Pro tools, open on every paid plan (Builder, Team, Enterprise), add forensic depth (calculation trees, restatement diffs on amendments and silent restatements, cross-period comparison, cross-issuer comparison, validation results, extension concepts). The tier of a tool mirrors the REST endpoint it calls: every Pro tool's endpoint requires the `facts.premium` scope server-side, and the Free caps (`get_fact_decomposition` depth 1 without roll-up components or dimensional slices, `screen_companies` 20 results without the quality filter, `get_segments` axes without members, `roll_up_metric` the value without its rule) are applied by the API, not only by this package. | Tool | Tier | What it does | |---|---|---| | `find_entity(query)` | Free | Search by name, ticker, or CIK. Returns the `entity_id` other tools need. | | `get_fact(entity_id, hub_concept_code, fiscal_year, fiscal_period_type?)` | Free | One normalized value plus its filing source. | | `get_fact_decomposition(entity_id, fiscal_year, hub_concept_code? or qname?, depth?)` | Free (depth 1, linkbase only) / Pro (depth 2-3) | Hub rollup, XBRL calc-linkbase children, and dimensional breakdown. Free: the linkbase layer, without the roll-up components and the dimensional slices. | | `search_concept(query)` | Free | Resolve a hub code from a label or a partial code. | | `list_hubs(category?, include_non_primary?, limit?)` | Free | Enumerate the standardized hub catalog by category. | | `get_entity_profile(entity_id)` | Free | Sector, auditor, employees, fiscal year end, recent filings. | | `get_metric_history(entity_id, hub_concept_code, n_years?)` | Free | N-year time series with YoY growth and CAGR. | | `get_segments(entity_id, hub_concept_code, fiscal_year)` | Free (capped) | Dimensional axis/member breakdown (segment, geography). Free: the axes without their members; Pro: every member. | | `get_amendments(entity_id, fiscal_year?)` | Free | 10-K/A restatement summary: every amendment, its per-concept changes (50 per amendment). | | `get_silent_restatements(entity_id, fiscal_year?)` | Free | Silent restatements: figures a later filing reprinted differently in its comparative columns, with no amendment filed, grouped by the filing that revealed them (50 changes per filing). | | `compare_entities(entity_ids, hub_concept_codes, fiscal_year)` | Pro (up to 10x10) | Cross-issuer comparison matrix at one period, calendar-year aligned. Accepts UUIDs, tickers or `TICKER.MIC`. | | `roll_up_metric(entity_id, hub_concept_code, fiscal_year)` | Free (capped) | Compute a value from signed children when it is not directly tagged. Free: the value; Pro: the rule and the signed contributions behind it. | | `convert_currency(amount, from_currency, to_currency, date?, rate_type?)` | Free | ECB reference-rate FX conversion (closing, average YTD, average prior year). | | `screen_companies(country?, sector?, min_revenue?, ..., sort_by?, limit?)` | Free (limit 20, no quality filter) / Pro (higher) | Filter the issuer universe by metadata, revenue, audit, and data quality. | | `get_amendment_diff(entity_id, fiscal_year?, hub_concept_code?, min_diff_pct?, kind_filter?)` | Pro | Per-concept 10-K/A diffs: materiality, kind and concept filters, label, absolute delta, both filings as sources. | | `get_silent_restatement_diff(entity_id, fiscal_year?, hub_concept_code?, min_diff_pct?, kind_filter?)` | Pro | The same diff on the silent restatements. | | `get_calculation_tree(filing_id, link_role?)` | Pro | The filing's calculation linkbase: every total and its components, declared sign next to the sign the filed values support. | | `compare_filings(entity_id, fiscal_year_a, fiscal_year_b, hub_concept_codes?)` | Pro | Cross-period same-entity compare with new / removed / sign-flip / restatement flags. | | `get_extension_concepts(entity_id, status_filter?, limit?)` | Pro | Issuer-specific qnames declared outside standard taxonomies. | | `get_data_quality_grade(entity_id)` | Free | A+ to D grade (or none), the two gates and counts behind it, freshness, direct-vs-derived split. | | `get_validation_results(filing_id?, entity_id?, fiscal_year?, status_filter?)` | Pro | XBRL accounting and calculation-linkbase checks. | ## Resources | URI | Type | Purpose | |---|---|---| | `hub-equity://catalog/hubs` | json | Full standardized hub catalog with EN/FR labels and category. | | `hub-equity://catalog/categories` | json | Hub counts per category. | | `hub-equity://catalog/tool-tiers` | markdown | Free vs Pro tool catalog and gating conditions. | | `hub-equity://schema/financial-statements` | markdown | Statement structure and reading rules. | | `hub-equity://catalog/hub/{hub_code}` | template | Forward catalog entry for one hub. | | `hub-equity://entity/{entity_id}/profile` | template | Full entity snapshot. | | `hub-equity://prompts/best-practices` | markdown | System-prompt guidance for client integrations. Load this before calling any tool. | | `hub-equity://prompts/tool-usage-examples` | markdown | Per-tool few-shot examples (good and anti-pattern). | | `hub-equity://prompts/data-coverage` | json | Live dataset snapshot (issuer and filing counts, sources, taxonomies, fiscal year range). Cached 24h. | ## Prompts Eight analytical templates: `peer_comparison`, `quality_of_earnings`, `restatement_audit`, `sector_overview`, `valuation_screen`, `goodwill_impairment_risk`, `working_capital_diagnostic`, `cash_flow_consistency`. ## For client developers Before calling any tool, fetch `hub-equity://prompts/best-practices` and inject the markdown into your system prompt. This makes your client follow the same tool routing, source-citation, and numeric-fidelity rules as Hub-Equity's own chat. ```python # Pseudo-code for a typical MCP client integration session = mcp.connect("hub-equity-mcp") best_practices = session.read_resource("hub-equity://prompts/best-practices") system_prompt = "You are an assistant ...\n\n" + best_practices # now call session.call_tool("find_entity", {"query": "AAPL"}) etc. ``` ## Rate limits | Mode | Limit | Notes | |---|---|---| | Free key | 120 requests / minute | Base tools. | | Builder key | 300 requests / minute | Premium tools unlocked. | | Team key | 600 requests / minute | Premium tools unlocked. | | Enterprise key | 1 000 requests / minute | Negotiable. | The bucket is per API key. On a per-minute 429 the client retries with exponential backoff (up to 3 times) before raising `RateLimitExceeded`. A used-up daily or monthly allowance is raised at once, with the API's message. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | `429 Too Many Requests` / `RateLimitExceeded` | Rate cap hit (120/min with a Free key, 300 to 1 000/min on a paid key) | Move to a paid plan, or slow down the tool-call fan-out. The client already backs off up to 3 times. | | `HUBEQUITY_API_KEY is not set` on every tool call | No key in the client's `env` block | Set `HUBEQUITY_API_KEY`; a Free key takes a minute at https://hub-equity.com/settings/api-keys. | | `HubEquityRestError: HTTP 401` | An invalid, expired or revoked `hubq_` key (`INVALID_API_KEY`) | Create a new key at https://hub-equity.com/settings/api-keys. | | `HubEquityRestError: HTTP 403` | The key's workspace is on the Free plan and the call needs a Pro tool, or a Free cap (`depth > 1`, `limit > 20`, `min_quality_grade`) | Upgrade the plan, or stay within the Free caps. | | `RateLimitExceeded` whose body carries `DAILY_QUOTA_EXCEEDED` / `MONTHLY_QUOTA_EXCEEDED` | The workspace's volume allowance is used up (Free 200 a day / 5 000 a month, Builder 50 000, Team 500 000 a month); raised at once, without retry | Wait for `resets_on`, or upgrade the plan. | | Connection or timeout errors | Network issue reaching `api.hub-equity.com`, or a bad `HUBEQUITY_API_URL` | Check connectivity; confirm `HUBEQUITY_API_URL` (if set) points to a reachable `https://` host. | | `ValueError: HUBEQUITY_API_URL must use https://` | A key is set but the URL is plain `http://` on a non-loopback host | Use `https://`, or unset `HUBEQUITY_API_URL` to fall back to the default API. | | Server does not appear in Claude Desktop or Cursor | Config JSON error, or `pipx` not on the client's PATH | Validate the JSON; use the absolute path of `pipx` (or `uvx`) if the client cannot resolve it. | ## Run locally ```bash python -m hub_equity_mcp.server ``` Or drive it interactively with the MCP inspector: ```bash npx @modelcontextprotocol/inspector python -m hub_equity_mcp.server ``` The inspector lists every tool, resource and prompt and lets you call each one. ## Development ```bash pip install -e '.[dev]' pytest tests/ ``` Tests are hermetic: tool tests mock the REST API with `respx`, so no live backend is needed. ## License Apache-2.0. See [LICENSE](LICENSE) and [NOTICE](NOTICE). This connector is an open client to the public Hub-Equity REST API; access to premium data stays gated by API key, plan, and rate limits on the service side.