Skip to main content
Glama
adithyx-0

CodeWeaver

by adithyx-0
README.md
# CodeWeaver

An MCP server that reads a clinical note and returns ranked ICD-10-CM diagnosis
code candidates with clinical reasoning, validated against official CMS coding
constraints (Excludes1/2 relationships, specificity rules).

Built by **Team EternalBlue** for the **Amrita MCP Hackathon 2026** — HealthTech track.

> **CodeWeaver is a coding/documentation assistance tool, not a diagnostic tool.**
> It does not make or suggest clinical/diagnostic decisions. All output must be
> reviewed by a qualified medical coder.

## What it does

Given free-text clinical note, CodeWeaver:

1. Segments the note into sentences and detects negation/rule-out language
   (`"denies"`, `"no evidence of"`, `"r/o"`, etc.) so ruled-out conditions
   aren't coded.
2. Fuzzy-matches affirmed segments against ICD-10-CM descriptions and
   inclusion terms (token-overlap / Dice coefficient — no ML, no external
   NLP APIs).
3. Runs candidates through a constraint engine:
   - **Excludes1** (mutually exclusive diagnoses) → hard-blocked, flagged for
     provider clarification.
   - **Excludes2** (can co-occur but distinct) → soft-flagged for combination
     code review.
   - **Unspecified/NOS codes** → deprioritised when a more specific code is
     also a valid match.
   - **7th-character extensions** (e.g. initial/subsequent/sequela) → flagged
     with the valid options when a matched code requires one; CodeWeaver does
     not infer which character applies.
4. Returns each candidate with a plain-English reasoning string explaining
   the match and any constraint outcome.

Low-confidence matches (score < 0.5) are never presented as suggestions, even
as a fallback — if nothing clears that bar, CodeWeaver says so explicitly and
recommends provider clarification instead of guessing. In that case it also
live-queries the **NLM Clinical Tables ICD-10-CM API** (full official code
set, U.S. National Library of Medicine, free/no key) as a best-effort,
explicitly unvalidated fallback — those results skip the Excludes1/2 engine
entirely and are labeled as such.

## Scope

Four clinical categories, **1,934 real CMS FY2026 ICD-10-CM codes**:

| Category | Code range | Codes |
|---|---|---|
| Cardiac | I20-I25 | 119 |
| Diabetes | E08-E13 | 322 |
| Respiratory | J00-J99 | 471 |
| Musculoskeletal (curated) | M15-M19, M25, M54, M70-M81 | 1,022 |

Musculoskeletal is a deliberately curated slice — arthritis, joint pain, back
pain/sciatica, soft tissue disorders, and osteoporosis — not the full M00-M99
chapter (~7,100 codes in the source XML), which would have been far too large
and far too thin on local synonym coverage to add responsibly in one pass.

This is not full ICD-10-CM coverage and CodeWeaver does not claim to be.

## Data

- **ICD-10-CM codes**: real CMS FY2026 data, parsed from the official
  `icd10cm-tabular-2026.xml` tabular index (`src/data/parse-icd10-xml.ts`).
  Inclusion terms are supplemented for codes with sparse official terms,
  using lay/clinical synonyms derived strictly from the official
  descriptions — not fabricated clinical claims. Final dataset:
  `src/data/icd10-final.json` (`_dataSource: "cms-fy26-supplemented"`).
- **Clinical notes**: synthetic (not real patient records), covering
  single-condition, multi-condition, negation, and deliberate Excludes1
  conflict scenarios built around real CMS Excludes1 pairs (e.g.
  `E10.9 ↔ E11` for T1DM vs T2DM, `I22.0 ↔ I21` for subsequent MI).

## Tools, resources, and prompts

### Tools

| Name | Input | Returns |
|---|---|---|
| `code_clinical_note` | `note_text` (string), `max_results` (int, default 10) | Ranked candidates split into `suggested` / `flagged` / `blocked`, each with a `reasoning` string, plus a confidence advisory and `externalSuggestions` (unvalidated NLM fallback) if nothing scores highly enough |
| `lookup_icd10_code` | `code` (string, e.g. `"E11.9"`) | Full detail for one code — description, inclusion terms, Excludes1/2 relationships. Normalizes case, whitespace, and missing decimals |

### Resources

| URI | Returns |
|---|---|
| `icd10://codes` | Summary of all 1,934 in-scope codes |
| `icd10://codes/cardiac` | Full detail, cardiac codes |
| `icd10://codes/diabetes` | Full detail, diabetes codes |
| `icd10://codes/respiratory` | Full detail, respiratory codes |
| `icd10://codes/musculoskeletal` | Full detail, musculoskeletal codes (curated slice) |

