name-variants
name-variants
"Chan" is simultaneously 陳, 陈 and 찬 and จัน — lookup() returns all of them.
Every romanization system produces a member of an equivalence class: no canonical form, no ordering dependency, no silent data loss. share_cluster("Hsu", "Xu") is True. lookup("Chan") returns a Chinese surname cluster and a Korean given-name cluster, sorted by bearer count.
Available as a Python library, CLI, pandas accessor, and Model Context Protocol (MCP) server.
pip install name-variantsThe core idea
A NameCluster is a frozenset of co-equal representations. 陳, 陈, chen, chan, tan, chern are all members of the same Chinese surname cluster — none is more "real" than another. lookup() returns every cluster that contains your query, sorted by frequency:
from name_variants import lookup, share_cluster
clusters = lookup("Chan")
# [NameCluster(language='chinese', 8 forms),
# NameCluster(language='korean_given', 3 forms)]
# Both Chinese scripts are in the same cluster — co-equal
assert "陳" in clusters[0] # Traditional
assert "陈" in clusters[0] # Simplified
# Membership is case-insensitive
assert "CHAN" in clusters[0]
# Ambiguity is surfaced, not suppressed
assert len(clusters) == 2 # Chinese AND Korean, not one-or-the-otherRelated MCP server: indic-normalize-mcp
API
lookup() — all matching clusters
from name_variants import lookup
lookup("Chan")
# [NameCluster(language='chinese', 8 forms),
# NameCluster(language='korean_given', 3 forms)]
lookup("Nguyen")
# [NameCluster(language='vietnamese', 4 forms)]
lookup("Smith")
# []Results are sorted by frequency descending — most statistically likely interpretation first.
share_cluster() — equivalence check
from name_variants import share_cluster
share_cluster("Chan", "Chen") # True — same Chinese cluster
share_cluster("Chou", "Zhou") # True — Wade-Giles = Pinyin
share_cluster("Chiang", "Jiang") # True — Chiang Kai-shek / 蒋介石
share_cluster("Hsu", "Xu") # True — Taiwan diaspora romanization
share_cluster("Tsao", "Cao") # True — Ts'ao Ts'ao / 曹操
share_cluster("Chan", "Kim") # False — different names
share_cluster("", "Chan") # False — empty inputdialect() — Chinese romanization system tag
from name_variants import dialect
dialect("chen") # "mandarin_pinyin"
dialect("chan") # "cantonese"
dialect("tan") # "hokkien"
dialect("陳") # "traditional"
dialect("chou") # "wade_giles"
dialect("hsu") # "wade_giles"
dialect("Smith") # Nonenormalize() — text preprocessing
from name_variants import normalize
normalize(" NGUYỄN ") # "nguyễn"
normalize("Nguyễn", strip_diacritics=True) # "nguyen"
normalize("chan") # strips zero-width spacesCLI
nv lookup Chan
# [chinese] (~90M bearers)
# 陳 陈 chan chen tan ...
# [korean_given]
# 찬 chan chahn
nv match Chan Chen # true
nv match Chan Kim # false
nv match --exit-code Chan Chen && echo same # shell-scripting friendly
nv canonicalize-csv names.csv --col name --out out.csv
# adds {name}_canonical column
nv dedupe names.csv --col name --out out.csv
# adds cluster_id column grouping romanization variantsPandas
pip install "name-variants[pandas]"import pandas as pd
import name_variants # registers .nv accessor
s = pd.Series(["Chan", "Chen", "Smith", "Park"])
s.nv.lookup()
# 0 [NameCluster(chinese, ...), NameCluster(korean_given, ...)]
# 1 [NameCluster(chinese, ...)]
# 2 []
# 3 [NameCluster(korean, ...)]
s.nv.cluster_id()
# 0 a3f2b1c4d5e6 ← same as row 1 (Chan and Chen share chinese cluster)
# 1 a3f2b1c4d5e6
# 2 ← empty string for unknown
# 3 9b8c7d6e5f4a
a = pd.Series(["Chan", "Park"])
b = pd.Series(["Chen", "Bak"])
a.nv.share_cluster_with(b) # [True, True]MCP server (Model Context Protocol)
name-variants ships a built-in Model Context Protocol server, exposing name lookup as MCP tools that any MCP-compatible AI client (Claude Desktop, Claude Code, Cursor, etc.) can call directly.
Claude Code:
claude mcp add name-variants -- uvx --from "name-variants[mcp]" nv-mcpClaude Desktop — add to claude_desktop_config.json:
{
"mcpServers": {
"name-variants": {
"command": "uvx",
"args": ["--from", "name-variants[mcp]", "nv-mcp"]
}
}
}Three MCP tools are exposed:
Tool | Arguments | Returns |
|
| list of |
|
|
|
|
| romanization system string or |
Language tables
Language | Entries | Coverage |
| 140 | Pinyin + Wade-Giles + Cantonese + Hokkien + Hakka + Teochew + Traditional |
| 143 | Hepburn + macron variants |
| 100 | Revised Romanization + McCune-Reischauer |
| 92 | Multiple transliteration systems |
| 84 | Diacritics + stripped forms |
| 79 | Multiple transliteration systems |
| 77 | — |
| 80 | — |
| 80 | — |
| 75 | — |
| 74 | Dotted-İ variants |
| 60 | — |
| 68 | — |
| 56 | — |
| 53 | — |
| 120 | Common given-name characters with Pinyin |
| 70 | Common given-name syllables |
| 107 | Common given-name kanji |
from name_variants import ALL_TABLES
list(ALL_TABLES.keys()) # all 18 table namesChinese writing systems
System | Examples |
Mandarin Pinyin | Zhou, Zhang, Wang, Xu |
Wade-Giles | Chou, Chang, Wang, Hsu, Tsao, Kuo, Hsieh |
Cantonese (Jyutping/Yale) | Chan, Wong, Ng, Lam, Tsui |
Hokkien/Min Nan | Tan, Ng, Lim, Goh |
Hakka | Fong, Thong |
Teochew | Teo, Ng |
Postal romanization | Peking, Nanking, Chungking |
Traditional characters | 陳, 劉, 張, 楊, 趙 |
Simplified characters | 陈, 刘, 张, 杨, 赵 |
NameCluster reference
@dataclass(frozen=True)
class NameCluster:
forms: frozenset[str] # all representations — co-equal
language: str # "chinese", "korean", "vietnamese", etc.
frequency: int | None # approximate global bearer count
def __contains__(self, text: str) -> bool # case-insensitive
def __iter__(self) # iterate all forms
def __len__(self)Why equivalence classes instead of a canonical key?
A canonical-key model forces a false choice: "Chan" must map to either 陳, 陈 or 찬, not both. Table ordering becomes load-bearing — whichever table is consulted last wins. Romanizations must be stripped from given-name tables to prevent collisions.
The NameCluster model eliminates this: every romanization system's output is just another member of a frozenset. lookup() returns all matching clusters. Ambiguity is surfaced, not suppressed. The most likely interpretation comes first by frequency.
Contributing
git clone https://github.com/SecurityRonin/name-variants
cd name-variants
pip install -e ".[dev]"
pytestData files are in name_variants/*_names.py and name_variants/*_surnames.py. Each entry is a plain Python dict — easy to read and edit:
"陈": {
"forms": ["陳", "chen", "chan", "tan", ...],
"frequency": 90_000_000,
"dialects": {
"chen": "mandarin_pinyin",
"chan": "cantonese",
"tan": "hokkien",
"陳": "traditional",
},
},Adding a new variant is one edit to one entry — forms, frequency, and dialect tag colocated.
Privacy Policy · Terms of Service · © 2026 Security Ronin Ltd
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- Flicense-qualityDmaintenanceEnables querying Chinese enterprise business data including company profiles, shareholder information, investments, branch offices, and key personnel through fuzzy search and detailed lookups.Last updated2
- Flicense-qualityBmaintenanceEnables Indic-language transliteration and normalization of Indian names and addresses using Wikidata, Aksharamukha, and OpenStreetMap.Last updated
- AlicenseAqualityBmaintenanceResolves any combination of company name, domain, or LinkedIn URL into the canonical company identity (name, domain, LinkedIn URL) with confidence scores and match method.Last updated143MIT
- Alicense-qualityCmaintenanceScreens names against the US Consolidated Screening List including OFAC SDN and BIS Entity List, keyless.Last updated9MIT
Related MCP Connectors
Indic transliteration plus Indian name, address, PIN, PAN, GSTIN normalization.
Screen names against the US Consolidated Screening List: OFAC SDN, BIS Entity List +10 more lists
Screen names against OFAC, EU, UK, UN sanctions lists; resolve entities via GLEIF. Screening aid.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/SecurityRonin/name-variants'
If you have feedback or need assistance with the MCP directory API, please join our Discord server