@cyanheads/medical-codes-mcp-server 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/AGENTS.md +375 -0
  2. package/CLAUDE.md +375 -0
  3. package/Dockerfile +124 -0
  4. package/LICENSE +201 -0
  5. package/README.md +328 -0
  6. package/changelog/0.1.x/0.1.0.md +24 -0
  7. package/changelog/template.md +127 -0
  8. package/data/medical-codes.db +0 -0
  9. package/dist/config/server-config.d.ts +23 -0
  10. package/dist/config/server-config.d.ts.map +1 -0
  11. package/dist/config/server-config.js +51 -0
  12. package/dist/config/server-config.js.map +1 -0
  13. package/dist/index.d.ts +9 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +54 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/mcp-server/tools/definitions/_render.d.ts +26 -0
  18. package/dist/mcp-server/tools/definitions/_render.d.ts.map +1 -0
  19. package/dist/mcp-server/tools/definitions/_render.js +45 -0
  20. package/dist/mcp-server/tools/definitions/_render.js.map +1 -0
  21. package/dist/mcp-server/tools/definitions/browse-hierarchy.tool.d.ts +51 -0
  22. package/dist/mcp-server/tools/definitions/browse-hierarchy.tool.d.ts.map +1 -0
  23. package/dist/mcp-server/tools/definitions/browse-hierarchy.tool.js +131 -0
  24. package/dist/mcp-server/tools/definitions/browse-hierarchy.tool.js.map +1 -0
  25. package/dist/mcp-server/tools/definitions/check-code.tool.d.ts +42 -0
  26. package/dist/mcp-server/tools/definitions/check-code.tool.d.ts.map +1 -0
  27. package/dist/mcp-server/tools/definitions/check-code.tool.js +91 -0
  28. package/dist/mcp-server/tools/definitions/check-code.tool.js.map +1 -0
  29. package/dist/mcp-server/tools/definitions/get-code.tool.d.ts +52 -0
  30. package/dist/mcp-server/tools/definitions/get-code.tool.d.ts.map +1 -0
  31. package/dist/mcp-server/tools/definitions/get-code.tool.js +165 -0
  32. package/dist/mcp-server/tools/definitions/get-code.tool.js.map +1 -0
  33. package/dist/mcp-server/tools/definitions/list-systems.tool.d.ts +21 -0
  34. package/dist/mcp-server/tools/definitions/list-systems.tool.d.ts.map +1 -0
  35. package/dist/mcp-server/tools/definitions/list-systems.tool.js +81 -0
  36. package/dist/mcp-server/tools/definitions/list-systems.tool.js.map +1 -0
  37. package/dist/mcp-server/tools/definitions/map-codes.tool.d.ts +55 -0
  38. package/dist/mcp-server/tools/definitions/map-codes.tool.d.ts.map +1 -0
  39. package/dist/mcp-server/tools/definitions/map-codes.tool.js +132 -0
  40. package/dist/mcp-server/tools/definitions/map-codes.tool.js.map +1 -0
  41. package/dist/mcp-server/tools/definitions/search-codes.tool.d.ts +48 -0
  42. package/dist/mcp-server/tools/definitions/search-codes.tool.d.ts.map +1 -0
  43. package/dist/mcp-server/tools/definitions/search-codes.tool.js +133 -0
  44. package/dist/mcp-server/tools/definitions/search-codes.tool.js.map +1 -0
  45. package/dist/services/code-index/code-index-service.d.ts +185 -0
  46. package/dist/services/code-index/code-index-service.d.ts.map +1 -0
  47. package/dist/services/code-index/code-index-service.js +530 -0
  48. package/dist/services/code-index/code-index-service.js.map +1 -0
  49. package/dist/services/code-index/detect.d.ts +23 -0
  50. package/dist/services/code-index/detect.d.ts.map +1 -0
  51. package/dist/services/code-index/detect.js +58 -0
  52. package/dist/services/code-index/detect.js.map +1 -0
  53. package/dist/services/code-index/schema.d.ts +55 -0
  54. package/dist/services/code-index/schema.d.ts.map +1 -0
  55. package/dist/services/code-index/schema.js +129 -0
  56. package/dist/services/code-index/schema.js.map +1 -0
  57. package/dist/services/code-index/types.d.ts +71 -0
  58. package/dist/services/code-index/types.d.ts.map +1 -0
  59. package/dist/services/code-index/types.js +24 -0
  60. package/dist/services/code-index/types.js.map +1 -0
  61. package/package.json +92 -0
  62. package/server.json +125 -0
