Back to Discover

disclosures

connector

carrotly-ai

Corporate-disclosure research across 12 official sources behind ten intent-based tools.

View on GitHub
0 starsSynced Aug 7, 2026

Install to Claude Code

/plugin marketplace add carrotly-ai/disclosures

README

disclosures

Corporate-disclosure research for AI agents and TypeScript — filings, insiders, owners, financials, and ownership chains from 12 official sources across 10 jurisdictions.

npm version CI License: Apache-2.0 Node >= 18 Zero runtime dependencies MCP Registry

disclosures is a free, open-source Model Context Protocol server and a TypeScript library. It answers questions like "who are NVIDIA's directors?", "who owns 5% of Samsung Electronics?", or "show me Vale's last three annual results" — with every answer linked back to the official source document.

  • 10 stable tools, 10 jurisdictions — seven intent tools each route across national sources via one jurisdiction parameter; three register tools add filed-document, secured-charge, and person-appointment lookups. Tool names and schemas never change as coverage grows.
  • Official sources only — SEC EDGAR, GLEIF, UK Companies House, FCA NSM, filings.xbrl.org, Korea DART, Japan EDINET, China cninfo, India BSE, Taiwan TWSE, Brazil CVM, Germany BaFin.
  • Honest by design — real source links only, explicit "unsupported here" answers instead of empty or fabricated results, and clear caveats ("absence of a filing is not proof").
  • Zero runtime dependencies — one bundled file, runs anywhere Node 18+ runs.

Quick start

Requires Node 18+. The only required configuration is a descriptive User-Agent for SEC EDGAR (their fair-access policy) — set it to your name/org and contact email.

npx -y disclosures

Claude Code

claude mcp add --transport stdio disclosures \
  --env DISCLOSURES_USER_AGENT="Your Organization your-email@example.com" \
  -- npx -y disclosures

Claude Desktop

Add to claude_desktop_config.json (Settings → Developer → Edit Config):

{
  "mcpServers": {
    "disclosures": {
      "command": "npx",
      "args": ["-y", "disclosures"],
      "env": {
        "DISCLOSURES_USER_AGENT": "Your Organization your-email@example.com"
      }
    }
  }
}

Cursor

Add the same mcpServers block as Claude Desktop to ~/.cursor/mcp.json (or per-project .cursor/mcp.json).

VS Code (Copilot / MCP)

Add to .vscode/mcp.json:

{
  "servers": {
    "disclosures": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "disclosures"],
      "env": {
        "DISCLOSURES_USER_AGENT": "Your Organization your-email@example.com"
      }
    }
  }
}
Other clients — Windsurf, Codex CLI, Gemini CLI, and any stdio MCP client

Windsurf (~/.codeium/windsurf/mcp_config.json) uses the same mcpServers JSON as Claude Desktop.

Codex CLI (~/.codex/config.toml):

[mcp_servers.disclosures]
command = "npx"
args = ["-y", "disclosures"]
env = { DISCLOSURES_USER_AGENT = "Your Organization your-email@example.com" }

Gemini CLI (~/.gemini/settings.json) uses the same mcpServers JSON as Claude Desktop.

Any other client: run npx -y disclosures as a stdio command with the DISCLOSURES_USER_AGENT environment variable set. The server speaks newline-delimited JSON-RPC on stdout. It is also listed on the official MCP registry as io.github.carrotly-ai/disclosures.

Restart the client after changing its configuration, then try:

"Use disclosures to list Apple's board of directors and their latest Form 4 activity." "Who holds 5% or more of NVIDIA? Link the filings." "Resolve Samsung Electronics in Korea and show its latest annual financials." "What's the GLEIF ownership chain above Apple Operations India?"

The ten tools

