Inspiration

Somewhere right now, someone is asking an AI whether a company is real. In most of the world there is no structured answer. Across much of Africa, South Asia and Latin America, company data exists only in fragmented public lists: PDFs, portals and scanned registers that never meet. Uganda, our reference country, publishes a dozen business registers (city trading licences, tax agent lists, the procurement portal, product certifications, sector regulators), each with its own identifier, format and outage schedule. A lender doing KYB, a procurement officer checking a bidder, or an agent asked "is this company real?" all repeat the same manual, unrecorded work. The businesses themselves have nowhere to see or correct what the public record says about them. Global aggregators work only where registers are already structured.

We tested the alternative before building: the same question through web search dug through PDFs, anchored on a 2024 licence, and confused two legal entities. That answer is what most of the world gets today.

What it does

Atlas harmonises public registers into one business record with field-level provenance. Every value is a statement: source, source reference, date pulled, licence and precedence. Records are resolved across registers by issuer-unique identifiers (tax identification numbers merge; name matches stay candidates, never merges). Four deterministic, versioned scores (Formality, Activity, Compliance Signals, Procurement Readiness) are computed from the evidence and always report coverage: how many registers were checked, found, and not yet checked. No language model produces a number; scores are explained by templated evidence rows a person can click through to the register. Unknown is shown as unknown, never as zero.

Today: 79,078 businesses across 2 country packs, 9 of 13 registers loaded, over 600,000 sourced statements and 300,000 stored scores. Uganda is the reference pack (8 of 12 registers); Kenya's central-bank licensee directory is the second, proving the contract is portable. A country is a pack: declarations for its registers, identifier schemes and boundaries plus small adapters, forkable by anyone.

WebMCP. The site registers its tools with document.modelContext, scoped to the route (four on the home and search pages, more on a business page with atlas_id defaulting to the record on screen, all ten on /tools; tool sets change on navigation via toolchange). Read tools (search_businesses, get_business, get_evidence, score_business, explain_score, find_segment, list_sources) carry readOnlyHint, and every result containing register-derived text carries untrustedContentHint so agents treat it as data, not instructions; results are structured, bounded and paginated with context-bound cursors. Write tools (start_claim, submit_correction, label_linkage, report_issue) each require human confirmation: where the browser cannot prompt in-page, the tool stores an unconfirmed request and returns a link to a plain form the person must submit, and unconfirmed requests expire and never count. The claim and report forms are declarative tools (toolname, toolparamdescription). The same implementations are served over a read-only remote MCP endpoint, and the /tools page runs every tool from the page itself in any browser, no flag needed.

Humans and agents together. An agent answers "is this bidder real, and has it delivered public contracts?" with cited register rows, and "no evidence in checked registers" is an honest answer. A business owner then claims their record: the agent starts the claim, the person confirms on the page, the claim is verified by publishing a string on the business website (or a link mailed to a domain a register published), a maintainer approves it in a moderation queue that shows exactly what the approval rests on, and the next regeneration publishes the operator-verified value above the register value, with its own source beside it. Corrections never overwrite a regulator's word.

How we built it

SvelteKit on Cloudflare Workers; three D1 databases (records, statements, scores) regenerated by a transactional pointer swap; R2 for raw snapshots and Parquet bundles with Frictionless data packages; KV caching keyed by regeneration and deployment; a Python pipeline (adapters with a six-check conformance suite, probabilistic linkage with Splink, versioned rubrics) scheduled with GitHub Actions, with an identity-churn guard. Personal contact details in registers are dropped at parse time. Apache-2.0 code, CC-BY-4.0 data.

Challenges we ran into

Chrome 152 does not expose in-page confirmation for write tools, so we built a page-confirmation fallback that keeps a human in every write. Registers disagree about where a business is, so location is resolved as a unit from one winning source rather than field by field. A register's export job failed on the publisher's side mid-build; the pipeline now records that source as failed with both dates and regenerates from everything that succeeded, so one outage never blocks the rest.

Accomplishments that we're proud of

Tested in ChatGPT's built-in browser (GPT-5.6 Sol): the site tools arrow lists the tools, and the ask about a real company came back cited register by register, with identifiers and dates from this week, in under four minutes (screenshots in the README). The verified-claims loop is live end to end: domain-proved claims, evidence documents, a gated moderation queue, and operator statements that outrank scraped values at the next regeneration. And the whole thing runs on free-tier infrastructure with open data downloads.

What we learned

Agents are honest exactly when the data layer is: give them provenance and coverage and they say "not checked" instead of guessing. Deterministic scores plus clickable evidence beat any generated number. And a register that fails must be shown as failed, because stale data dressed as fresh is worse than a gap.

What's next for TrustScore Atlas

More country packs through the same declarations, cross-country search, and the operator-verified claims loop running at scale.

Built With

Share this project:

Updates