fuzzy-match-mcp
A Python MCP server for deterministic fuzzy text matching using RapidFuzz. It enables text normalization, string comparison, best-match ranking, duplicate grouping, and match explanation via configurable profiles and strategies.
README
Fuzzy Match MCP
A Python-based Model Context Protocol (MCP) server for deterministic fuzzy text matching.
It can normalize text, compare strings, rank possible matches, group duplicate values, and explain why two values match or differ. The server uses RapidFuzz for similarity scoring and can be used from MCP clients such as Cursor.
Features
- Normalize text before comparison
- Compare two strings using multiple similarity algorithms
- Rank the best matches from a list
- Detect and group probable duplicates
- Explain matching results using token overlap
- Use specialized matching profiles for companies, products, and addresses
- Select different scoring strategies
- Run locally through MCP over
stdio - Testable with
pytest
Available MCP tools
normalize_text
Normalizes a text value before fuzzy matching.
Example input:
{
"value": " Müller & Söhne GmbH! ",
"profile": "general"
}
Example result:
{
"original": " Müller & Söhne GmbH! ",
"profile": "general",
"normalized": "muller and sohne gmbh"
}
compare_strings
Compares two strings and returns detailed similarity scores.
Example input:
{
"first": "Deutsche Bank AG",
"second": "Deutsche Bank Aktiengesellschaft",
"threshold": 90,
"profile": "company",
"strategy": "strict"
}
The response includes:
- Normalized values
- Individual similarity scores
- Selected strategy
- Final selected score
- Match decision
find_best_matches
Ranks candidate strings according to their similarity with a query.
Example input:
{
"query": "Samsung Galaxy S24",
"choices": [
"Apple iPhone 15",
"Samsung Galaxy S24 128GB",
"Galaxy S24 Smartphone",
"Google Pixel 9"
],
"limit": 3,
"threshold": 40,
"profile": "product",
"strategy": "weighted"
}
find_duplicate_groups
Groups values that probably represent the same entity.
Example input:
{
"values": [
"Deutsche Bank AG",
"Deutsche-Bank Aktiengesellschaft",
"Deutsche Bank",
"Commerzbank AG",
"Commerz Bank",
"Amazon Germany GmbH"
],
"threshold": 80,
"profile": "company",
"strategy": "strict"
}
This is useful for:
- Customer-data cleanup
- Product-catalog deduplication
- Company-name matching
- Imported CSV cleanup
- Contact-list deduplication
explain_match
Compares two values and explains the result using normalized tokens and score information.
Example input:
{
"first": "Samsung Galaxy S24 128GB Black",
"second": "Galaxy S24 Black 128 GB",
"threshold": 75,
"profile": "product",
"strategy": "strict"
}
The response includes:
- Common tokens
- Tokens found only in the first value
- Tokens found only in the second value
- Token-overlap percentage
- A deterministic explanation
- Final match decision
Matching profiles
The server supports the following normalization profiles.
| Profile | Description |
|---|---|
general |
Lowercases text, removes accents and punctuation, and collapses whitespace |
company |
Applies general normalization and removes common legal company suffixes |
product |
Standardizes product units and joins model names such as S 24 → s24 |
address |
Standardizes common address terms such as street → st |
Company-profile example
Deutsche Bank AG
Deutsche Bank Aktiengesellschaft
Both normalize approximately to:
deutsche bank
Product-profile example
Samsung Galaxy S 24 128 GB
Normalizes to:
samsung galaxy s24 128gb
Matching strategies
| Strategy | Description |
|---|---|
ratio |
Standard character similarity |
partial |
Finds the best matching substring |
token_sort |
Sorts tokens before comparison |
token_set |
Compares unique token sets |
weighted |
Uses RapidFuzz's weighted ratio |
strict |
Uses a composite score designed to reduce permissive partial matches |
The strict strategy excludes partial_ratio from the final composite score because partial matching can be misleading when a short string appears inside a much longer string.
Thresholds
Similarity scores range from 0 to 100.
90–100: very strict80–89: useful default for names and product titles70–79: more permissive- Below
70: may create more false-positive matches
A result is considered a match when:
selected_score >= threshold
The ideal threshold depends on the dataset and the acceptable false-positive rate.
Project structure
fuzzy-match-mcp/
├── .cursor/
│ └── mcp.json
├── fuzzy_match_mcp/
│ ├── __init__.py
│ ├── grouping.py
│ ├── matching.py
│ ├── server.py
│ ├── tools.py
│ └── validators.py
├── tests/
│ ├── __init__.py
│ └── test_matching.py
├── main.py
├── pyproject.toml
├── README.md
└── uv.lock
Requirements
- Python 3.10 or newer
uv- MCP Python SDK
- RapidFuzz
- pytest for development
Installation
Clone the repository:
git clone <your-repository-url>
cd fuzzy-match-mcp
Install the dependencies:
uv sync
If you are creating the project from scratch:
uv add "mcp[cli]" rapidfuzz
uv add --dev pytest
Running the tests
uv run pytest
Running with MCP Inspector
Use MCP Inspector during development:
uv run mcp dev main.py
This opens an MCP development interface where the registered tools can be inspected and called.
Running as a local stdio server
uv run python main.py
The process waits for MCP JSON-RPC messages through standard input.
Do not type into the terminal while the server is running over stdio. A blank terminal input is not a valid MCP JSON-RPC message.
Do not use ordinary print() statements in the server because standard output is reserved for MCP communication. Use logging through standard error instead.
Cursor configuration
Create:
.cursor/mcp.json
Use the following configuration on Windows:
{
"mcpServers": {
"fuzzy-match": {
"type": "stdio",
"command": "${workspaceFolder}/.venv/Scripts/python.exe",
"args": [
"${workspaceFolder}/main.py"
]
}
}
}
Then:
- Open the repository root in Cursor.
- Open Customize → MCPs.
- Enable
fuzzy-match. - Click Reload after changing the registered tools.
- Use Cursor Agent to call the MCP tools.
Cursor starts the Python process automatically. Do not manually run main.py at the same time.
Example Cursor Agent prompts
Normalize text
Use the fuzzy-match normalize_text tool to normalize:
" Müller & Söhne GmbH! "
Use the general profile.
Compare company names
Use the fuzzy-match compare_strings tool.
Compare:
- Deutsche Bank AG
- Deutsche Bank Aktiengesellschaft
Use:
- profile: company
- strategy: strict
- threshold: 90
Rank product matches
Use the fuzzy-match find_best_matches tool.
Query:
Samsung Galaxy S24
Choices:
- Apple iPhone 15
- Samsung Galaxy S24 128GB
- Galaxy S24 Smartphone
- Google Pixel 9
Use the product profile and return the best three matches.
Group duplicates
Use the fuzzy-match find_duplicate_groups tool with:
- Deutsche Bank AG
- Deutsche-Bank Aktiengesellschaft
- Deutsche Bank
- Commerzbank AG
- Commerz Bank
- Amazon Germany GmbH
Use:
- profile: company
- strategy: strict
- threshold: 80
Explain a match
Use the fuzzy-match explain_match tool to compare:
- Samsung Galaxy S24 128GB Black
- Galaxy S24 Black 128 GB
Use:
- profile: product
- strategy: strict
- threshold: 75
How it works
The request flow is:
MCP client
↓
MCP tool in tools.py
↓
Input validation in validators.py
↓
Matching or grouping logic
↓
RapidFuzz
↓
Structured JSON result
Normalization happens before similarity scoring. It can include:
- Unicode case folding
- Accent removal
- Punctuation replacement
- Whitespace cleanup
- Profile-specific transformations
Performance notes
find_best_matches compares one query against each candidate.
find_duplicate_groups performs pairwise comparisons. Its approximate comparison count is:
n × (n - 1) / 2
For this reason, duplicate grouping is intentionally limited to 500 values in the current version.
The grouping implementation uses union-find. If A matches B and B matches C, all three values can be placed in the same group even when A and C do not directly exceed the threshold.
Current limitations
- Duplicate grouping is pairwise and is not intended for very large datasets.
- Company suffixes and address aliases are rule-based and may not cover every country.
- Product normalization supports only a small set of common units.
- Fuzzy matching does not prove that two real-world entities are identical.
- Thresholds should be evaluated against domain-specific examples before automatic merging.
Planned improvements
- Structured record matching
- Batch matching
- Threshold evaluation
- Custom normalization options
- Additional international company suffixes
- CSV import and export
- Better canonical-value selection
- More matching profiles
- MCP resources and reusable prompts
Contributing
Contributions are welcome.
Suggested workflow:
git checkout -b feature/my-change
uv sync
uv run pytest
Before submitting a change:
- Add tests for new behavior
- Keep MCP tools focused
- Avoid writing to standard output
- Preserve structured JSON responses
- Document new profiles and strategies
License
Add your chosen license before publishing the project.
A common choice for an open-source MCP server is the MIT License.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。