ToolWhat it answersCoverage
CompanyResolve"Which company is this?" — canonical name plus CIK, ticker, LEI, ISIN, and local registry identifiers; GB adds previous names with date ranges and status/accounts detail.US, GB, KR, JP, CN, IN, TW, BR, DE + global LEI/ISIN
CompanyFilings"What has it filed?" — dates, types, descriptions, direct source links; a latest annual/quarterly report mode, plus a GB insolvency-history mode.US, GB, KR, JP, CN, IN, TW, BR
CompanyInsiders"Who runs it?" — directors, officers, titles, and 10%+ owners from insider registers.US, GB (incl. ECCTA identity status), KR, TW, DE (MAR Art. 19)
CompanyOwners"Who owns it?" — major-shareholder filers with thresholds, dates, and filing links.US (13D/13G), GB (PSC + TR-1), KR (5% rule), JP (5% rule / 大量保有報告書), TW (>10%), DE (§§33 ff. WpHG)
CompanyFinancials"What are its numbers?" — annual as-filed revenue, income, balance sheet, EPS, cash flow by fiscal period.US (XBRL), GB/EU (ESEF/UKSEF IFRS), KR, BR
OwnershipChain"Who consolidates it?" — GLEIF direct/ultimate accounting-consolidation parents and children.🌐 Global (any LEI or legal name)
PrivateRaises"Has it raised privately?" — Form D exempt offerings, amounts, investor counts, named related persons.US only in v1
CompanyDocument"What does the filing actually say?" — fetches a filed document's content: extracted iXBRL/HTML/DART-XML text or the source PDF saved to disk (image-only / pre-inline / XBRL-archive filings are reported honestly, never faked).GB (Companies House), US (SEC EDGAR), JP (EDINET), KR (OpenDART)
CompanyCharges"What's secured against it?" — registered charges/mortgages with status, dates, persons entitled, and fixed/floating/negative-pledge particulars.GB (Companies House)
PersonAppointments"Where else does this person sit?" — person search, cross-company role history, and disqualification / enforcement lookups (linked to the safe public register).GB (Companies House), US (SEC EDGAR), DE (BaFin DealingsInfo)

The first seven tools dispatch across jurisdictions via jurisdiction. Of the last three, CompanyDocument accepts a jurisdiction of GB (default), US, JP, or KR; PersonAppointments accepts US, GB (default), or DE; CompanyCharges is Companies House-specific and takes no jurisdiction — it always queries the UK register.

Every company input accepts a name or a local identifier — ticker, CIK, LEI, or ISIN (US/global), Companies House number (GB), OpenDART corp/stock code (KR), EDINET/securities/corporate code (JP), A-share or HK code (CN), BSE scrip (IN), TWSE listing code (TW), CVM registration code (BR), BaFin-Id or ISIN (DE). Pass jurisdiction: "US" | "GB" | "EU" | "KR" | "JP" | "CN" | "IN" | "TW" | "BR" | "DE" (default US).

Coverage matrix

IntentUSGBEUKRJPCNINTWBRDE
CompanyResolve
CompanyFilings
CompanyInsiders
CompanyOwners
CompanyFinancials
PrivateRaises
OwnershipChain🌐 global via GLEIF — jurisdiction-independent

✅ supported · — returns an honest unsupported-jurisdiction explanation, never an empty or fabricated result

Each jurisdiction has a full reference page — data source, credentials, accepted identifiers, per-intent behavior, and caveats — under docs/jurisdictions/.

Data sources and credentials

US and global lookups work with just the User-Agent. Non-US sources are keyless where the upstream allows it; the two that need keys are free. Provide only the keys for jurisdictions you query — everything else keeps working without them, and a missing credential produces a readable error naming the exact variable to set.

