InferMap

io.github.benseverndev-ossPublished by GitHub account benseverndev-ossv0.7.0Updated Oct 24, 2026

Map messy columns to a known schema — 7 scorers, domain dictionaries, F1 0.84. Zero config.

VerifiedStreamable HTTPWeb executableDatabasesData & Analytics

Overview

AI-generated overview

InferMap is a schema-mapping engine exposed as an MCP server that lets an assistant match messy source columns to a known target schema with confidence scores…

What you can do
  • Map source and target field collections (CSV, Parquet, XLSX, DataFrames, in-memory records, SQLite/Postgres/DuckDB tables) and get one-to-one column matches with confidence scores and explanations
  • Inspect the schema of a file or database table
  • Save mappings to a reusable config file and reload them without re-inference
  • Apply a saved mapping to rename columns in a dataset
  • Validate a saved config against a source, including required fields
  • Tune the engine with domain dictionaries, scorer weights, custom scorers and confidence calibration
When to use it
Useful when you need to reconcile a CRM export, vendor file or legacy table with a canonical schema, or to build repeatable column-renaming and validation steps in a data pipeline. It fits data engineering and analytics work where column names differ but the meaning overlaps.
Requirements
Available as a remote streamable HTTP endpoint or as a local process; the Python package runs via uvx and is also installable from PyPI, with a TypeScript package on npm. Optional database extras are needed for Postgres, MySQL or DuckDB providers. No authentication, environment variables or headers are declared.
Before you install
The README states the repository has moved into another monorepo and this repo is archived, so check which version the endpoint serves. Mappings are inferred, not guaranteed; review low-confidence matches before applying them to real data. Database providers may read table schemas and sample values from connected databases.

Installation

In SourceWeft

  1. Open InferMap in the dashboard and add it to a workspace.
  2. Enable the server for the chats that should use its tools.

Web executable via Streamable HTTP. Remote servers run from the web runtime once configured in a workspace.

Use with an agent

Paste this prompt into Claude Code, Codex, Cursor or another agent — or into SourceWeft chat. The agent reads this server's install guide, shows you its publisher, endpoint and sign-in, and adds it to your client once you agree. Keys never go into a command.

Read https://sourceweft.com/mcp/io.github.benseverndev-oss%2Finfermap/install.md and connect the MCP server it describes to my client. Before changing any config, show me its publisher, endpoint and sign-in method and wait for my OK. Never put an API key in a command or in a file I would commit; ask me where it should come from.

Read the install guide the agent follows

Other MCP clients

Add this to your client's mcpServers config.

{
  "mcpServers": {
    "infermap": {
      "type": "http",
      "url": "https://infermap-mcp-production.up.railway.app/mcp/"
    }
  }
}

README

Moved. This repo has moved into the benzsevern/goldenmatch monorepo at packages/python/infermap (and packages/typescript/infermap)/. This repo is archived; new development happens in the monorepo.

infermap

Inference-driven schema mapping engine.
Map messy source columns to a known target schema — accurately, explainably, with zero config.
Built by Ben Severn.

[PyPI] [npm] [PyPI downloads] [npm downloads] [CI]

[Python 3.11+] [Node 20+] [TypeScript] [Edge runtime] [Parity] [License: MIT]

📖 Wiki · 🌐 Docs · 🧪 Examples · 💬 Discussions · 🐛 Issues


infermap is a schema-mapping engine. Give it any two field collections (CSVs, DataFrames, database tables, in-memory records) and it figures out which source field corresponds to which target field, with confidence scores and human-readable reasoning. Available as a Python package on PyPI and a TypeScript package on npm, with mapping decisions verified bit-for-bit by a shared golden-test parity suite.

Table of contents

Install

Python

bash
pip install infermap

Optional database extras:

bash
pip install infermap[postgres]   # psycopg2-binarypip install infermap[mysql]      # mysql-connector-pythonpip install infermap[duckdb]     # duckdbpip install infermap[all]        # all extras

TypeScript / Next.js

bash
npm install infermap

Zero runtime dependencies in the core entrypoint. Compatible with Next.js Server Components, Route Handlers, Server Actions, and the Edge Runtime out of the box. See the package README for the full reference.

Quick start

Python