@@ -0,0 +1,55 @@
1
+ /**
2
+ * @fileoverview Shared SQLite schema and code-shape derivation for the bundled
3
+ * medical-codes index. Imported by BOTH the runtime service (read) and the
4
+ * build script (write) so the table layout, FTS configuration, and the
5
+ * parent/chapter/header derivation rules can never drift between the two.
6
+ * @module services/code-index/schema
7
+ */
8
+ import type { SystemId } from './types.js';
9
+ /**
10
+ * Full DDL for the bundled database. Run once by the build/fixture scripts.
11
+ * `codes_fts` is an external-content FTS5 index over `codes` — triggers keep it
12
+ * in sync on insert so the build script only writes `codes`.
13
+ */
14
+ export declare const SCHEMA_SQL = "\nCREATE TABLE codes (\n system TEXT NOT NULL,\n code TEXT NOT NULL,\n short_desc TEXT,\n long_desc TEXT,\n billable INTEGER NOT NULL DEFAULT 0,\n header INTEGER NOT NULL DEFAULT 0,\n chapter TEXT,\n parent TEXT,\n effective TEXT,\n terminated TEXT,\n PRIMARY KEY (system, code)\n) WITHOUT ROWID;\n\nCREATE INDEX idx_codes_parent ON codes (system, parent);\nCREATE INDEX idx_codes_chapter ON codes (system, chapter);\n\nCREATE VIRTUAL TABLE codes_fts USING fts5 (\n system UNINDEXED,\n code UNINDEXED,\n short_desc,\n long_desc,\n tokenize = 'unicode61'\n);\n\nCREATE TABLE pcs_axes (\n position INTEGER NOT NULL,\n value TEXT NOT NULL,\n meaning TEXT NOT NULL,\n PRIMARY KEY (position, value)\n) WITHOUT ROWID;\n\nCREATE TABLE rxnorm_rel (\n rxcui TEXT NOT NULL,\n rel TEXT NOT NULL,\n target TEXT NOT NULL,\n target_type TEXT NOT NULL\n);\n\nCREATE INDEX idx_rxnorm_rel_rxcui ON rxnorm_rel (rxcui, rel);\nCREATE INDEX idx_rxnorm_rel_target ON rxnorm_rel (target, rel);\n\nCREATE TABLE ndc_map (\n ndc TEXT NOT NULL,\n rxcui TEXT NOT NULL\n);\n\nCREATE INDEX idx_ndc_map_ndc ON ndc_map (ndc);\nCREATE INDEX idx_ndc_map_rxcui ON ndc_map (rxcui);\n\nCREATE TABLE build_meta (\n system TEXT PRIMARY KEY,\n release_id TEXT NOT NULL,\n effective_start TEXT,\n effective_end TEXT,\n code_count INTEGER NOT NULL DEFAULT 0,\n source_url TEXT,\n built_at TEXT NOT NULL\n) WITHOUT ROWID;\n";
15
+ /**
16
+ * Insert a `codes` row and mirror it into the FTS index in one call. The build
17
+ * and fixture scripts use this so FTS population is centralized. `code` is the
18
+ * storage form (no dots).
19
+ */
20
+ export declare const INSERT_CODE_SQL = "INSERT INTO codes (system, code, short_desc, long_desc, billable, header, chapter, parent, effective, terminated)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
21
+ export declare const INSERT_FTS_SQL = "INSERT INTO codes_fts (system, code, short_desc, long_desc) VALUES (?, ?, ?, ?)";
22
+ /**
23
+ * Re-insert a dot into an ICD-10-CM storage code for display: a dot after the
24
+ * 3-character category when there are subsequent characters (`A0101` → `A01.01`).
25
+ * Storage codes are dot-free; display codes carry the dot the way clinicians write
26
+ * them. ICD-10-PCS, HCPCS, and RXCUI have no dot — returned unchanged.
27
+ */
28
+ export declare function displayCode(system: SystemId, storageCode: string): string;
29
+ /**
30
+ * Normalize a user-supplied code to its storage form: uppercase, trimmed, and —
31
+ * for ICD-10-CM — with the dot stripped so `A01.01` and `A0101` both match the
32
+ * stored `A0101`.
33
+ */
34
+ export declare function storageCode(rawCode: string): string;
35
+ /**
36
+ * Derive the parent of an ICD-10-CM code: strip the last character. `A0101` →
37
+ * `A010` → `A01`. Returns null at the 3-character category root (a category has
38
+ * no parent code). Dots are already absent in storage form.
39
+ */
40
+ export declare function icd10cmParent(code: string): string | null;
41
+ /**
42
+ * Derive the parent of a HCPCS code: the single-letter range bucket it belongs
43
+ * to (e.g. `J0120` → `J`). The bucket itself (a 1-char "code") has no parent.
44
+ */
45
+ export declare function hcpcsParent(code: string): string | null;
46
+ /**
47
+ * ICD-10-CM chapter label from the first character + leading digits of the
48
+ * category. The 3-char category prefix (letter + 2 digits) is a coarse but
49
+ * stable chapter bucket the order file doesn't carry explicitly; using the
50
+ * category-letter range keeps `chapter` queryable without a separate lookup
51
+ * table. Returns the leading letter, which buckets codes into their A/B/C…
52
+ * alpha chapter — enough for browse grouping.
53
+ */
54
+ export declare function icd10cmChapterLetter(code: string): string;
55
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/services/code-index/schema.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C;;;;GAIG;AACH,eAAO,MAAM,UAAU,u+CA4DtB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,eAAe,gKACa,CAAC;AAE1C,eAAO,MAAM,cAAc,oFAAoF,CAAC;AAEhH;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAIzE;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAEzD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAEvD;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEzD"}
@@ -0,0 +1,129 @@
1
+ /**
2
+ * @fileoverview Shared SQLite schema and code-shape derivation for the bundled
3
+ * medical-codes index. Imported by BOTH the runtime service (read) and the
4
+ * build script (write) so the table layout, FTS configuration, and the
5
+ * parent/chapter/header derivation rules can never drift between the two.
6
+ * @module services/code-index/schema
7
+ */
8
+ /**
9
+ * Full DDL for the bundled database. Run once by the build/fixture scripts.
10
+ * `codes_fts` is an external-content FTS5 index over `codes` — triggers keep it
11
+ * in sync on insert so the build script only writes `codes`.
12
+ */
13
+ export const SCHEMA_SQL = `
14
+ CREATE TABLE codes (
15
+ system TEXT NOT NULL,
16
+ code TEXT NOT NULL,
17
+ short_desc TEXT,
18
+ long_desc TEXT,
19
+ billable INTEGER NOT NULL DEFAULT 0,
20
+ header INTEGER NOT NULL DEFAULT 0,
21
+ chapter TEXT,
22
+ parent TEXT,
23
+ effective TEXT,
24
+ terminated TEXT,
25
+ PRIMARY KEY (system, code)
26
+ ) WITHOUT ROWID;
27
+
28
+ CREATE INDEX idx_codes_parent ON codes (system, parent);
29
+ CREATE INDEX idx_codes_chapter ON codes (system, chapter);
30
+
31
+ CREATE VIRTUAL TABLE codes_fts USING fts5 (
32
+ system UNINDEXED,
33
+ code UNINDEXED,
34
+ short_desc,
35
+ long_desc,
36
+ tokenize = 'unicode61'
37
+ );
38
+
39
+ CREATE TABLE pcs_axes (
40
+ position INTEGER NOT NULL,
41
+ value TEXT NOT NULL,
42
+ meaning TEXT NOT NULL,
43
+ PRIMARY KEY (position, value)
44
+ ) WITHOUT ROWID;
45
+
46
+ CREATE TABLE rxnorm_rel (
47
+ rxcui TEXT NOT NULL,
48
+ rel TEXT NOT NULL,
49
+ target TEXT NOT NULL,
50
+ target_type TEXT NOT NULL
51
+ );
52
+
53
+ CREATE INDEX idx_rxnorm_rel_rxcui ON rxnorm_rel (rxcui, rel);
54
+ CREATE INDEX idx_rxnorm_rel_target ON rxnorm_rel (target, rel);
55
+
56
+ CREATE TABLE ndc_map (
57
+ ndc TEXT NOT NULL,
58
+ rxcui TEXT NOT NULL
59
+ );
60
+
61
+ CREATE INDEX idx_ndc_map_ndc ON ndc_map (ndc);
62
+ CREATE INDEX idx_ndc_map_rxcui ON ndc_map (rxcui);
63
+
64
+ CREATE TABLE build_meta (
65
+ system TEXT PRIMARY KEY,
66
+ release_id TEXT NOT NULL,
67
+ effective_start TEXT,
68
+ effective_end TEXT,
69
+ code_count INTEGER NOT NULL DEFAULT 0,
70
+ source_url TEXT,
71
+ built_at TEXT NOT NULL
72
+ ) WITHOUT ROWID;
73
+ `;
74
+ /**
75
+ * Insert a `codes` row and mirror it into the FTS index in one call. The build
76
+ * and fixture scripts use this so FTS population is centralized. `code` is the
77
+ * storage form (no dots).
78
+ */
79
+ export const INSERT_CODE_SQL = `INSERT INTO codes (system, code, short_desc, long_desc, billable, header, chapter, parent, effective, terminated)
80
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
81
+ export const INSERT_FTS_SQL = `INSERT INTO codes_fts (system, code, short_desc, long_desc) VALUES (?, ?, ?, ?)`;
82
+ /**
83
+ * Re-insert a dot into an ICD-10-CM storage code for display: a dot after the
84
+ * 3-character category when there are subsequent characters (`A0101` → `A01.01`).
85
+ * Storage codes are dot-free; display codes carry the dot the way clinicians write
86
+ * them. ICD-10-PCS, HCPCS, and RXCUI have no dot — returned unchanged.
87
+ */
88
+ export function displayCode(system, storageCode) {
89
+ if (system !== 'ICD10CM')
90
+ return storageCode;
91
+ if (storageCode.length <= 3)
92
+ return storageCode;
93
+ return `${storageCode.slice(0, 3)}.${storageCode.slice(3)}`;
94
+ }
95
+ /**
96
+ * Normalize a user-supplied code to its storage form: uppercase, trimmed, and —
97
+ * for ICD-10-CM — with the dot stripped so `A01.01` and `A0101` both match the
98
+ * stored `A0101`.
99
+ */
100
+ export function storageCode(rawCode) {
101
+ return rawCode.trim().toUpperCase().replace(/\./g, '');
102
+ }
103
+ /**
104
+ * Derive the parent of an ICD-10-CM code: strip the last character. `A0101` →
105
+ * `A010` → `A01`. Returns null at the 3-character category root (a category has
106
+ * no parent code). Dots are already absent in storage form.
107
+ */
108
+ export function icd10cmParent(code) {
109
+ return code.length > 3 ? code.slice(0, -1) : null;
110
+ }
111
+ /**
112
+ * Derive the parent of a HCPCS code: the single-letter range bucket it belongs
113
+ * to (e.g. `J0120` → `J`). The bucket itself (a 1-char "code") has no parent.
114
+ */
115
+ export function hcpcsParent(code) {
116
+ return code.length > 1 ? code.charAt(0) : null;
117
+ }
118
+ /**
119
+ * ICD-10-CM chapter label from the first character + leading digits of the
120
+ * category. The 3-char category prefix (letter + 2 digits) is a coarse but
121
+ * stable chapter bucket the order file doesn't carry explicitly; using the
122
+ * category-letter range keeps `chapter` queryable without a separate lookup
123
+ * table. Returns the leading letter, which buckets codes into their A/B/C…
124
+ * alpha chapter — enough for browse grouping.
125
+ */
126
+ export function icd10cmChapterLetter(code) {
127
+ return code.charAt(0).toUpperCase();
128
+ }
129
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/services/code-index/schema.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH;;;;GAIG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4DzB,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG;yCACU,CAAC;AAE1C,MAAM,CAAC,MAAM,cAAc,GAAG,iFAAiF,CAAC;AAEhH;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,MAAgB,EAAE,WAAmB;IAC/D,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,WAAW,CAAC;IAC7C,IAAI,WAAW,CAAC,MAAM,IAAI,CAAC;QAAE,OAAO,WAAW,CAAC;IAChD,OAAO,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;AAC9D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACzD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACpD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACjD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;AACtC,CAAC"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * @fileoverview Domain types for the code-index service — the bundled medical
3
+ * code systems, the row shapes the SQLite tables hold, and the query results
4
+ * the tools consume.
5
+ * @module services/code-index/types
6
+ */
7
+ /** The four bundled US healthcare code systems. */
8
+ export type SystemId = 'ICD10CM' | 'ICD10PCS' | 'HCPCS' | 'RXNORM';
9
+ /** All system ids, in canonical display order. */
10
+ export declare const SYSTEM_IDS: readonly SystemId[];
11
+ /** Human-facing label for each system, surfaced in tool output and notices. */
12
+ export declare const SYSTEM_LABELS: Record<SystemId, string>;
13
+ /**
14
+ * A single code row from the `codes` table. The spine of the index — one row
15
+ * per code across all systems. `billable`/`header` are 0/1 integers in SQLite;
16
+ * the service maps them to booleans at the boundary.
17
+ */
18
+ export interface CodeRow {
19
+ /** 1 = billable leaf code. 0 = not billable (header/category or completeness-only). */
20
+ billable: number;
21
+ /** Chapter label (ICD-10-CM/PCS) or range bucket (HCPCS). NULL when not applicable. */
22
+ chapter: string | null;
23
+ /** Storage form of the code — no dots, as it appears in the source order file. */
24
+ code: string;
25
+ /** Effective date (YYYYMMDD) when known. NULL otherwise. */
26
+ effective: string | null;
27
+ /** 1 = non-billable header/category row (ICD-10-CM). 0 otherwise. */
28
+ header: number;
29
+ longDesc: string | null;
30
+ /** Parent code (CM/HCPCS). NULL for ICD-10-PCS (axis-based, not prefix-based). */
31
+ parent: string | null;
32
+ shortDesc: string | null;
33
+ system: SystemId;
34
+ /** Termination date (YYYYMMDD) when the code is retired. NULL = active. */
35
+ terminated: string | null;
36
+ }
37
+ /** A provenance row from `build_meta` — one per bundled system. */
38
+ export interface BuildMetaRow {
39
+ builtAt: string;
40
+ codeCount: number;
41
+ effectiveEnd: string | null;
42
+ effectiveStart: string | null;
43
+ releaseId: string;
44
+ sourceUrl: string | null;
45
+ system: SystemId;
46
+ }
47
+ /** An ICD-10-PCS axis-value row from `pcs_axes`. */
48
+ export interface PcsAxisRow {
49
+ /** What that value means at that position. */
50
+ meaning: string;
51
+ /** Character position 1-7 in the PCS code. */
52
+ position: number;
53
+ /** The single-character axis value. */
54
+ value: string;
55
+ }
56
+ /** An RXCUI relationship edge from `rxnorm_rel`. */
57
+ export interface RxNormRelRow {
58
+ /** Relationship/attribute label, e.g. 'has_ingredient', 'tradename_of', 'NDC'. */
59
+ rel: string;
60
+ rxcui: string;
61
+ target: string;
62
+ /** Concept type of the target, e.g. 'IN', 'BN', 'SCD', 'NDC'. */
63
+ targetType: string;
64
+ }
65
+ /** Discriminated validity status for `medcode_check_code`. */
66
+ export type CheckStatus = 'valid_billable' | 'valid_not_billable' | 'valid_header' | 'terminated' | 'unknown';
67
+ /** Crosswalk directions for `medcode_map_codes`. Drug directions land with RxNorm (phase 2). */
68
+ export type MapDirection = 'parents' | 'children' | 'name_to_rxcui' | 'ndc_to_rxcui' | 'rxcui_to_ndc' | 'rxcui_to_ingredients' | 'rxcui_to_brands';
69
+ /** Map directions that require the RxNorm tables (absent in v1). */
70
+ export declare const DRUG_DIRECTIONS: readonly MapDirection[];
71
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/services/code-index/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,mDAAmD;AACnD,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,UAAU,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEnE,kDAAkD;AAClD,eAAO,MAAM,UAAU,EAAE,SAAS,QAAQ,EAAwD,CAAC;AAEnG,+EAA+E;AAC/E,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAKlD,CAAC;AAEF;;;;GAIG;AACH,MAAM,WAAW,OAAO;IACtB,uFAAuF;IACvF,QAAQ,EAAE,MAAM,CAAC;IACjB,uFAAuF;IACvF,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,kFAAkF;IAClF,IAAI,EAAE,MAAM,CAAC;IACb,4DAA4D;IAC5D,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,kFAAkF;IAClF,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,EAAE,QAAQ,CAAC;IACjB,2EAA2E;IAC3E,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,mEAAmE;AACnE,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,EAAE,QAAQ,CAAC;CAClB;AAED,oDAAoD;AACpD,MAAM,WAAW,UAAU;IACzB,8CAA8C;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,uCAAuC;IACvC,KAAK,EAAE,MAAM,CAAC;CACf;AAED,oDAAoD;AACpD,MAAM,WAAW,YAAY;IAC3B,kFAAkF;IAClF,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,8DAA8D;AAC9D,MAAM,MAAM,WAAW,GACnB,gBAAgB,GAChB,oBAAoB,GACpB,cAAc,GACd,YAAY,GACZ,SAAS,CAAC;AAEd,gGAAgG;AAChG,MAAM,MAAM,YAAY,GACpB,SAAS,GACT,UAAU,GACV,eAAe,GACf,cAAc,GACd,cAAc,GACd,sBAAsB,GACtB,iBAAiB,CAAC;AAEtB,oEAAoE;AACpE,eAAO,MAAM,eAAe,EAAE,SAAS,YAAY,EAMzC,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * @fileoverview Domain types for the code-index service — the bundled medical
3
+ * code systems, the row shapes the SQLite tables hold, and the query results
4
+ * the tools consume.
5
+ * @module services/code-index/types
6
+ */
7
+ /** All system ids, in canonical display order. */
8
+ export const SYSTEM_IDS = ['ICD10CM', 'ICD10PCS', 'HCPCS', 'RXNORM'];
9
+ /** Human-facing label for each system, surfaced in tool output and notices. */
10
+ export const SYSTEM_LABELS = {
11
+ ICD10CM: 'ICD-10-CM',
12
+ ICD10PCS: 'ICD-10-PCS',
13
+ HCPCS: 'HCPCS Level II',
14
+ RXNORM: 'RxNorm',
15
+ };
16
+ /** Map directions that require the RxNorm tables (absent in v1). */
17
+ export const DRUG_DIRECTIONS = [
18
+ 'name_to_rxcui',
19
+ 'ndc_to_rxcui',
20
+ 'rxcui_to_ndc',
21
+ 'rxcui_to_ingredients',
22
+ 'rxcui_to_brands',
23
+ ];
24
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/services/code-index/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,kDAAkD;AAClD,MAAM,CAAC,MAAM,UAAU,GAAwB,CAAC,SAAS,EAAE,UAAU,EAAE,OAAO,EAAE,QAAQ,CAAU,CAAC;AAEnG,+EAA+E;AAC/E,MAAM,CAAC,MAAM,aAAa,GAA6B;IACrD,OAAO,EAAE,WAAW;IACpB,QAAQ,EAAE,YAAY;IACtB,KAAK,EAAE,gBAAgB;IACvB,MAAM,EAAE,QAAQ;CACjB,CAAC;AA4EF,oEAAoE;AACpE,MAAM,CAAC,MAAM,eAAe,GAA4B;IACtD,eAAe;IACf,cAAc;IACd,cAAc;IACd,sBAAsB;IACtB,iBAAiB;CACT,CAAC"}
package/package.json ADDED
@@ -0,0 +1,92 @@
1
+ {
2
+ "name": "@cyanheads/medical-codes-mcp-server",
3
+ "version": "0.1.0",
4
+ "description": "Decode, search, validate, and crosswalk US medical codes — ICD-10-CM, ICD-10-PCS, HCPCS Level II — over a bundled offline index via MCP. STDIO or Streamable HTTP.",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "bin": {
9
+ "medical-codes-mcp-server": "dist/index.js"
10
+ },
11
+ "files": [
12
+ "changelog/",
13
+ "dist/",
14
+ "data/medical-codes.db",
15
+ "README.md",
16
+ "LICENSE",
17
+ "CLAUDE.md",
18
+ "AGENTS.md",
19
+ "Dockerfile",
20
+ "server.json"
21
+ ],
22
+ "scripts": {
23
+ "build": "bun run scripts/build.ts",
24
+ "rebuild": "bun run scripts/clean.ts && bun run scripts/build.ts",
25
+ "clean": "bun run scripts/clean.ts",
26
+ "devcheck": "bun run scripts/devcheck.ts",
27
+ "audit:refresh": "rm -f bun.lock && bun install && bun audit",
28
+ "tree": "bun run scripts/tree.ts",
29
+ "list-skills": "bun run scripts/list-skills.ts",
30
+ "format": "biome check --write .",
31
+ "format:unsafe": "biome check --write --unsafe .",
32
+ "lint:mcp": "bun run scripts/lint-mcp.ts",
33
+ "lint:packaging": "bun run scripts/lint-packaging.ts",
34
+ "bundle": "bun run build && npx -y @anthropic-ai/mcpb pack . dist/medical-codes-mcp-server.mcpb && bun run scripts/clean-mcpb.ts dist/medical-codes-mcp-server.mcpb",
35
+ "changelog:build": "bun run scripts/build-changelog.ts",
36
+ "changelog:check": "bun run scripts/build-changelog.ts --check",
37
+ "publish-mcp": "mcp-publisher login github -token \"$(security find-generic-password -a \"$USER\" -s mcp-publisher-github-pat -w)\" && mcp-publisher publish",
38
+ "release:github": "bun run scripts/release-github.ts",
39
+ "test": "vitest run",
40
+ "start": "node dist/index.js",
41
+ "start:stdio": "MCP_TRANSPORT_TYPE=stdio node dist/index.js",
42
+ "start:http": "MCP_TRANSPORT_TYPE=http node dist/index.js"
43
+ },
44
+ "keywords": [
45
+ "mcp",
46
+ "mcp-server",
47
+ "model-context-protocol",
48
+ "icd-10-cm",
49
+ "icd-10-pcs",
50
+ "hcpcs",
51
+ "medical-codes",
52
+ "medical-coding",
53
+ "healthcare"
54
+ ],
55
+ "mcpName": "io.github.cyanheads/medical-codes-mcp-server",
56
+ "repository": {
57
+ "type": "git",
58
+ "url": "git+https://github.com/cyanheads/medical-codes-mcp-server.git"
59
+ },
60
+ "homepage": "https://github.com/cyanheads/medical-codes-mcp-server#readme",
61
+ "bugs": {
62
+ "url": "https://github.com/cyanheads/medical-codes-mcp-server/issues"
63
+ },
64
+ "author": "cyanheads <casey@caseyjhand.com> (https://github.com/cyanheads/medical-codes-mcp-server#readme)",
65
+ "license": "Apache-2.0",
66
+ "engines": {
67
+ "bun": ">=1.3.0",
68
+ "node": ">=24.0.0"
69
+ },
70
+ "packageManager": "bun@1.3.2",
71
+ "publishConfig": {
72
+ "access": "public"
73
+ },
74
+ "dependencies": {
75
+ "@cyanheads/mcp-ts-core": "^0.10.6",
76
+ "pino-pretty": "^13.1.3",
77
+ "zod": "^4.4.3"
78
+ },
79
+ "optionalDependencies": {
80
+ "better-sqlite3": "^12.10.0"
81
+ },
82
+ "devDependencies": {
83
+ "@biomejs/biome": "2.5.0",
84
+ "@types/better-sqlite3": "^7.6.13",
85
+ "@types/node": "25.9.3",
86
+ "depcheck": "^1.4.7",
87
+ "ignore": "^7.0.5",
88
+ "tsc-alias": "^1.8.17",
89
+ "typescript": "^6.0.3",
90
+ "vitest": "^4.1.8"
91
+ }
92
+ }
package/server.json ADDED
@@ -0,0 +1,125 @@
1
+ {
2
+ "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
+ "name": "io.github.cyanheads/medical-codes-mcp-server",
4
+ "description": "Offline US medical code lookup and crosswalk — ICD-10-CM, ICD-10-PCS, HCPCS Level II. Keyless.",
5
+ "repository": {
6
+ "url": "https://github.com/cyanheads/medical-codes-mcp-server",
7
+ "source": "github"
8
+ },
9
+ "version": "0.1.0",
10
+ "packages": [
11
+ {
12
+ "registryType": "npm",
13
+ "registryBaseUrl": "https://registry.npmjs.org",
14
+ "identifier": "@cyanheads/medical-codes-mcp-server",
15
+ "runtimeHint": "node",
16
+ "version": "0.1.0",
17
+ "packageArguments": [
18
+ {
19
+ "type": "positional",
20
+ "value": "run"
21
+ },
22
+ {
23
+ "type": "positional",
24
+ "value": "start:stdio"
25
+ }
26
+ ],
27
+ "environmentVariables": [
28
+ {
29
+ "name": "MCP_LOG_LEVEL",
30
+ "description": "Sets the minimum log level for output (e.g., 'debug', 'info', 'warn').",
31
+ "format": "string",
32
+ "isRequired": false,
33
+ "default": "info"
34
+ },
35
+ {
36
+ "name": "MEDCODE_DB_PATH",
37
+ "description": "Absolute path override for the bundled SQLite index. Defaults to the packaged data/medical-codes.db.",
38
+ "format": "string",
39
+ "isRequired": false
40
+ },
41
+ {
42
+ "name": "MEDCODE_MAX_RESULTS",
43
+ "description": "Cap on rows returned by search and browse tools (default 50, hard ceiling 200).",
44
+ "format": "string",
45
+ "isRequired": false,
46
+ "default": "50"
47
+ }
48
+ ],
49
+ "transport": {
50
+ "type": "stdio"
51
+ }
52
+ },
53
+ {
54
+ "registryType": "npm",
55
+ "registryBaseUrl": "https://registry.npmjs.org",
56
+ "identifier": "@cyanheads/medical-codes-mcp-server",
57
+ "runtimeHint": "node",
58
+ "version": "0.1.0",
59
+ "packageArguments": [
60
+ {
61
+ "type": "positional",
62
+ "value": "run"
63
+ },
64
+ {
65
+ "type": "positional",
66
+ "value": "start:http"
67
+ }
68
+ ],
69
+ "environmentVariables": [
70
+ {
71
+ "name": "MCP_HTTP_HOST",
72
+ "description": "The hostname for the HTTP server.",
73
+ "format": "string",
74
+ "isRequired": false,
75
+ "default": "127.0.0.1"
76
+ },
77
+ {
78
+ "name": "MCP_HTTP_PORT",
79
+ "description": "The port to run the HTTP server on.",
80
+ "format": "string",
81
+ "isRequired": false,
82
+ "default": "3010"
83
+ },
84
+ {
85
+ "name": "MCP_HTTP_ENDPOINT_PATH",
86
+ "description": "The endpoint path for the MCP server.",
87
+ "format": "string",
88
+ "isRequired": false,
89
+ "default": "/mcp"
90
+ },
91
+ {
92
+ "name": "MCP_AUTH_MODE",
93
+ "description": "Authentication mode to use: 'none', 'jwt', or 'oauth'.",
94
+ "format": "string",
95
+ "isRequired": false,
96
+ "default": "none"
97
+ },
98
+ {
99
+ "name": "MCP_LOG_LEVEL",
100
+ "description": "Sets the minimum log level for output (e.g., 'debug', 'info', 'warn').",
101
+ "format": "string",
102
+ "isRequired": false,
103
+ "default": "info"
104
+ },
105
+ {
106
+ "name": "MEDCODE_DB_PATH",
107
+ "description": "Absolute path override for the bundled SQLite index. Defaults to the packaged data/medical-codes.db.",
108
+ "format": "string",
109
+ "isRequired": false
110
+ },
111
+ {
112
+ "name": "MEDCODE_MAX_RESULTS",
113
+ "description": "Cap on rows returned by search and browse tools (default 50, hard ceiling 200).",
114
+ "format": "string",
115
+ "isRequired": false,
116
+ "default": "50"
117
+ }
118
+ ],
119
+ "transport": {
120
+ "type": "streamable-http",
121
+ "url": "http://localhost:3010/mcp"
122
+ }
123
+ }
124
+ ]
125
+ }