SourceJurisdictionKey requiredNotes
SEC EDGARUS (default)None — set DISCLOSURES_USER_AGENTFilings, insiders, 13D/13G owners, XBRL financials, Form D.
GLEIF🌐 globalNoneLEI/ISIN resolution, ownership chain.
Companies HouseGBCOMPANIES_HOUSE_API_KEY (free)Resolution, filings, officers, PSC — incl. ECCTA identity-verification status.
FCA NSMGBNone — inject-onlyDTR5/TR-1 ~3%+ major holdings inside CompanyOwners; activates only when you inject a fetchFn (no public read API).
filings.xbrl.orgGB, EUNoneESEF/UKSEF normalized annual IFRS financials (FY2020+).
DART / OpenDARTKROPENDART_API_KEY (free)Resolution, reports, executive ownership, 5% mass holdings, financials.
EDINETJPEDINET_API_KEY (free, search only)Resolution is keyless; document search needs the key.
cninfoCNNoneSSE/SZSE (+ HKEX mirror) resolution and announcement PDFs.
BSE IndiaINNoneResolution and announcement PDFs; anti-bot host — inject a fetchFn if throttled.
TWSE OpenAPITWNoneResolution, material information, directors/supervisors, >10% shareholders.
CVM open dataBRNoneResolution, IPE disclosure index, DFP annual financials in BRL.
BaFin AnteileInfo + DealingsInfoDENoneResolution, §§33 ff. WpHG major holdings, Art. 19 MAR directors' dealings.
# Required (SEC fair-access policy — your name/org and contact email)
export DISCLOSURES_USER_AGENT="Your Organization your-email@example.com"

# Optional, per jurisdiction
export COMPANIES_HOUSE_API_KEY="..."   # GB
export OPENDART_API_KEY="..."          # KR
export EDINET_API_KEY="..."            # JP document search

SEC_EDGAR_USER_AGENT is accepted as a fallback for compatibility; DISCLOSURES_USER_AGENT wins.

Use as a TypeScript library

The same package imports cleanly without starting stdio — every adapter takes injectable { fetchFn?, env?, cache? }, so it embeds and tests deterministically.

import { createTools } from "disclosures";

const tools = createTools({
  env: { DISCLOSURES_USER_AGENT: "Your Organization your-email@example.com" },
});

const resolve = tools.find((tool) => tool.name === "CompanyResolve")!;
const result = await resolve.handler({ company: "NVDA" });

Handlers never throw — every failure comes back as a readable MCP-shaped result. Individual adapters are also exported as namespaces (secEdgar, gleif, companiesHouse, openDart, edinet, cninfo, bseIndia, fcaNsm, xbrlFilings, twseOpenApi, cvmOpenData) if you want the raw normalized records instead of Markdown.

Persistent caching — skip re-downloading the KR/JP reference archives on restart

The OpenDART corp-code list (KR) and EDINET code list (JP) are multi-megabyte archives that regenerate about daily. Without a cache they are memoized per process; supply one to persist across restarts:

import { FileCache, createTools } from "disclosures";

const tools = createTools({
  env: { OPENDART_API_KEY: process.env.OPENDART_API_KEY },
  cache: new FileCache("/var/cache/disclosures"), // TTL-aware, survives restarts
});

cache is any DisclosuresCache (get/set). InMemoryCache and FileCache ship in the box; a corrupt, expired, or missing entry degrades to a normal refetch — a broken cache never breaks a lookup.

ISIN ↔ LEI cross-walk — map securities to issuers and back via GLEIF
import { gleif } from "disclosures";

gleif.isIsin("US0378331005");                               // true — validates the check digit
const issuer = await gleif.resolveLeiByIsin("US0378331005"); // → issuer's GLEIF Entity (with .lei)
const isins = await gleif.getIsinsForLei("HWUPKR0MPOU8FGXBT394"); // → every ISIN for that LEI

CompanyResolve already accepts a bare ISIN and routes it through this cross-walk; the helpers are for building your own identifier maps.

MCP server factory — embed the server in your own process
import { createDisclosuresServer } from "disclosures";

const server = createDisclosuresServer(); // McpServer with all ten tools registered

Importing the package never opens stdio; only the CLI entry point connects the transport.

Honesty and scope

These tools report public disclosures, faithfully — they are not KYC, UBO, or cap-table products:

  • Absence is not proof. No Form D doesn't mean a company never raised privately; a missing PSC doesn't prove no controller exists; a blank ECCTA identity field doesn't prove an officer is unverified.
  • GLEIF parents are accounting-consolidation relationships — not voting control, market-disclosure ownership, or ultimate beneficial ownership.
  • Schedule 13D/13G identifies filers at the 5% threshold; it is not a complete or continuously current capitalization table.
  • Section 16 insiders reflect recent Forms 3/4/5 and may not be a complete current roster.
  • Filings can be amended, restated, late, or tagged under alternate XBRL concepts — verify against the linked source documents.
  • Nothing here is legal, investment, accounting, or financial advice.