python
import infermap
# Map a CRM export CSV to a canonical customer schemaresult = infermap.map("crm_export.csv", "canonical_customers.csv")
for m in result.mappings:    print(f"{m.source} -> {m.target}  ({m.confidence:.0%})")# fname -> first_name  (97%)# lname -> last_name   (95%)# email_addr -> email  (91%)
# Apply mappings to rename DataFrame columnsimport polars as pldf = pl.read_csv("crm_export.csv")renamed = result.apply(df)
# Save mappings to a reusable config fileresult.to_config("my_mapping.yaml")
# Reload later — no re-inference neededsaved = infermap.from_config("my_mapping.yaml")

TypeScript

ts
import { map } from "infermap";
const crm = [  { fname: "John", lname: "Doe", email_addr: "[email protected]" },  { fname: "Jane", lname: "Smith", email_addr: "[email protected]" },];
const canonical = [  { first_name: "", last_name: "", email: "" },];
const result = map({ records: crm }, { records: canonical });
for (const m of result.mappings) {  console.log(`${m.source} → ${m.target}  (${m.confidence.toFixed(2)})`);}// fname       → first_name  (0.44)// lname       → last_name   (0.48)// email_addr  → email       (0.69)

For Next.js, drop it directly into a Route Handler — works on Edge Runtime with zero config:

ts
// app/api/infer/route.tsimport { map } from "infermap";export const runtime = "edge";
export async function POST(req: Request) {  const { sourceCsv, targetCsv } = await req.json();  const result = map({ csvText: sourceCsv }, { csvText: targetCsv });  return Response.json(result);}

How it works

Each field pair runs through a pipeline of 7 scorers. Each scorer returns a score in [0.0, 1.0] or abstains (None/null). The engine combines scores via weighted average (requiring at least 2 contributors), then uses the Hungarian algorithm for optimal one-to-one assignment.

ScorerWeightWhat it detects
ExactScorer1.0Case-insensitive exact name match
AliasScorer0.95Known field aliases (fname ↔ first_name, tel ↔ phone) + domain dictionaries
InitialismScorer0.75Abbreviation-style names (assay_id ↔ ASSI, confidence_score ↔ CONSC)
PatternTypeScorer0.7Semantic type from sample values — email, date_iso, phone, uuid, url, zip, currency
ProfileScorer0.5Statistical profile similarity — dtype, null rate, unique rate, length, cardinality
FuzzyNameScorer0.4Jaro-Winkler similarity on normalized field names (with common-prefix canonicalization)
LLMScorer0.8Pluggable LLM-backed scorer (stubbed by default)

The engine also applies common-prefix canonicalization — automatically stripping schema-wide prefixes like prospect_ so that City vs prospect_City is compared as City vs City. And optional confidence calibration transforms raw scores into calibrated probabilities post-assignment (ECE from 0.46 to 0.005 on real-world data).

Read the full architecture →

Features

PythonTypeScript
7 built-in scorers✅✅
Hungarian assignment✅ (scipy)✅ (vendored)
Custom scorers@infermap.scorerdefineScorer()
Domain dictionaries✅ (YAML)✅ (inlined)
Confidence calibration✅ (Identity/Isotonic/Platt)✅
Score matrix inspection✅✅
In-memory dataPolars, Pandas, list[dict]Array<Record>
File providersCSV, Parquet, XLSXCSV, JSON
Schema definition filesYAML + JSONJSON
Database providersSQLite, Postgres, DuckDBSQLite, Postgres, DuckDB
Engine configYAMLJSON
Saved mapping formatYAMLJSON
CLI✅ (Typer)✅ (node:util)
Apply to DataFrame✅❌ (CSV rewrite via CLI)
Edge-runtime compatible❌✅
Zero runtime depsn/a✅
Accuracy benchmark✅ (162 cases, F1 0.84)✅ (parity within 0.0005)

Full feature parity matrix →

Which package should I use?

If you are…Use
Building a Python data pipeline or notebookPython
Building a Next.js app, Node service, or browser toolTypeScript
Running mapping in a serverless edge functionTypeScript (zero Node built-ins)
Doing ad-hoc CSV exploration on the command linePython CLI has more features; TS CLI is leaner
Both — Python backend + Next.js admin UIBoth — outputs are interoperable via the JSON config format

What's new in v0.3