### Prompts

| Name | What it does |
|---|---|
| `explain_decision` | Takes `code_clinical_note` output and produces a structured coding-rationale prompt for an LLM, for either a `coder` or `clinician` audience, with the required non-diagnostic disclaimer |

## Quick start

```bash
npm install
npm run dev          # start in development mode
```

```bash
npm run build         # compile + copy data files into dist/
npm start              # build, then start
npm run start:prod    # start an existing production build
```

Use [NitroStudio](https://nitrostack.ai/studio) to connect to the running
server and call the tools interactively.

## Testing

Rule-based pipeline logic is covered by lightweight script tests (no test
framework — run directly with `tsx`):

```bash
npx tsx src/modules/icd10/segmentation.test.ts
npx tsx src/modules/icd10/matcher.test.ts
npx tsx src/modules/icd10/constraints.test.ts
npx tsx src/modules/icd10/pipeline.test.ts
```

51/51 assertions passing across all four files, exercising all 15 synthetic
notes end-to-end. Or run `npm test` to execute all four in sequence.

## Architecture

Built on [NitroStack](https://nitrostack.ai) (`@nitrostack/core`), using its
decorator-based `@Tool` / `@Resource` / `@Prompt` pattern with Zod schema
validation. The coding pipeline itself is plain TypeScript, no ML training
and no external NLP APIs — intentionally simple and explainable for a
time-boxed hackathon build:

```
src/modules/icd10/
├── segmentation.ts   # sentence split + negation detection
├── matcher.ts         # Dice-coefficient fuzzy matching + word-form normalization
├── constraints.ts     # Excludes1/2 constraint engine + specificity ranking
├── pipeline.ts        # orchestrates the above end-to-end
├── external-lookup.ts # NLM Clinical Tables fallback (used only when no local match is confident)
├── icd10.tools.ts     # @Tool: code_clinical_note, lookup_icd10_code
├── icd10.resources.ts # @Resource: icd10://codes/*
├── icd10.prompts.ts   # @Prompt: explain_decision
└── icd10.module.ts    # module registration
```

## Known limitations

- Fuzzy matching is pure token-overlap with no term-importance weighting, so
  generic shared words (e.g. "diabetes mellitus", "acute") can produce
  low-score noise across many codes in a category. Excludes1/2 gating and
  the 0.5 suggestion threshold contain this but don't eliminate it.
- A curated set of clinical word-form equivalences (e.g. "diabetic" ↔
  "diabetes", "coughing" ↔ "cough") narrows — but doesn't eliminate — the gap
  from having no general stemmer.
- Negation detection is sentence-level; a negation cue can't reach into an
  adjacent sentence.
- ~85% of in-scope codes have no supplemented inclusion terms and rely on
  matching the raw official CMS description text, which is often too terse or
  formal for how notes are actually phrased — most pronounced in the
  musculoskeletal slice (94% unsupplemented) since it was added last.
- 7th-character code requirements are flagged in a code's `reasoning` string
  with the valid extension characters, but CodeWeaver does not infer which
  one applies (e.g. initial vs. subsequent encounter) — that's left to the
  coder.
- `externalSuggestions` (the NLM Clinical Tables fallback) only fires when no
  local match clears the confidence threshold, is not run through the
  Excludes1/2 engine, and depends on NLM's own word-prefix search semantics —
  it can miss valid codes on word-form mismatches (e.g. "femoral" vs. "femur").

TDQS

A4/5.0

Scored across 2 tools

Disambiguation5/5

The two tools serve clearly distinct purposes: one generates diagnosis code candidates from a clinical note, while the other retrieves detailed information for a specific ICD-10 code. There is no functional overlap.

Naming Consistency5/5

Both tool names follow a consistent verb_noun pattern in snake_case: 'code_clinical_note' and 'lookup_icd10_code'. The naming is predictable and clear.

Tool Count3/5

With only 2 tools, the server feels thin for a coding/ICD-10 domain. While the two covered operations are useful, the scope is narrow and one could expect additional utilities like searching for codes or exploring categories.

Completeness4/5

The server covers the primary workflows: submitting a note for code suggestions and looking up code details. Minor gaps exist, such as no direct search-by-description or category-browsing tool, but the core use cases are addressed.

Maintenance

ActivityStale
ResponsivenessNo issues