@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,91 @@
1
+ /**
2
+ * @fileoverview medcode_check_code — validate whether a code exists, is current,
3
+ * and is billable in the active release. Returns a discriminated status with a
4
+ * why-not for non-billable or terminated codes. Validity vs. existence is split:
5
+ * a non-billable or terminated code is a SUCCESS result with a whyNot (the
6
+ * recovery detail a coder needs), not an error. Only a code absent from every
7
+ * detected system is an `unknown_code` failure.
8
+ * @module mcp-server/tools/definitions/check-code.tool
9
+ */
10
+ import { tool, z } from '@cyanheads/mcp-ts-core';
11
+ import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
12
+ import { getCodeIndexService } from '../../../services/code-index/code-index-service.js';
13
+ import { SYSTEM_IDS, SYSTEM_LABELS } from '../../../services/code-index/types.js';
14
+ const SOURCE_URL = 'https://github.com/cyanheads/medical-codes-mcp-server/blob/main/src/mcp-server/tools/definitions/check-code.tool.ts';
15
+ export const checkCodeTool = tool('medcode_check_code', {
16
+ title: 'medical-codes-mcp-server',
17
+ description: 'Validate whether a US medical code exists, is current, and is billable in the active bundled release. Returns a discriminated status — valid_billable, valid_not_billable, valid_header, or terminated — with a `whyNot` explaining non-billable and terminated cases (e.g. "valid ICD-10-CM category but not billable — submit a more specific child code"). This is the detail a coder needs before submitting a claim. Auto-detects the system from the code\'s shape; pass an explicit `system` to disambiguate. A non-billable or terminated code is a successful result with a whyNot, not an error — only a code that exists in no bundled system raises unknown_code.',
18
+ annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
19
+ sourceUrl: SOURCE_URL,
20
+ input: z.object({
21
+ code: z.string().min(1).describe('The code to validate, with or without dots.'),
22
+ system: z
23
+ .enum(SYSTEM_IDS)
24
+ .optional()
25
+ .describe("Force the lookup into this system. Omit to auto-detect from the code's shape."),
26
+ }),
27
+ output: z.object({
28
+ system: z.string().describe('The system the code was resolved in, echoed for chaining.'),
29
+ code: z.string().describe('The code in display form (ICD-10-CM carries the dot).'),
30
+ status: z
31
+ .enum(['valid_billable', 'valid_not_billable', 'valid_header', 'terminated'])
32
+ .describe('Validity status. valid_billable = submit as-is; valid_header/valid_not_billable = needs a more specific code; terminated = retired.'),
33
+ billable: z.boolean().describe('True only when status is valid_billable.'),
34
+ whyNot: z
35
+ .string()
36
+ .nullable()
37
+ .describe('Explanation for non-billable/terminated statuses, or null when valid_billable.'),
38
+ }),
39
+ errors: [
40
+ {
41
+ reason: 'unknown_code',
42
+ code: JsonRpcErrorCode.NotFound,
43
+ when: 'The code does not exist in the named or detected system.',
44
+ recovery: 'Check the code, or search by description with medcode_search_codes.',
45
+ },
46
+ {
47
+ reason: 'ambiguous_system',
48
+ code: JsonRpcErrorCode.InvalidParams,
49
+ when: 'The code is present in more than one bundled system and no `system` was given.',
50
+ recovery: 'Re-call with an explicit `system` to disambiguate.',
51
+ },
52
+ ],
53
+ handler(input, ctx) {
54
+ const outcome = getCodeIndexService().checkCode(input.code, input.system);
55
+ if (outcome.kind === 'ambiguous') {
56
+ throw ctx.fail('ambiguous_system', `"${input.code.trim()}" exists in multiple systems: ${outcome.systems.join(', ')}.`, { candidateSystems: outcome.systems, ...ctx.recoveryFor('ambiguous_system') });
57
+ }
58
+ const r = outcome.result;
59
+ if (r.status === 'unknown') {
60
+ throw ctx.fail('unknown_code', r.whyNot ?? `Unknown code "${input.code.trim()}".`, {
61
+ ...ctx.recoveryFor('unknown_code'),
62
+ });
63
+ }
64
+ ctx.log.info('Checked code', { code: r.code, system: r.system, status: r.status });
65
+ return {
66
+ system: r.system,
67
+ code: r.code,
68
+ status: r.status,
69
+ billable: r.status === 'valid_billable',
70
+ whyNot: r.whyNot ?? null,
71
+ };
72
+ },
73
+ format: (result) => {
74
+ const label = SYSTEM_LABELS[result.system] ?? result.system;
75
+ const verdict = {
76
+ valid_billable: '✅ Valid and billable',
77
+ valid_not_billable: '⚠️ Valid but not billable',
78
+ valid_header: '⚠️ Valid category/header — not billable',
79
+ terminated: '⛔ Terminated',
80
+ };
81
+ const lines = [
82
+ `## ${result.code} — ${label}`,
83
+ `**Status:** ${verdict[result.status]}`,
84
+ `**Billable:** ${result.billable ? 'Yes' : 'No'}`,
85
+ ];
86
+ if (result.whyNot)
87
+ lines.push('', result.whyNot);
88
+ return [{ type: 'text', text: lines.join('\n') }];
89
+ },
90
+ });
91
+ //# sourceMappingURL=check-code.tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-code.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/check-code.tool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * @fileoverview medcode_get_code — decode one or more codes to their official
3
+ * descriptions across ICD-10-CM, ICD-10-PCS, HCPCS Level II, and (phase 2)
4
+ * RxNorm. The 80% entry point: resolve a code seen in a claim, EHR field, or
5
+ * another health server's output into its meaning. Auto-detects the system from
6
+ * each code's shape; an explicit `system` disambiguates. Array-in /
7
+ * partial-success-out — one bad code never fails the rest.
8
+ * @module mcp-server/tools/definitions/get-code.tool
9
+ */
10
+ import { z } from '@cyanheads/mcp-ts-core';
11
+ import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
12
+ export declare const getCodeTool: import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{
13
+ codes: z.ZodArray<z.ZodString>;
14
+ system: z.ZodOptional<z.ZodEnum<{
15
+ ICD10CM: "ICD10CM";
16
+ ICD10PCS: "ICD10PCS";
17
+ HCPCS: "HCPCS";
18
+ RXNORM: "RXNORM";
19
+ }>>;
20
+ includeHierarchy: z.ZodDefault<z.ZodBoolean>;
21
+ }, z.core.$strip>, z.ZodObject<{
22
+ found: z.ZodArray<z.ZodObject<{
23
+ system: z.ZodString;
24
+ code: z.ZodString;
25
+ description: z.ZodNullable<z.ZodString>;
26
+ shortDescription: z.ZodNullable<z.ZodString>;
27
+ billable: z.ZodBoolean;
28
+ header: z.ZodBoolean;
29
+ chapter: z.ZodNullable<z.ZodString>;
30
+ parent: z.ZodOptional<z.ZodNullable<z.ZodString>>;
31
+ children: z.ZodOptional<z.ZodArray<z.ZodObject<{
32
+ system: z.ZodString;
33
+ code: z.ZodString;
34
+ description: z.ZodNullable<z.ZodString>;
35
+ shortDescription: z.ZodNullable<z.ZodString>;
36
+ billable: z.ZodBoolean;
37
+ header: z.ZodBoolean;
38
+ chapter: z.ZodNullable<z.ZodString>;
39
+ }, z.core.$strip>>>;
40
+ }, z.core.$strip>>;
41
+ notFound: z.ZodArray<z.ZodObject<{
42
+ code: z.ZodString;
43
+ reason: z.ZodString;
44
+ candidateSystems: z.ZodOptional<z.ZodArray<z.ZodString>>;
45
+ }, z.core.$strip>>;
46
+ }, z.core.$strip>, readonly [{
47
+ readonly reason: "no_codes_found";
48
+ readonly code: JsonRpcErrorCode.NotFound;
49
+ readonly when: "None of the requested codes exist in any bundled system.";
50
+ readonly recovery: "Verify code formatting, or call medcode_search_codes with a description to find the right code.";
51
+ }], undefined>;
52
+ //# sourceMappingURL=get-code.tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-code.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/get-code.tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAkFjE,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA0GtB,CAAC"}
@@ -0,0 +1,165 @@
1
+ /**
2
+ * @fileoverview medcode_get_code — decode one or more codes to their official
3
+ * descriptions across ICD-10-CM, ICD-10-PCS, HCPCS Level II, and (phase 2)
4
+ * RxNorm. The 80% entry point: resolve a code seen in a claim, EHR field, or
5
+ * another health server's output into its meaning. Auto-detects the system from
6
+ * each code's shape; an explicit `system` disambiguates. Array-in /
7
+ * partial-success-out — one bad code never fails the rest.
8
+ * @module mcp-server/tools/definitions/get-code.tool
9
+ */
10
+ import { tool, z } from '@cyanheads/mcp-ts-core';
11
+ import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
12
+ import { CodeIndexService, getCodeIndexService } from '../../../services/code-index/code-index-service.js';
13
+ import { SYSTEM_IDS } from '../../../services/code-index/types.js';
14
+ import { renderCodeBlock, renderCodeLine } from './_render.js';
15
+ const SOURCE_URL = 'https://github.com/cyanheads/medical-codes-mcp-server/blob/main/src/mcp-server/tools/definitions/get-code.tool.ts';
16
+ const DecodedCodeSchema = z
17
+ .object({
18
+ system: z
19
+ .string()
20
+ .describe('The system that answered, echoed for chaining: "ICD10CM", "ICD10PCS", "HCPCS", or "RXNORM".'),
21
+ code: z
22
+ .string()
23
+ .describe('The resolved code in display form (ICD-10-CM codes carry the dot, e.g. "E11.9").'),
24
+ description: z
25
+ .string()
26
+ .nullable()
27
+ .describe('Official long description (falls back to the short description when no long form exists).'),
28
+ shortDescription: z
29
+ .string()
30
+ .nullable()
31
+ .describe('Official short/abbreviated description, or null when none is on record.'),
32
+ billable: z
33
+ .boolean()
34
+ .describe('True when the code is a billable leaf. False for headers/categories and non-billable codes.'),
35
+ header: z
36
+ .boolean()
37
+ .describe('True when the code is a non-billable category/header (ICD-10-CM) rather than a leaf code.'),
38
+ chapter: z
39
+ .string()
40
+ .nullable()
41
+ .describe('Chapter/range bucket the code belongs to, or null when not applicable.'),
42
+ })
43
+ .describe('A decoded code with its official descriptions and derived flags.');
44
+ /** A found code, optionally carrying its hierarchy when includeHierarchy is set. */
45
+ const FoundCodeSchema = DecodedCodeSchema.extend({
46
+ parent: z
47
+ .string()
48
+ .nullable()
49
+ .optional()
50
+ .describe('Immediate parent code (present only when includeHierarchy is true). Null at a root.'),
51
+ children: z
52
+ .array(DecodedCodeSchema)
53
+ .optional()
54
+ .describe('Immediate child codes (present only when includeHierarchy is true).'),
55
+ }).describe('A decoded code, optionally with its parent and immediate children.');
56
+ /** A code that did not resolve, with a per-code reason. */
57
+ const NotFoundCodeSchema = z
58
+ .object({
59
+ code: z.string().describe('The input code that could not be resolved.'),
60
+ reason: z
61
+ .string()
62
+ .describe('Why it could not be resolved (unknown shape, not in the bundled release, or ambiguous).'),
63
+ candidateSystems: z
64
+ .array(z.string())
65
+ .optional()
66
+ .describe('When ambiguous, the systems whose shape/content the code matched — re-call with one as `system`.'),
67
+ })
68
+ .describe('An input code that did not resolve, with the reason it failed.');
69
+ export const getCodeTool = tool('medcode_get_code', {
70
+ title: 'medical-codes-mcp-server',
71
+ description: "Decode one or more US medical codes to their official descriptions across ICD-10-CM (diagnoses), ICD-10-PCS (inpatient procedures), HCPCS Level II (supplies/drugs/services), and — when bundled — RxNorm (drugs). Auto-detects the system from each code's shape; pass an explicit `system` only when a value is genuinely ambiguous. Accepts 1–50 codes and returns partial success: resolved codes in `found`, unresolved in `notFound` with a per-code reason, so one bad code never fails the batch. Set `includeHierarchy` to attach each code's parent and immediate children. The resolved `system` is echoed on every result for chaining into medcode_map_codes or a billability check.",
72
+ annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
73
+ sourceUrl: SOURCE_URL,
74
+ input: z.object({
75
+ codes: z
76
+ .array(z.string().min(1).describe('A single code to decode, with or without dots.'))
77
+ .min(1)
78
+ .max(50)
79
+ .describe('Codes to decode (1–50). Mixed systems are fine — each is detected independently.'),
80
+ system: z
81
+ .enum(SYSTEM_IDS)
82
+ .optional()
83
+ .describe('Force every code to be looked up in this system. Omit to auto-detect per code.'),
84
+ includeHierarchy: z
85
+ .boolean()
86
+ .default(false)
87
+ .describe("When true, attach each found code's parent and immediate children."),
88
+ }),
89
+ output: z.object({
90
+ found: z.array(FoundCodeSchema).describe('Successfully decoded codes, in request order.'),
91
+ notFound: z
92
+ .array(NotFoundCodeSchema)
93
+ .describe('Codes that did not resolve, with per-code reasons.'),
94
+ }),
95
+ errors: [
96
+ {
97
+ reason: 'no_codes_found',
98
+ code: JsonRpcErrorCode.NotFound,
99
+ when: 'None of the requested codes exist in any bundled system.',
100
+ recovery: 'Verify code formatting, or call medcode_search_codes with a description to find the right code.',
101
+ },
102
+ ],
103
+ handler(input, ctx) {
104
+ const svc = getCodeIndexService();
105
+ const found = [];
106
+ const notFound = [];
107
+ for (const raw of input.codes) {
108
+ const result = svc.getByCode(raw, input.system);
109
+ if (result.kind === 'not_found') {
110
+ const shapes = svc.detectSystem(raw);
111
+ notFound.push({
112
+ code: raw,
113
+ reason: shapes.length === 0
114
+ ? `"${raw}" does not match the shape of any bundled code system.`
115
+ : `"${raw}" is not present in the bundled release (matched shape: ${shapes.join(', ')}).`,
116
+ });
117
+ }
118
+ else if (result.kind === 'ambiguous') {
119
+ notFound.push({
120
+ code: raw,
121
+ reason: `"${raw}" matches multiple systems — re-call with an explicit \`system\` to disambiguate.`,
122
+ candidateSystems: result.systems,
123
+ });
124
+ }
125
+ else if (input.includeHierarchy) {
126
+ found.push(svc.getByCodeWithHierarchy(result.row));
127
+ }
128
+ else {
129
+ found.push(CodeIndexService.project(result.row));
130
+ }
131
+ }
132
+ ctx.log.info('Decoded codes', {
133
+ requested: input.codes.length,
134
+ found: found.length,
135
+ notFound: notFound.length,
136
+ });
137
+ if (found.length === 0) {
138
+ throw ctx.fail('no_codes_found', `None of the ${input.codes.length} requested code(s) resolved in any bundled system.`, { ...ctx.recoveryFor('no_codes_found') });
139
+ }
140
+ return { found, notFound };
141
+ },
142
+ format: (result) => {
143
+ const lines = [];
144
+ for (const c of result.found) {
145
+ lines.push(...renderCodeBlock(c));
146
+ if (c.parent !== undefined)
147
+ lines.push(`**Parent:** ${c.parent ?? '(none — root)'}`);
148
+ if (c.children && c.children.length > 0) {
149
+ lines.push(`**Children (${c.children.length}):**`);
150
+ for (const k of c.children)
151
+ lines.push(renderCodeLine(k));
152
+ }
153
+ lines.push('');
154
+ }
155
+ if (result.notFound.length > 0) {
156
+ lines.push(`### Not found (${result.notFound.length})`);
157
+ for (const n of result.notFound) {
158
+ const cands = n.candidateSystems?.length ? ` [${n.candidateSystems.join(', ')}]` : '';
159
+ lines.push(`- **${n.code}**: ${n.reason}${cands}`);
160
+ }
161
+ }
162
+ return [{ type: 'text', text: lines.join('\n').trim() }];
163
+ },
164
+ });
165
+ //# sourceMappingURL=get-code.tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-code.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/get-code.tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAEjE,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,6CAA6C,CAAC;AACpG,OAAO,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE/D,MAAM,UAAU,GACd,mHAAmH,CAAC;AAEtH,MAAM,iBAAiB,GAAG,CAAC;KACxB,MAAM,CAAC;IACN,MAAM,EAAE,CAAC;SACN,MAAM,EAAE;SACR,QAAQ,CACP,6FAA6F,CAC9F;IACH,IAAI,EAAE,CAAC;SACJ,MAAM,EAAE;SACR,QAAQ,CAAC,kFAAkF,CAAC;IAC/F,WAAW,EAAE,CAAC;SACX,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CACP,2FAA2F,CAC5F;IACH,gBAAgB,EAAE,CAAC;SAChB,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CAAC,yEAAyE,CAAC;IACtF,QAAQ,EAAE,CAAC;SACR,OAAO,EAAE;SACT,QAAQ,CACP,6FAA6F,CAC9F;IACH,MAAM,EAAE,CAAC;SACN,OAAO,EAAE;SACT,QAAQ,CACP,2FAA2F,CAC5F;IACH,OAAO,EAAE,CAAC;SACP,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CAAC,wEAAwE,CAAC;CACtF,CAAC;KACD,QAAQ,CAAC,kEAAkE,CAAC,CAAC;AAEhF,oFAAoF;AACpF,MAAM,eAAe,GAAG,iBAAiB,CAAC,MAAM,CAAC;IAC/C,MAAM,EAAE,CAAC;SACN,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,EAAE;SACV,QAAQ,CACP,qFAAqF,CACtF;IACH,QAAQ,EAAE,CAAC;SACR,KAAK,CAAC,iBAAiB,CAAC;SACxB,QAAQ,EAAE;SACV,QAAQ,CAAC,qEAAqE,CAAC;CACnF,CAAC,CAAC,QAAQ,CAAC,oEAAoE,CAAC,CAAC;AAElF,2DAA2D;AAC3D,MAAM,kBAAkB,GAAG,CAAC;KACzB,MAAM,CAAC;IACN,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,4CAA4C,CAAC;IACvE,MAAM,EAAE,CAAC;SACN,MAAM,EAAE;SACR,QAAQ,CACP,yFAAyF,CAC1F;IACH,gBAAgB,EAAE,CAAC;SAChB,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;SACjB,QAAQ,EAAE;SACV,QAAQ,CACP,kGAAkG,CACnG;CACJ,CAAC;KACD,QAAQ,CAAC,gEAAgE,CAAC,CAAC;AAK9E,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,CAAC,kBAAkB,EAAE;IAClD,KAAK,EAAE,0BAA0B;IACjC,WAAW,EACT,mqBAAmqB;IACrqB,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE;IAC/E,SAAS,EAAE,UAAU;IAErB,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC;QACd,KAAK,EAAE,CAAC;aACL,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,gDAAgD,CAAC,CAAC;aACnF,GAAG,CAAC,CAAC,CAAC;aACN,GAAG,CAAC,EAAE,CAAC;aACP,QAAQ,CAAC,kFAAkF,CAAC;QAC/F,MAAM,EAAE,CAAC;aACN,IAAI,CAAC,UAAU,CAAC;aAChB,QAAQ,EAAE;aACV,QAAQ,CAAC,gFAAgF,CAAC;QAC7F,gBAAgB,EAAE,CAAC;aAChB,OAAO,EAAE;aACT,OAAO,CAAC,KAAK,CAAC;aACd,QAAQ,CAAC,oEAAoE,CAAC;KAClF,CAAC;IAEF,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,QAAQ,CAAC,+CAA+C,CAAC;QACzF,QAAQ,EAAE,CAAC;aACR,KAAK,CAAC,kBAAkB,CAAC;aACzB,QAAQ,CAAC,oDAAoD,CAAC;KAClE,CAAC;IAEF,MAAM,EAAE;QACN;YACE,MAAM,EAAE,gBAAgB;YACxB,IAAI,EAAE,gBAAgB,CAAC,QAAQ;YAC/B,IAAI,EAAE,0DAA0D;YAChE,QAAQ,EACN,iGAAiG;SACpG;KACF;IAED,OAAO,CAAC,KAAK,EAAE,GAAG;QAChB,MAAM,GAAG,GAAG,mBAAmB,EAAE,CAAC;QAClC,MAAM,KAAK,GAAgB,EAAE,CAAC;QAC9B,MAAM,QAAQ,GAAmB,EAAE,CAAC;QAEpC,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YAC9B,MAAM,MAAM,GAAG,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;YAChD,IAAI,MAAM,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;gBAChC,MAAM,MAAM,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;gBACrC,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI,EAAE,GAAG;oBACT,MAAM,EACJ,MAAM,CAAC,MAAM,KAAK,CAAC;wBACjB,CAAC,CAAC,IAAI,GAAG,wDAAwD;wBACjE,CAAC,CAAC,IAAI,GAAG,2DAA2D,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;iBAC9F,CAAC,CAAC;YACL,CAAC;iBAAM,IAAI,MAAM,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;gBACvC,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI,EAAE,GAAG;oBACT,MAAM,EAAE,IAAI,GAAG,mFAAmF;oBAClG,gBAAgB,EAAE,MAAM,CAAC,OAAO;iBACjC,CAAC,CAAC;YACL,CAAC;iBAAM,IAAI,KAAK,CAAC,gBAAgB,EAAE,CAAC;gBAClC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,sBAAsB,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YACrD,CAAC;iBAAM,CAAC;gBACN,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YACnD,CAAC;QACH,CAAC;QAED,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,eAAe,EAAE;YAC5B,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM;YAC7B,KAAK,EAAE,KAAK,CAAC,MAAM;YACnB,QAAQ,EAAE,QAAQ,CAAC,MAAM;SAC1B,CAAC,CAAC;QAEH,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,MAAM,GAAG,CAAC,IAAI,CACZ,gBAAgB,EAChB,eAAe,KAAK,CAAC,KAAK,CAAC,MAAM,oDAAoD,EACrF,EAAE,GAAG,GAAG,CAAC,WAAW,CAAC,gBAAgB,CAAC,EAAE,CACzC,CAAC;QACJ,CAAC;QAED,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;IAC7B,CAAC;IAED,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE;QACjB,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YAC7B,KAAK,CAAC,IAAI,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC;YAClC,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS;gBAAE,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,MAAM,IAAI,eAAe,EAAE,CAAC,CAAC;YACrF,IAAI,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACxC,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,QAAQ,CAAC,MAAM,MAAM,CAAC,CAAC;gBACnD,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,QAAQ;oBAAE,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC;YAC5D,CAAC;YACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACjB,CAAC;QACD,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC/B,KAAK,CAAC,IAAI,CAAC,kBAAkB,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;YACxD,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;gBAChC,MAAM,KAAK,GAAG,CAAC,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;gBACtF,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,MAAM,GAAG,KAAK,EAAE,CAAC,CAAC;YACrD,CAAC;QACH,CAAC;QACD,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAC3D,CAAC;CACF,CAAC,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @fileoverview medcode_list_systems — list the bundled code systems with their
3
+ * release identifiers, effective dates, and code counts. Cheap orientation /
4
+ * provenance call so a caller can confirm which ICD-10 fiscal year, HCPCS
5
+ * release, and RxNorm month are active before acting on results.
6
+ * @module mcp-server/tools/definitions/list-systems.tool
7
+ */
8
+ import { z } from '@cyanheads/mcp-ts-core';
9
+ export declare const listSystemsTool: import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{}, z.core.$strip>, z.ZodObject<{
10
+ systems: z.ZodArray<z.ZodObject<{
11
+ system: z.ZodString;
12
+ label: z.ZodString;
13
+ releaseId: z.ZodString;
14
+ effectiveStart: z.ZodNullable<z.ZodString>;
15
+ effectiveEnd: z.ZodNullable<z.ZodString>;
16
+ codeCount: z.ZodNumber;
17
+ sourceUrl: z.ZodNullable<z.ZodString>;
18
+ builtAt: z.ZodString;
19
+ }, z.core.$strip>>;
20
+ }, z.core.$strip>, undefined, undefined>;
21
+ //# sourceMappingURL=list-systems.tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-systems.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/list-systems.tool.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAQjD,eAAO,MAAM,eAAe;;;;;;;;;;;wCA+E1B,CAAC"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * @fileoverview medcode_list_systems — list the bundled code systems with their
3
+ * release identifiers, effective dates, and code counts. Cheap orientation /
4
+ * provenance call so a caller can confirm which ICD-10 fiscal year, HCPCS
5
+ * release, and RxNorm month are active before acting on results.
6
+ * @module mcp-server/tools/definitions/list-systems.tool
7
+ */
8
+ import { tool, z } from '@cyanheads/mcp-ts-core';
9
+ import { getCodeIndexService } from '../../../services/code-index/code-index-service.js';
10
+ import { SYSTEM_LABELS } from '../../../services/code-index/types.js';
11
+ const SOURCE_URL = 'https://github.com/cyanheads/medical-codes-mcp-server/blob/main/src/mcp-server/tools/definitions/list-systems.tool.ts';
12
+ export const listSystemsTool = tool('medcode_list_systems', {
13
+ title: 'medical-codes-mcp-server',
14
+ description: 'List the bundled US medical code systems with their release identifiers, effective dates, and code counts. Confirms which ICD-10-CM fiscal year, ICD-10-PCS fiscal year, HCPCS Level II release, and (when bundled) RxNorm month are active before acting on any decode, search, or crosswalk result. The corpus is offline and built at package-build time — this call reports exactly which release is baked into the running server. ICD-10-CM/PCS are the US clinical modifications, not the ICD-10/ICD-11 base.',
15
+ annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
16
+ sourceUrl: SOURCE_URL,
17
+ input: z.object({}),
18
+ output: z.object({
19
+ systems: z
20
+ .array(z
21
+ .object({
22
+ system: z
23
+ .string()
24
+ .describe('System identifier, e.g. "ICD10CM", "ICD10PCS", "HCPCS", "RXNORM".'),
25
+ label: z.string().describe('Human-readable system name, e.g. "ICD-10-CM".'),
26
+ releaseId: z
27
+ .string()
28
+ .describe('Release/version identifier baked into this build, e.g. "ICD-10-CM FY2026".'),
29
+ effectiveStart: z
30
+ .string()
31
+ .nullable()
32
+ .describe('First date this release is effective (YYYY-MM-DD), or null if not recorded.'),
33
+ effectiveEnd: z
34
+ .string()
35
+ .nullable()
36
+ .describe('Last date this release is effective (YYYY-MM-DD), or null if open-ended.'),
37
+ codeCount: z.number().describe('Number of code rows bundled for this system.'),
38
+ sourceUrl: z
39
+ .string()
40
+ .nullable()
41
+ .describe('Canonical .gov source the release was built from, or null.'),
42
+ builtAt: z.string().describe('ISO 8601 timestamp when this system was last baked.'),
43
+ })
44
+ .describe('Provenance for one bundled code system.'))
45
+ .describe('One entry per bundled code system, in canonical order.'),
46
+ }),
47
+ handler(_input, ctx) {
48
+ const systems = getCodeIndexService()
49
+ .listSystems()
50
+ .map((s) => ({
51
+ system: s.system,
52
+ label: SYSTEM_LABELS[s.system] ?? s.system,
53
+ releaseId: s.releaseId,
54
+ effectiveStart: s.effectiveStart,
55
+ effectiveEnd: s.effectiveEnd,
56
+ codeCount: s.codeCount,
57
+ sourceUrl: s.sourceUrl,
58
+ builtAt: s.builtAt,
59
+ }));
60
+ ctx.log.info('Listed bundled code systems', { count: systems.length });
61
+ return { systems };
62
+ },
63
+ format: (result) => {
64
+ const lines = ['## Bundled code systems', ''];
65
+ lines.push('| System | Release | Effective | Codes |');
66
+ lines.push('|:---|:---|:---|---:|');
67
+ for (const s of result.systems) {
68
+ const effective = s.effectiveStart && s.effectiveEnd
69
+ ? `${s.effectiveStart} → ${s.effectiveEnd}`
70
+ : (s.effectiveStart ?? s.effectiveEnd ?? '—');
71
+ lines.push(`| ${s.label} (${s.system}) | ${s.releaseId} | ${effective} | ${s.codeCount} |`);
72
+ }
73
+ lines.push('');
74
+ for (const s of result.systems) {
75
+ if (s.sourceUrl)
76
+ lines.push(`- **${s.label}** source: ${s.sourceUrl} (built ${s.builtAt})`);
77
+ }
78
+ return [{ type: 'text', text: lines.join('\n') }];
79
+ },
80
+ });
81
+ //# sourceMappingURL=list-systems.tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-systems.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/list-systems.tool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * @fileoverview medcode_map_codes — crosswalk a code or drug across systems and
3
+ * within a hierarchy. v1 ships the hierarchy directions (code → parents/children);
4
+ * the drug directions (drug name → RXCUI, NDC ↔ RXCUI, RXCUI → ingredients/brands)
5
+ * land with RxNorm in phase 2 and raise `direction_unavailable` until then. The
6
+ * relational bridge between the bundled systems and a composition point with the
7
+ * openfda server (NDC/labels).
8
+ * @module mcp-server/tools/definitions/map-codes.tool
9
+ */
10
+ import { z } from '@cyanheads/mcp-ts-core';
11
+ import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
12
+ export declare const mapCodesTool: import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{
13
+ from: z.ZodString;
14
+ direction: z.ZodEnum<{
15
+ parents: "parents";
16
+ children: "children";
17
+ name_to_rxcui: "name_to_rxcui";
18
+ ndc_to_rxcui: "ndc_to_rxcui";
19
+ rxcui_to_ndc: "rxcui_to_ndc";
20
+ rxcui_to_ingredients: "rxcui_to_ingredients";
21
+ rxcui_to_brands: "rxcui_to_brands";
22
+ }>;
23
+ system: z.ZodOptional<z.ZodEnum<{
24
+ ICD10CM: "ICD10CM";
25
+ ICD10PCS: "ICD10PCS";
26
+ HCPCS: "HCPCS";
27
+ RXNORM: "RXNORM";
28
+ }>>;
29
+ }, z.core.$strip>, z.ZodObject<{
30
+ from: z.ZodString;
31
+ direction: z.ZodString;
32
+ resolvedSystem: z.ZodNullable<z.ZodString>;
33
+ hits: z.ZodArray<z.ZodObject<{
34
+ source: z.ZodString;
35
+ system: z.ZodNullable<z.ZodString>;
36
+ value: z.ZodString;
37
+ description: z.ZodOptional<z.ZodString>;
38
+ }, z.core.$strip>>;
39
+ }, z.core.$strip>, readonly [{
40
+ readonly reason: "no_mapping";
41
+ readonly code: JsonRpcErrorCode.NotFound;
42
+ readonly when: "The source resolved but has no edge in the requested direction.";
43
+ readonly recovery: "Confirm the direction is supported for this system, or decode the code with medcode_get_code first.";
44
+ }, {
45
+ readonly reason: "direction_unavailable";
46
+ readonly code: JsonRpcErrorCode.InvalidParams;
47
+ readonly when: "A drug-crosswalk direction was requested before RxNorm (phase 2) is bundled.";
48
+ readonly recovery: "Use a hierarchy direction (parents/children); drug crosswalks land with RxNorm.";
49
+ }, {
50
+ readonly reason: "ambiguous_system";
51
+ readonly code: JsonRpcErrorCode.InvalidParams;
52
+ readonly when: "The source code is present in more than one system and no `system` was given.";
53
+ readonly recovery: "Re-call with an explicit `system` to disambiguate.";
54
+ }], undefined>;
55
+ //# sourceMappingURL=map-codes.tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"map-codes.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/map-codes.tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAkBjE,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAoJvB,CAAC"}
@@ -0,0 +1,132 @@
1
+ /**
2
+ * @fileoverview medcode_map_codes — crosswalk a code or drug across systems and
3
+ * within a hierarchy. v1 ships the hierarchy directions (code → parents/children);
4
+ * the drug directions (drug name → RXCUI, NDC ↔ RXCUI, RXCUI → ingredients/brands)
5
+ * land with RxNorm in phase 2 and raise `direction_unavailable` until then. The
6
+ * relational bridge between the bundled systems and a composition point with the
7
+ * openfda server (NDC/labels).
8
+ * @module mcp-server/tools/definitions/map-codes.tool
9
+ */
10
+ import { tool, z } from '@cyanheads/mcp-ts-core';
11
+ import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
12
+ import { CodeIndexService, getCodeIndexService } from '../../../services/code-index/code-index-service.js';
13
+ import { SYSTEM_IDS } from '../../../services/code-index/types.js';
14
+ const SOURCE_URL = 'https://github.com/cyanheads/medical-codes-mcp-server/blob/main/src/mcp-server/tools/definitions/map-codes.tool.ts';
15
+ const DIRECTIONS = [
16
+ 'parents',
17
+ 'children',
18
+ 'name_to_rxcui',
19
+ 'ndc_to_rxcui',
20
+ 'rxcui_to_ndc',
21
+ 'rxcui_to_ingredients',
22
+ 'rxcui_to_brands',
23
+ ];
24
+ export const mapCodesTool = tool('medcode_map_codes', {
25
+ title: 'medical-codes-mcp-server',
26
+ description: "Crosswalk a US medical code or drug across systems and within a hierarchy. Hierarchy directions (available now): `parents` and `children` walk a code's prefix hierarchy (ICD-10-CM/HCPCS; ICD-10-PCS codes have no prefix parent). Drug directions (RxNorm): `name_to_rxcui`, `ndc_to_rxcui`, `rxcui_to_ndc`, `rxcui_to_ingredients`, `rxcui_to_brands` — these return an error until RxNorm is bundled in a later release. Every result carries `source` provenance (which system or edge answered) so a chained call (e.g. into openfda with a resolved NDC) uses the right identifier.",
27
+ annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
28
+ sourceUrl: SOURCE_URL,
29
+ input: z.object({
30
+ from: z
31
+ .string()
32
+ .min(1)
33
+ .describe('The source value: a code (for parents/children), a drug name, an NDC, or an RXCUI.'),
34
+ direction: z
35
+ .enum(DIRECTIONS)
36
+ .describe('What to map to. parents/children walk the code hierarchy; the rxcui/ndc/name directions are RxNorm drug crosswalks (phase 2).'),
37
+ system: z
38
+ .enum(SYSTEM_IDS)
39
+ .optional()
40
+ .describe('For parents/children, force the source code into this system. Omit to auto-detect.'),
41
+ }),
42
+ output: z.object({
43
+ from: z.string().describe('The source value, echoed back.'),
44
+ direction: z.string().describe('The mapping direction that was applied.'),
45
+ resolvedSystem: z
46
+ .string()
47
+ .nullable()
48
+ .describe('The system the source resolved in, or null when not system-scoped.'),
49
+ hits: z
50
+ .array(z
51
+ .object({
52
+ source: z
53
+ .string()
54
+ .describe('Which system or relationship edge produced this hit (e.g. "ICD10CM", "has_ingredient", "NDC").'),
55
+ system: z
56
+ .string()
57
+ .nullable()
58
+ .describe('The code system of the target value, or null when the target is not a system code (e.g. an NDC).'),
59
+ value: z.string().describe('The mapped target value (a code, RXCUI, or NDC).'),
60
+ description: z
61
+ .string()
62
+ .optional()
63
+ .describe('Description of the target when available.'),
64
+ })
65
+ .describe('One crosswalk result tagged with the edge that produced it.'))
66
+ .describe('Crosswalk results, each tagged with the edge that produced it.'),
67
+ }),
68
+ errors: [
69
+ {
70
+ reason: 'no_mapping',
71
+ code: JsonRpcErrorCode.NotFound,
72
+ when: 'The source resolved but has no edge in the requested direction.',
73
+ recovery: 'Confirm the direction is supported for this system, or decode the code with medcode_get_code first.',
74
+ },
75
+ {
76
+ reason: 'direction_unavailable',
77
+ code: JsonRpcErrorCode.InvalidParams,
78
+ when: 'A drug-crosswalk direction was requested before RxNorm (phase 2) is bundled.',
79
+ recovery: 'Use a hierarchy direction (parents/children); drug crosswalks land with RxNorm.',
80
+ },
81
+ {
82
+ reason: 'ambiguous_system',
83
+ code: JsonRpcErrorCode.InvalidParams,
84
+ when: 'The source code is present in more than one system and no `system` was given.',
85
+ recovery: 'Re-call with an explicit `system` to disambiguate.',
86
+ },
87
+ ],
88
+ handler(input, ctx) {
89
+ const svc = getCodeIndexService();
90
+ if (CodeIndexService.isDrugDirection(input.direction) && !svc.hasRxNorm()) {
91
+ throw ctx.fail('direction_unavailable', `The "${input.direction}" crosswalk needs RxNorm, which is not bundled in this release.`, { ...ctx.recoveryFor('direction_unavailable') });
92
+ }
93
+ const result = svc.mapCode(input.from, input.direction, input.system);
94
+ if (result.kind === 'ambiguous') {
95
+ throw ctx.fail('ambiguous_system', `"${input.from.trim()}" exists in multiple systems: ${result.systems.join(', ')}.`, { candidateSystems: result.systems, ...ctx.recoveryFor('ambiguous_system') });
96
+ }
97
+ if (result.kind === 'source_not_found') {
98
+ throw ctx.fail('no_mapping', `No "${input.direction}" mapping found for "${input.from.trim()}".`, { ...ctx.recoveryFor('no_mapping') });
99
+ }
100
+ if (result.hits.length === 0) {
101
+ throw ctx.fail('no_mapping', `"${input.from.trim()}" resolved but has no ${input.direction} (e.g. a top-level code has no parent).`, { ...ctx.recoveryFor('no_mapping') });
102
+ }
103
+ ctx.log.info('Mapped code', {
104
+ from: input.from,
105
+ direction: input.direction,
106
+ hits: result.hits.length,
107
+ });
108
+ return {
109
+ from: input.from.trim(),
110
+ direction: input.direction,
111
+ resolvedSystem: result.resolvedSystem,
112
+ hits: result.hits.map((h) => ({
113
+ source: h.source,
114
+ system: h.system,
115
+ value: h.value,
116
+ ...(h.description ? { description: h.description } : {}),
117
+ })),
118
+ };
119
+ },
120
+ format: (result) => {
121
+ const lines = [
122
+ `## ${result.direction}: ${result.from}`,
123
+ result.resolvedSystem ? `**Resolved system:** ${result.resolvedSystem}` : '',
124
+ '',
125
+ ].filter(Boolean);
126
+ for (const h of result.hits) {
127
+ lines.push(`- **${h.value}**${h.system ? ` (${h.system})` : ''} via ${h.source}${h.description ? `: ${h.description}` : ''}`);
128
+ }
129
+ return [{ type: 'text', text: lines.join('\n') }];
130
+ },
131
+ });
132
+ //# sourceMappingURL=map-codes.tool.js.map