+18.3pp F1 on real-world data from four compounding improvements:

v0.2 baseline    F1 0.657+ min_conf 0.2   F1 0.765  (+10.8pp — empirically tuned threshold)+ prefix-strip   F1 0.821  (+5.6pp  — City vs prospect_City now works)+ InitialismScorer F1 0.840 (+1.9pp  — ASSI, CONSC, RELATIT now work)

New features:

  • Domain dictionaries — MapEngine(domains=["healthcare"]) loads curated aliases for your domain. Ships: generic (default), healthcare, finance, ecommerce. See examples/09_domain_dictionaries.py.
  • Confidence calibration — MapEngine(calibrator=cal) transforms raw scores into calibrated probabilities. Ships: IsotonicCalibrator, PlattCalibrator. Valentine ECE: 0.46 → 0.005. See examples/10_calibration.py.
  • InitialismScorer — matches abbreviation-style column names (assay_id ↔ ASSI). ChEMBL F1: 0.524 → 0.819.
  • Common-prefix canonicalization — automatically strips prospect_, assays_, etc. before fuzzy matching.
  • Valentine corpus — 82 real-world schema-matching cases from the Valentine benchmark for accuracy testing.
  • Full TypeScript parity — all new features ported. 186 TS tests. Benchmark F1 within 0.0005 of Python.

Custom scorers

Python

python
import infermapfrom infermap.types import FieldInfo, ScorerResult
@infermap.scorer("prefix_scorer", weight=0.8)def prefix_scorer(source: FieldInfo, target: FieldInfo) -> ScorerResult | None:    if source.name[:3].lower() != target.name[:3].lower():        return None    return ScorerResult(score=0.85, reasoning=f"Shared prefix '{source.name[:3]}'")
from infermap.engine import MapEnginefrom infermap.scorers import default_scorers
engine = MapEngine(scorers=[*default_scorers(), prefix_scorer])

TypeScript

ts
import { MapEngine, defaultScorers, defineScorer, makeScorerResult } from "infermap";
const prefixScorer = defineScorer(  "prefix_scorer",  (source, target) => {    if (source.name.slice(0, 3).toLowerCase() !== target.name.slice(0, 3).toLowerCase()) {      return null;    }    return makeScorerResult(0.85, `Shared prefix '${source.name.slice(0, 3)}'`);  },  0.8 // weight);
const engine = new MapEngine({  scorers: [...defaultScorers(), prefixScorer],});

CLI examples

The CLI works the same way in both packages:

bash
# Map two files and print a reportinfermap map crm_export.csv canonical_customers.csv
# Map and save the config (Python: --save, TS: -o)infermap map crm_export.csv canonical_customers.csv -o mapping.json
# Apply a saved mapping to rename columnsinfermap apply crm_export.csv --config mapping.json --output renamed.csv
# Inspect the schema of a file or DB tableinfermap inspect crm_export.csvinfermap inspect "sqlite:///mydb.db" --table customers
# Validate a saved config against a sourceinfermap validate crm_export.csv --config mapping.json --required email,id --strict

Config reference

Both packages accept an engine config (scorer weight overrides + alias extensions). Python uses YAML, TypeScript uses JSON; the shape is identical.

yaml
# Python: infermap.yamldomains:  - healthcare  - financescorers:  LLMScorer:    enabled: false  FuzzyNameScorer:    weight: 0.3aliases:  order_id:    - order_num    - ord_no
json
// TypeScript: infermap.config.json{  "scorers": {    "LLMScorer":       { "enabled": false },    "FuzzyNameScorer": { "weight": 0.3 }  },  "aliases": {    "order_id": ["order_num", "ord_no"]  }}

See infermap.yaml.example for a full annotated reference.

Documentation

Author

Ben Severn

License

MIT

Source: README.md at commit b1bcee5

Tools

0
SourceWeft has not read this server's tools yet.

Version history

1
  1. v0.7.0LatestSep 16, 2026

Score

85% of the applicable checks met

How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.

  • Reached in the last 30 daysRequired25 pts
  • Auth declaredRequired10 pts
  • Tools listedRequired15 pts

    Answer tools/list without a credential, or ship a package that starts in a sandbox without one.

  • DescriptionRequired15 pts
  • Logo10 pts
  • Documentation15 pts
  • License10 pts