Resolution misses come back as plain "Could not find…" text; configuration, upstream, and rate-limit failures come back as flagged errors naming the fix. Every link is a real, resolvable source URL.

Documentation

PageContents
docs/jurisdictions/Per-jurisdiction reference: sources, credentials, accepted identifiers, per-intent behavior, caveats, and the coverage matrix.
docs/TESTING.mdOffline test isolation plus the separate credential-aware live end-to-end suite.
PUBLISHING.mdnpm trusted publishing and MCP-registry release automation.
CHANGELOG.mdRelease history.

Development

Requires Bun for development; the published artifact runs on Node 18+.

bun install
bunx tsc --noEmit     # strict typecheck
bun test              # 371 tests, fully offline — no live HTTP
bun run build         # bundles dist/server.mjs (zero runtime deps)
bun run test:stdio    # stdio integration against the built artifact

The default suite never touches the network: routed fetch stubs throw on any unmatched request. A separate live end-to-end suite builds the real Node artifact, drives it over MCP stdio, and uses whichever credentials are present in .env.local:

bun run test:live       # missing jurisdiction keys are reported as skips
bun run test:live:all   # strict: require User-Agent + GB/KR/JP keys

Live assertions are drift-tolerant (identity, identifier shape, source host, and response structure rather than volatile counts or dates), transient failures retry once, calls are time-bounded, and diagnostics redact configured keys. The live files use a .live.ts suffix so bare bun test cannot discover them. See the full testing discipline. The smaller bun run smoke:live SEC/GLEIF diagnostic remains available for quick checks.

stdio rule: the server reserves stdout for JSON-RPC — contributor diagnostics must go to stderr, since console.log corrupts the MCP transport.

Roadmap

Existing tool names and schemas stay stable — the collection only ever grows additively. The seven cross-jurisdiction intents absorb new sources and deeper data behind the same shapes; where a register offers primitives with no cross-jurisdiction equivalent (filed-document retrieval, secured-charge registers, person-level appointment history), a focused tool is added rather than contorting an intent. GB (Companies House) now goes deep: CompanyDocument, CompanyCharges, and PersonAppointments join CompanyResolve previous-name history and a CompanyFilings insolvency mode (GB.md). US (SEC EDGAR) now answers CompanyDocument (filing document manifests + inline HTML/XBRL text) and PersonAppointments (reporting-owner CIK search, cross-issuer Section 16 role history, and a safe SALI enforcement-lookup link) alongside the seven core intents (US.md). JP (EDINET) and KR (OpenDART) now answer CompanyDocument too — JP downloads a filing's PDF (with page count) and lists its XBRL archive members by docID; KR lists a filing's DART documents and extracts the main document's text by receipt number (JP.md, KR.md). DE (Germany) resolves issuers and returns §§33 ff. WpHG major holdings and Art. 19 MAR directors' dealings over BaFin's free databases, and now answers PersonAppointments too — a person-name search over the BaFin DealingsInfo notifying-persons index, then that person's issuers by BaFin meldepflichtigerId (DE.md). That was the last candidate from the live-verified feasibility finding (CAPABILITY-PARITY-FEASIBILITY.md); secured-charge registers remain a GB-only open-data primitive. Also ahead: CN/IN ownership and financials currently locked inside report PDFs. Suggestions and issues welcome on GitHub.

License

Apache-2.0. Copyright Carrotly AI.

Rendered live from carrotly-ai/disclosures's GitHub README — not stored, always reflects the source repo.

1 Install Method

NameDescriptionCategorySource
npm packageInstall via npm (stdio transport)mcp-serverdisclosures

0 Comments

Login required
Log in to post a comment or update on this repo.

No comments yet — be the first to share an update.