mcp-icd10
by stabgan
README.md
# mcp-icd10
Offline MCP server for ICD medical code lookup, search, and crosswalk translation.
**Zero API keys. Zero network calls. Zero data leakage.**
124K codes across three coding systems with 102K bidirectional crosswalk mappings, all running locally in a SQLite database.
[](LICENSE)
[](https://python.org)
[](https://modelcontextprotocol.io)
## Why?
Existing medical terminology services are thin wrappers around remote APIs — rate-limited, network-dependent, and they send your clinical queries to third-party servers. That's a non-starter for HIPAA-conscious workflows.
This server embeds everything locally. A pre-built SQLite database with FTS5 full-text search ships inside the package. Queries resolve in microseconds. Nothing leaves your machine.
## What's included
| System | Codes | Source |
|--------|------:|--------|
| ICD-10-CM FY2026 | 98,186 | CMS.gov (incl. 74,719 billable) |
| ICD-9-CM | 14,567 | CMS GEMs package |
| ICD-10 WHO 2019 | 11,243 | WHO |
| GEMs crosswalk | 102,591 | CMS (bidirectional ICD-9 ↔ ICD-10) |
**Total: 123,996 codes + 102,591 crosswalk mappings**
## Performance
All benchmarks on Apple Silicon. Every query hits an index or FTS5 — no table scans.
| Operation | Latency |
|-----------|---------|
| Exact code lookup | **4.4 µs** |
| Full-text search | **0.23 ms** |
| Category browse | **0.04 ms** |
| Crosswalk translation | **5.9 µs** |
| DB decompress (first run only) | ~38 ms |
## Quick start
### Claude Desktop / Kiro / any MCP client
```json
{
"mcpServers": {
"icd": {
"command": "uvx",
"args": ["mcp-icd10"]
}
}
}
```
### Docker
```bash
docker build -t mcp-icd10 .
```
```json
{
"mcpServers": {
"icd": {
"command": "docker",
"args": ["run", "--rm", "-i", "mcp-icd10"]
}
}
}
```
### Install from source
```bash
git clone https://github.com/stabgan/mcp-icd10.git
cd mcp-icd10
pip install .
mcp-icd10
```
## Tools
### `lookup_code`
Look up a medical code and return its description. Accepts codes with or without dots. Searches across all systems when no system is specified.
```
> lookup_code("E11.9")
[icd10cm] E119: Type 2 diabetes mellitus without complications
> lookup_code("250.00")
[icd9cm] 25000: Diabetes mellitus without mention of complication, type II or unspecified type, not stated as uncontrolled
```
### `search_codes`
Full-text search across all 124K code descriptions. Supports natural language clinical queries.
```
> search_codes("acute myocardial infarction")
Found 20 result(s) for 'acute myocardial infarction':
[icd10cm] I219: Acute myocardial infarction, unspecified
[icd10cm] I2101: ST elevation (STEMI) myocardial infarction involving left main coronary artery
[icd9cm] 41071: Acute myocardial infarction of subendocardial wall, initial episode of care
...
```
### `browse_category`
Browse all codes under a category prefix within a specific system.
```
> browse_category("E11", system="icd10cm")
50 code(s) under 'E11' [icd10cm]:
E1100: Type 2 diabetes mellitus with hyperosmolarity without nonketotic hyperglycemic-hyperosmolar coma
E1101: Type 2 diabetes mellitus with hyperosmolarity with coma
...
```
### `translate_code`
Translate between ICD-9-CM and ICD-10-CM using the CMS General Equivalence Mappings (GEMs).
```
> translate_code("250.00")
Crosswalk for '25000':
25000 → [icd10cm] E119 — Type 2 diabetes mellitus without complications [≈]
> translate_code("E119", source_system="icd10cm")
Crosswalk for 'E119':
E119 → [icd9cm] 25000 — Diabetes mellitus without mention of complication, type II ... [≈]
```
### `get_stats`
Returns code counts per system and total crosswalk mappings.
## Architecture
```
┌─────────────────────────────────────────────┐
│ Claude / Kiro / AI Agent │
│ (MCP Client) │
└──────────────────┬──────────────────────────┘
│ MCP Protocol (stdio)
┌──────────────────▼──────────────────────────┐
│ mcp-icd10 Server │
│ ┌────────────────────────────────────────┐ │
│ │ FastMCP — 5 tools │ │
│ │ lookup · search · browse │ │
│ │ translate · stats │ │
│ └──────────────┬─────────────────────────┘ │
│ ┌──────────────▼─────────────────────────┐ │
│ │ SQLite + FTS5 (read-only) │ │
│ │ 124K codes │ 102K crosswalk mappings │ │
│ │ Integer system IDs │ Bitmask flags │ │
│ └──────────────┬─────────────────────────┘ │
│ ┌──────────────▼─────────────────────────┐ │
│ │ Pre-built DB (5.2 MB gzipped) │ │
│ │ Decompresses on first run → 23 MB │ │
│ └────────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
100% local · zero network calls
```
## How it works
The package ships a pre-built, gzipped SQLite database (`icd.db.gz`, ~5 MB). On first run, it decompresses to ~23 MB and opens in read-only mode with memory-mapped I/O.
Key design decisions:
- **Integer system IDs** (0/1/2) instead of text strings for compact storage and fast comparisons
- **`CROSS JOIN` in FTS queries** forces SQLite's query planner to use the FTS index first, avoiding full table scans on broad search terms
- **No FTS5 ranking** — `ORDER BY rank` forces full-scan scoring on all matches. Instead, we cap at 500 FTS candidates and sort by description length (shorter = more specific = better relevance proxy for medical codes)
- **Bitmask flags** on crosswalk entries pack approximate/no-map/combination into a single integer
- **`WITHOUT ROWID`** on the crosswalk table for clustered primary key access
## Data sources
- **ICD-10-CM FY2026**: [CMS.gov](https://www.cms.gov/medicare/coding-billing/icd-10-codes)
- **ICD-9-CM + GEMs**: [CMS General Equivalence Mappings](https://www.cms.gov/medicare/coding-billing/icd-10-codes/general-equivalence-mappings-gems)
- **ICD-10 WHO 2019**: [WHO ICD-10](https://icd.who.int/browse10/2019/en)
## Limitations
- No ICD-10-PCS (procedure codes), ICD-11, or SNOMED CT
- GEMs crosswalk covers ICD-9-CM ↔ ICD-10-CM only (not WHO codes)
- FTS5 uses Porter stemming — some medical abbreviations may not stem perfectly
- Update frequency depends on new package releases
## License
MIT
TDQS
A4.1/5.0
Scored across 5 tools
Disambiguation5/5
Each tool has a clearly distinct purpose: browsing categories, getting stats, looking up a specific code, searching by description, and translating between systems. No overlap in functionality.
Naming Consistency5/5
All tool names follow a consistent verb_noun pattern in snake_case (e.g., browse_category, lookup_code), making them predictable and easy to understand.
Tool Count5/5
With 5 tools, the set is well-scoped for an ICD-10 lookup and crosswalk tool. It covers essential operations without being bloated or sparse.
Completeness5/5
The tools provide complete coverage for the domain: browsing by category, exact code lookup, full-text search, crosswalk translation, and database statistics. No obvious gaps for the stated purpose.
Maintenance
ActivityInactive
ResponsivenessNo issues