vendor-crosswalk
Reconciles vendor records held in Airtable — the operational "ops" system that carries department, owner, and contract dates — against the ledger and spend systems. The server proposes which Airtable vendor records correspond to records in the other systems, attaching the evidence it found (exact/close name, address, email domain, ZIP), a confidence tier, and any discrepancies, so a finance reviewer can approve or reject each link. It only reads from these systems; every link is written to the crosswalk table by a human decision.
Reconciles vendor records held in Sage Intacct — the accounting "ledger" system that is the authority for accounting defaults — against the spend and ops systems. The server matches records that carry different native IDs across systems, surfaces the supporting evidence and a confidence tier, flags stale or near-duplicate ledger records and entity-suffix changes (LLC to Inc.), and packages each proposal into a short review packet. Read-only against Sage Intacct; linking happens only through an explicit reviewer decision in the crosswalk.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@vendor-crosswalkload the synthetic records and show me high-confidence vendor matches"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
vendor-crosswalk-mcp
In plain language. The same vendor often lives in three finance systems under three different names and IDs ("Harbor Sound, LLC", "HARBOR SOUND L.L.C.", "Harbor Sound"). This tool lines those records up, shows why it thinks they are the same vendor, and waits for a person to say yes. It never changes the finance systems, and it never reads tax IDs or bank details.
Four terms, briefly:
Crosswalk: a lookup table that says "this record in one system is the same vendor as that record in another." The source systems stay as they are.
MCP server: MCP (Model Context Protocol) is a standard way to give an AI assistant such as Claude a tool it can use. Run as an MCP server, this lets Claude ask for proposed matches and their evidence. There is no tool for approving a match or writing to a finance system; only a person does that.
Eval gate: the test it must pass before anyone relies on it. On a made-up benchmark, it may mark zero wrong matches as certain. One wrong "certain" match and it fails.
Synthetic data: every vendor here is invented. No real vendors, amounts, tax IDs or bank details.
A governed vendor-identity crosswalk. Three finance systems hold a record for the same vendor under three different IDs. This package proposes which records belong together, shows its evidence, keeps a person on every link, and refuses to touch tax IDs or bank details. It ships as a Python library, a one-command demo, and an MCP server.
It is the public, synthetic-data version of the flagship workflow in an
eight-week Finance AI pilot I proposed for a $17M+ cultural institution. The
proposed pilot would reconcile Sage Intacct, Airbase, and Airtable. This one
reconciles ledger, spend, and ops, with vendors that do not exist.
pip install -e ".[mcp]"
vendor-crosswalk demoThe problem, in one paragraph
Bills and card charges sync between the ledger and the spend platform. Vendor records do not. The name moves; the address, billing, and tax details do not. Finance creates the vendor in both systems, links them by hand, and keeps a third operational record with the department, owner, and contract dates. Each system uses its own ID. Matching them depends on whoever has been there longest. When records drift apart, nobody notices until a payment fails or a 1099 is wrong. That is a control problem, not a data-entry problem.
Related MCP server: grant-agent-harness
What it does
records (3 systems) -> propose() -> Proposals with evidence + tier
-> Crosswalk.from_proposals() -> rows waiting for a person
-> build_review_packet(row) -> the one thing the reviewer reads
-> Crosswalk.decide(...) -> the ONLY write, and it is to the crosswalk
-> evaluate() -> did "high" mean high?Evidence, not vibes. A proposal carries the kinds of evidence it found:
name_exact, name_close, address, domain, zip. Tiers are a contract
with the reviewer:
tier | rule | who acts |
high | exact normalized name plus address or email domain (a public provider such as gmail.com does not count), or close name plus both; no competing candidate, no stale marker, no entity-suffix change, no hold | still a person, but the packet is short |
medium | one strong signal, or two with a competitor within the ambiguity margin | a person, with the discrepancies laid out |
low | a single weak signal | shown so nothing is silently dropped |
A field-authority matrix instead of a fourth vendor master. authority.py
says which system is trusted for which field. Legal name comes from the tax
document (status only). Accounting defaults come from the ledger. Department
and owner come from ops. Addresses are three different things, not one field to
overwrite. It is data, so a finance reviewer can read and approve it.
A redaction boundary with a tripwire. redact.scrub strips restricted
fields; redact.assert_clean raises if one reaches an output. The matcher never
reads a TIN. Matching on tax IDs would be both a data-boundary violation and a
false comfort: the crosswalk exists to reconcile the records people actually
look at.
The only write is a human decision. Crosswalk.decide(index, reviewer, approve) is the single path to linked. It writes to the crosswalk table
(native IDs, status, evidence, reviewer, date). It never writes to a source
system. There is no tool that does.
The eval
The pilot's gate: zero incorrect high-confidence matches. Not "high precision." Zero. Everything else is reported so the number can be argued with.
Default benchmark (--seed 7, 40 vendors, 120 records, 119 true cross-system
pairs). The generator includes near-duplicate businesses, vendors missing from
one system, stale duplicate ledger records, entity reorganizations (LLC to
Inc.), address-type drift, and missing or expired tax documents.
tier | proposed | correct | incorrect | precision | coverage of true pairs |
high | 98 | 98 | 0 | 1.000 | 0.824 |
medium | 21 | 21 | 0 | 1.000 | 0.176 |
low | 34 | 0 | 34 | 0.000 | 0.000 |
False negatives: 0. Proposals demoted for a competing candidate: 12. The gate
holds across seeds 1, 2, 3, 7, 11, 42, 99, 123, 500, 2026 (tests/ checks
seven of them). High-tier coverage ranges 0.70 to 0.84 across seeds; the rest
lands in medium, where a person looks. That is the design, not a shortfall.
Hold rule (added October 2026 after an outside review): a shared public email
provider such as gmail.com is recorded as public_email_provider but never
counts as evidence, and the same exact name at two different addresses is held
for a person. The synthetic vendors use business domains, so the numbers above
are unchanged; tests/ covers the same-name, two-Gmail case, which now stays in
medium.
What the eval does not measure, because synthetic data cannot: reviewer change rate (how often a person materially edits the packet), active minutes per completed vendor against a baseline, and cost per packet. Those are Week 1 baseline measurements in the real pilot, not claims in a README.
Run it yourself:
vendor-crosswalk eval --seed 2026
vendor-crosswalk packet 3
python -m unittest discover -s testsThe MCP server
python -m vendor_crosswalk.serverTools: load_synthetic_records, find_vendor_matches, check_crosswalk,
build_review_packet_tool, run_eval. All read only against the sources. The
model can propose, check, and package. It cannot link, and it cannot write to a
source system, because the server registers no such tool: the human decision
(record_reviewer_decision, which calls Crosswalk.decide) exists in the module
for the CLI and a future authenticated review UI, and is deliberately kept out
of the MCP tool registry. tests/test_mcp_boundary.py fails if that changes.
Authority is not something an agent should be able to ask for.
Demo limitation: find_vendor_matches rebuilds the in-memory crosswalk, so
repeated matching discards earlier decisions. A real deployment would persist
decisions in a separate store; this synthetic demo does not.
Claude Desktop config:
{ "mcpServers": { "vendor-crosswalk": { "command": "python", "args": ["-m", "vendor_crosswalk.server"] } } }Design decisions
Tiers over scores. A score of 0.83 invites arguing with the number. A tier invites checking the evidence. The score is still there for sorting.
Ambiguity demotes both candidates. If a record's best and second-best matches in a target system are within 0.10, both are demoted and each names the other. Nothing is quietly picked.
Stale markers and suffix changes cap the tier. "(do not use)" and "OLD" are things people leave in systems on purpose. An LLC that became an Inc. is probably the same vendor. Probably is a person's call.
Standard library only. The matcher, crosswalk, packet, and eval have no dependencies. The MCP server is an optional extra.
The generator is honest. It is built to include the cases that break naive matching. If you make it easier, the eval stops meaning anything.
Files
src/vendor_crosswalk/
authority.py field-authority matrix + RESTRICTED set (data, not code)
redact.py scrub + tripwire
normalize.py names, addresses, domains, token similarity
match.py propose(): evidence, tiers, ambiguity, notes
crosswalk.py the linking table; decide() is the only write
packet.py build_review_packet(): checklist, discrepancies, change lists
evaluate.py the gate, precision and coverage by tier, false negatives
synth.py synthetic benchmark with hidden ground truth
server.py MCP server (FastMCP, stdio)
cli.py demo / eval / packet
tests/ unittest; the gate is a test
HUMANS.md who is on the other end, and what they get back
STOP.md when this must be paused, and who decidesWho made this
Kai Chieh Tu. Dramaturg by training, finance operator by trade, builds with Claude Code. I wrote the pilot this comes from because our vendor setup was a control problem dressed as a typing problem. MIT license. No real vendor, person, or institution appears in this repository.
This server cannot be deployed
Maintenance
Related MCP Connectors
Public read-only Précis Finance MCP demo with synthetic data; no account or credentials required.
Read-only, deterministic AI triage and readiness tools implementing Sophon's published rubrics.
Evaluate supplied rules, save deterministic decisions, and read/export owned records. Paid pilot.
SAM.gov contract opportunities and entity lookup (BYOK) plus USASpending federal award data.
Related MCP Servers
- FlicenseAqualityBmaintenanceProvides deterministic tools and resources for order-to-cash deployment, enabling payment investigation, collection prioritization, and controlled benchmark runs on synthetic ERP data.4-
- AlicenseNot gradedqualityFmaintenanceEnables reviewing grant application text, running deterministic eval cases, and executing Office of Research Aid packet workflows through MCP.Apache 2.0
- AlicenseNot gradedqualityCmaintenanceEnables natural-language-driven purchase-to-pay workflows against mock procurement and payment APIs, including invoice matching, exception handling, human approvals, and idempotent payment scheduling with a tamper-evident audit log.MIT
- AlicenseAqualityBmaintenanceEnables LLM agents to query and reconcile an accounts-payable ledger through safe read-only SQL, deterministic invoice-to-payment matching, and exception explanations for underpayments, duplicates, currency mismatches, and overdue invoices. Ships with a synthetic ledger, golden-set evals, and a month-end review workflow so quality can be measured in CI.4MIT