@cosyte/cli 0.0.1

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.
package/dist/mcp.cjs ADDED
@@ -0,0 +1,1161 @@
1
+ 'use strict';
2
+
3
+ var index_js = require('@modelcontextprotocol/sdk/server/index.js');
4
+ var stdio_js = require('@modelcontextprotocol/sdk/server/stdio.js');
5
+ var types_js = require('@modelcontextprotocol/sdk/types.js');
6
+ var util = require('util');
7
+
8
+ // src/mcp/server.ts
9
+
10
+ // src/core/version.ts
11
+ var VERSION = "0.0.0";
12
+
13
+ // src/core/exit-codes.ts
14
+ var EXIT = {
15
+ /** Success: the operation completed; `validate` found the input **valid**. */
16
+ OK: 0,
17
+ /** Operation-level failure: `validate` found the input **invalid** (parseable but non-conformant):
18
+ * a real, expected CI signal that the message is bad and the tool worked. Never emitted for
19
+ * unparseable input (that is `DATAERR`). */
20
+ INVALID: 1,
21
+ /** Usage error: unknown command, bad flag, missing argument (`EX_USAGE`). */
22
+ USAGE: 2,
23
+ /** Data error: input could not be parsed or its format could not be detected (`EX_DATAERR`). */
24
+ DATAERR: 65,
25
+ /** No input: the named file does not exist or is unreadable (`EX_NOINPUT`). */
26
+ NOINPUT: 66,
27
+ /** Unavailable: a required capability is not yet built (e.g. `redact` before `@cosyte/deid`), a
28
+ * distinct non-zero signal that is never a fake success (`EX_UNAVAILABLE`). */
29
+ UNAVAILABLE: 69,
30
+ /** Internal error: an unexpected exception (a bug), distinct from a handled bad input (`EX_SOFTWARE`). */
31
+ SOFTWARE: 70
32
+ };
33
+
34
+ // src/core/diagnostics.ts
35
+ var CLI_CODES = {
36
+ /** A required argument was missing or a flag was invalid. Exit `2`. */
37
+ CLI_USAGE: "CLI_USAGE",
38
+ /** The named file does not exist or could not be read. Exit `66`. */
39
+ CLI_NO_INPUT: "CLI_NO_INPUT",
40
+ /** The input was empty, no bytes to detect or parse. Exit `65`. */
41
+ CLI_EMPTY_INPUT: "CLI_EMPTY_INPUT",
42
+ /** No format signature matched; the CLI will not guess. Names the candidates, never the bytes. Exit `65`. */
43
+ CLI_FORMAT_UNDETECTED: "CLI_FORMAT_UNDETECTED",
44
+ /** More than one format signature matched; the CLI will not guess. Names the candidates, never the bytes. Exit `65`. */
45
+ CLI_FORMAT_AMBIGUOUS: "CLI_FORMAT_AMBIGUOUS",
46
+ /** A recognised format whose parser does not support the requested operation (e.g. `parse` on a
47
+ * format wired only for `inspect`). Names the format + operation, never the bytes. Exit `65`. */
48
+ CLI_FORMAT_UNSUPPORTED: "CLI_FORMAT_UNSUPPORTED",
49
+ /** The optional parser package for a recognised format is not installed. The six breadth parsers
50
+ * (dicom/x12/ccda/ncpdp/astm/mllp) are `optionalDependencies`: normally installed, but if
51
+ * one is absent the CLI degrades to this value-free signal rather than crashing. Exit `69`. */
52
+ CLI_PARSER_UNAVAILABLE: "CLI_PARSER_UNAVAILABLE",
53
+ /** The wrapped parser rejected the input. Positional context only, never the offending bytes. Exit `65`. */
54
+ CLI_PARSE_FAILED: "CLI_PARSE_FAILED",
55
+ /** A command whose ground-layer library is not yet built (e.g. `redact` before `@cosyte/deid`
56
+ * ships). Never a fake success: a distinct, value-free "unavailable" signal. Exit `69`. */
57
+ CLI_NOT_IMPLEMENTED: "CLI_NOT_IMPLEMENTED",
58
+ /** An unexpected internal error (a bug). Exit `70`. */
59
+ CLI_INTERNAL: "CLI_INTERNAL"
60
+ };
61
+ var CliError = class extends Error {
62
+ /** The stable diagnostic code. */
63
+ code;
64
+ /** The exit code this error resolves the invocation to. */
65
+ exit;
66
+ /**
67
+ * @param code - A stable {@link CliCode}.
68
+ * @param exit - The {@link ExitCode} to resolve to.
69
+ * @param message - A **value-free** explanation (positional/structural context only, never PHI).
70
+ */
71
+ constructor(code, exit, message) {
72
+ super(message);
73
+ this.name = "CliError";
74
+ this.code = code;
75
+ this.exit = exit;
76
+ }
77
+ };
78
+ function formatDiagnostic(err) {
79
+ return `cosyte: ${err.code}: ${err.message}`;
80
+ }
81
+ function errorResult(err) {
82
+ return { stdout: "", stderr: `${formatDiagnostic(err)}
83
+ `, exit: err.exit };
84
+ }
85
+ function toCliError(err) {
86
+ if (err instanceof CliError) return err;
87
+ return new CliError(
88
+ CLI_CODES.CLI_INTERNAL,
89
+ EXIT.SOFTWARE,
90
+ "an unexpected internal error occurred (no input is echoed)"
91
+ );
92
+ }
93
+
94
+ // src/core/format.ts
95
+ var SNIFF_BYTES = 512;
96
+ function leadingText(bytes) {
97
+ const slice = bytes.subarray(0, SNIFF_BYTES);
98
+ let text = new TextDecoder("utf-8", { fatal: false }).decode(slice);
99
+ if (text.charCodeAt(0) === 65279) text = text.slice(1);
100
+ return text;
101
+ }
102
+ function trimLeading(prefix) {
103
+ return prefix.replace(/^[ \t\r\n\f]+/, "");
104
+ }
105
+ function isDelimiter(ch) {
106
+ if (ch === void 0) return false;
107
+ const code = ch.charCodeAt(0);
108
+ return code > 32 && code < 127 && !/[A-Za-z0-9]/.test(ch);
109
+ }
110
+ function looksLikeHl7(prefix) {
111
+ const s = trimLeading(prefix);
112
+ return s.startsWith("MSH") && isDelimiter(s[3]);
113
+ }
114
+ function looksLikeMllp(_prefix, bytes) {
115
+ return bytes[0] === 11;
116
+ }
117
+ function looksLikeFhir(prefix) {
118
+ const s = trimLeading(prefix);
119
+ if (!s.startsWith("{")) return false;
120
+ return /"resourceType"\s*:\s*"/.test(s);
121
+ }
122
+ function looksLikeX12(prefix) {
123
+ const s = trimLeading(prefix);
124
+ return s.startsWith("ISA") && isDelimiter(s[3]);
125
+ }
126
+ function looksLikeAstm(prefix) {
127
+ const s = trimLeading(prefix);
128
+ if (!s.startsWith("H") || s.length < 5) return false;
129
+ return isDelimiter(s[1]) && isDelimiter(s[2]) && isDelimiter(s[3]) && isDelimiter(s[4]);
130
+ }
131
+ function looksLikeCcda(prefix) {
132
+ return /<ClinicalDocument[\s>]/.test(prefix);
133
+ }
134
+ function looksLikeNcpdp(prefix) {
135
+ return /<Message[\s>]/.test(prefix) && /ncpdp/i.test(prefix);
136
+ }
137
+ function looksLikeDicom(_prefix, bytes) {
138
+ if (bytes.length < 132) return false;
139
+ return bytes[128] === 68 && bytes[129] === 73 && bytes[130] === 67 && bytes[131] === 77;
140
+ }
141
+ var SIGNATURES = [
142
+ { format: "hl7", match: (prefix) => looksLikeHl7(prefix) },
143
+ { format: "mllp", match: (prefix, bytes) => looksLikeMllp(prefix, bytes) },
144
+ { format: "fhir", match: (prefix) => looksLikeFhir(prefix) },
145
+ { format: "x12", match: (prefix) => looksLikeX12(prefix) },
146
+ { format: "astm", match: (prefix) => looksLikeAstm(prefix) },
147
+ { format: "ccda", match: (prefix) => looksLikeCcda(prefix) },
148
+ { format: "ncpdp", match: (prefix) => looksLikeNcpdp(prefix) },
149
+ { format: "dicom", match: (prefix, bytes) => looksLikeDicom(prefix, bytes) }
150
+ ];
151
+ var DETECTABLE_FORMATS = SIGNATURES.map((s) => s.format);
152
+ function detectFormat(bytes) {
153
+ if (bytes.length === 0) return classifyCandidates([]);
154
+ const prefix = leadingText(bytes);
155
+ const candidates = SIGNATURES.filter((sig) => sig.match(prefix, bytes)).map((sig) => sig.format);
156
+ return classifyCandidates(candidates);
157
+ }
158
+ function classifyCandidates(candidates) {
159
+ const [first, second] = candidates;
160
+ if (second !== void 0) return { format: null, confidence: "ambiguous", candidates };
161
+ if (first !== void 0) return { format: first, confidence: "certain", candidates };
162
+ return { format: null, confidence: "none", candidates: [] };
163
+ }
164
+ function detectionError(detected) {
165
+ if (detected.confidence === "ambiguous") {
166
+ return new CliError(
167
+ CLI_CODES.CLI_FORMAT_AMBIGUOUS,
168
+ EXIT.DATAERR,
169
+ `input matched multiple formats (${detected.candidates.join(", ")}); disambiguate with --format`
170
+ );
171
+ }
172
+ return new CliError(
173
+ CLI_CODES.CLI_FORMAT_UNDETECTED,
174
+ EXIT.DATAERR,
175
+ `could not detect the input format; re-run with --format (detectable: ${DETECTABLE_FORMATS.join(", ")})`
176
+ );
177
+ }
178
+ var KNOWN_FORMATS = [
179
+ "hl7",
180
+ "fhir",
181
+ "dicom",
182
+ "x12",
183
+ "ccda",
184
+ "ncpdp",
185
+ "astm",
186
+ "mllp"
187
+ ];
188
+ function asCosyteFormat(value) {
189
+ return KNOWN_FORMATS.includes(value) ? value : null;
190
+ }
191
+
192
+ // src/core/findings.ts
193
+ function formatHl7Position(pos) {
194
+ let out = `seg[${String(pos.segmentIndex)}]`;
195
+ if (pos.fieldIndex !== void 0) out += `.field[${String(pos.fieldIndex)}]`;
196
+ if (pos.repetitionIndex !== void 0) out += `.rep[${String(pos.repetitionIndex)}]`;
197
+ if (pos.componentIndex !== void 0) out += `.comp[${String(pos.componentIndex)}]`;
198
+ if (pos.subcomponentIndex !== void 0) out += `.sub[${String(pos.subcomponentIndex)}]`;
199
+ return out;
200
+ }
201
+
202
+ // src/core/parsers.ts
203
+ var OP_SUPPORT = {
204
+ hl7: /* @__PURE__ */ new Set(["parse", "inspect", "fmt", "validate"]),
205
+ fhir: /* @__PURE__ */ new Set(["parse", "inspect", "fmt", "validate"]),
206
+ x12: /* @__PURE__ */ new Set(["parse", "inspect", "fmt", "validate"]),
207
+ astm: /* @__PURE__ */ new Set(["parse", "inspect", "fmt", "validate"]),
208
+ ncpdp: /* @__PURE__ */ new Set(["parse", "inspect", "fmt", "validate"]),
209
+ ccda: /* @__PURE__ */ new Set(["inspect", "fmt", "validate"]),
210
+ dicom: /* @__PURE__ */ new Set(["inspect", "validate"]),
211
+ mllp: /* @__PURE__ */ new Set(["parse", "inspect"])
212
+ };
213
+ function supportsOp(format, op) {
214
+ return OP_SUPPORT[format].has(op);
215
+ }
216
+ function formatsSupporting(op) {
217
+ return Object.keys(OP_SUPPORT).filter((f) => OP_SUPPORT[f].has(op)).sort();
218
+ }
219
+ function valueFreeLocator(pos) {
220
+ if (typeof pos !== "object" || pos === null) return "?";
221
+ const parts = [];
222
+ for (const [k, v] of Object.entries(pos)) {
223
+ if (typeof v === "number") parts.push(`${k}[${String(v)}]`);
224
+ }
225
+ return parts.length > 0 ? parts.join(".") : "?";
226
+ }
227
+ var ERROR_SEVERITIES = /* @__PURE__ */ new Set(["error", "fatal"]);
228
+ function anyError(findings) {
229
+ return findings.some((f) => ERROR_SEVERITIES.has(f.severity));
230
+ }
231
+ async function loadOptional(format, load) {
232
+ try {
233
+ return await load();
234
+ } catch (e) {
235
+ if (isModuleNotFound(e)) {
236
+ throw new CliError(
237
+ CLI_CODES.CLI_PARSER_UNAVAILABLE,
238
+ EXIT.UNAVAILABLE,
239
+ `the @cosyte/${format} parser is not installed; install it to use this format (it is an optional dependency)`
240
+ );
241
+ }
242
+ throw e;
243
+ }
244
+ }
245
+ function isModuleNotFound(e) {
246
+ if (typeof e !== "object" || e === null) return false;
247
+ const code = "code" in e ? e.code : void 0;
248
+ if (code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND") return true;
249
+ const message = "message" in e && typeof e.message === "string" ? e.message : "";
250
+ return /Cannot find (package|module)|Failed to (load|resolve)/i.test(message);
251
+ }
252
+ async function parseFormat(format, bytes) {
253
+ switch (format) {
254
+ case "hl7": {
255
+ const { parseHL7 } = await import('@cosyte/hl7');
256
+ const msg = parseHL7(Buffer.from(bytes));
257
+ return {
258
+ model: msg.toJSON(),
259
+ warnings: msg.warnings.map((w) => ({ code: w.code, position: w.position }))
260
+ };
261
+ }
262
+ case "fhir": {
263
+ const { parseResource, serializeResource } = await import('@cosyte/fhir');
264
+ const text = decode(bytes);
265
+ const { resource, issues } = parseResource(text);
266
+ const model = JSON.parse(serializeResource(resource));
267
+ return {
268
+ model,
269
+ warnings: issues.map((i) => ({
270
+ code: i.code,
271
+ severity: i.severity,
272
+ expression: i.expression
273
+ }))
274
+ };
275
+ }
276
+ case "x12": {
277
+ const { parseX12 } = await loadOptional("x12", () => import('@cosyte/x12'));
278
+ const ix = parseX12(Buffer.from(bytes));
279
+ return {
280
+ model: ix,
281
+ warnings: ix.warnings.map((w) => ({ code: w.code, position: w.position }))
282
+ };
283
+ }
284
+ case "astm": {
285
+ const { parseAstmRecords } = await loadOptional("astm", () => import('@cosyte/astm'));
286
+ const msg = parseAstmRecords(decode(bytes));
287
+ return {
288
+ model: msg,
289
+ warnings: msg.warnings.map((w) => ({ code: w.code, position: w.position }))
290
+ };
291
+ }
292
+ case "ncpdp": {
293
+ const { parseScript } = await loadOptional("ncpdp", () => import('@cosyte/ncpdp'));
294
+ const msg = parseScript(decode(bytes));
295
+ return {
296
+ model: msg,
297
+ warnings: msg.warnings.map((w) => ({ code: w.code, position: w.position }))
298
+ };
299
+ }
300
+ case "dicom":
301
+ case "ccda":
302
+ case "mllp":
303
+ throw unsupportedInternal(format, "parse");
304
+ }
305
+ }
306
+ async function inspectFormat(format, bytes) {
307
+ switch (format) {
308
+ case "hl7": {
309
+ const { parseHL7 } = await import('@cosyte/hl7');
310
+ const msg = parseHL7(Buffer.from(bytes));
311
+ const segments = {};
312
+ const all = msg.allSegments();
313
+ for (const seg of all) segments[seg.type] = (segments[seg.type] ?? 0) + 1;
314
+ return {
315
+ format: "hl7",
316
+ messageType: msg.meta.type ?? null,
317
+ version: msg.meta.version ?? null,
318
+ segmentCount: all.length,
319
+ segments,
320
+ warningCount: msg.warnings.length
321
+ };
322
+ }
323
+ case "fhir": {
324
+ const { parseResource, resourceType, readBundle } = await import('@cosyte/fhir');
325
+ const { resource, issues } = parseResource(decode(bytes));
326
+ const rt = resourceType(resource) ?? null;
327
+ if (rt === "Bundle") {
328
+ const bundle = readBundle(resource);
329
+ const entryResourceTypes = {};
330
+ for (const e of bundle.entries) {
331
+ const t = e.resourceType ?? "(none)";
332
+ entryResourceTypes[t] = (entryResourceTypes[t] ?? 0) + 1;
333
+ }
334
+ return {
335
+ format: "fhir",
336
+ resourceType: rt,
337
+ bundleType: bundle.type ?? null,
338
+ entryCount: bundle.entries.length,
339
+ entryResourceTypes,
340
+ issueCount: issues.length
341
+ };
342
+ }
343
+ return { format: "fhir", resourceType: rt, issueCount: issues.length };
344
+ }
345
+ case "x12": {
346
+ const { parseX12 } = await loadOptional("x12", () => import('@cosyte/x12'));
347
+ const ix = parseX12(Buffer.from(bytes));
348
+ const transactionSetIds = {};
349
+ let transactionCount = 0;
350
+ let segmentCount = 0;
351
+ for (const g of ix.groups) {
352
+ for (const t of g.transactions) {
353
+ transactionCount += 1;
354
+ segmentCount += t.segments.length;
355
+ const id = t.st.elements[1] ?? "(unknown)";
356
+ transactionSetIds[id] = (transactionSetIds[id] ?? 0) + 1;
357
+ }
358
+ }
359
+ return {
360
+ format: "x12",
361
+ groupCount: ix.groups.length,
362
+ transactionCount,
363
+ transactionSetIds,
364
+ segmentCount,
365
+ warningCount: ix.warnings.length
366
+ };
367
+ }
368
+ case "astm": {
369
+ const { parseAstmRecords } = await loadOptional("astm", () => import('@cosyte/astm'));
370
+ const msg = parseAstmRecords(decode(bytes));
371
+ const recordTypes = {};
372
+ for (const r of msg.records) recordTypes[r.type] = (recordTypes[r.type] ?? 0) + 1;
373
+ return {
374
+ format: "astm",
375
+ messageKind: msg.classification.kind,
376
+ recordCount: msg.records.length,
377
+ recordTypes,
378
+ warningCount: msg.warnings.length
379
+ };
380
+ }
381
+ case "ccda": {
382
+ const { parseCcda } = await loadOptional("ccda", () => import('@cosyte/ccda'));
383
+ const doc = parseCcda(decode(bytes));
384
+ const sectionCodes = [];
385
+ for (const s of doc.sections) {
386
+ const code = s.code?.code;
387
+ if (typeof code === "string") sectionCodes.push(code);
388
+ }
389
+ return {
390
+ format: "ccda",
391
+ documentType: doc.documentType ?? null,
392
+ sectionCount: doc.sections.length,
393
+ sectionCodes,
394
+ warningCount: doc.warnings.length
395
+ };
396
+ }
397
+ case "dicom": {
398
+ const { parseDicom } = await loadOptional("dicom", () => import('@cosyte/dicom'));
399
+ let warningCount = 0;
400
+ const ds = parseDicom(Buffer.from(bytes), { onWarning: () => warningCount += 1 });
401
+ return {
402
+ format: "dicom",
403
+ sopClassUid: ds.fileMeta?.mediaStorageSOPClassUID ?? null,
404
+ transferSyntaxUid: ds.fileMeta?.transferSyntaxUID ?? null,
405
+ elementCount: ds.elements().length,
406
+ warningCount
407
+ };
408
+ }
409
+ case "ncpdp": {
410
+ const { parseScript } = await loadOptional("ncpdp", () => import('@cosyte/ncpdp'));
411
+ const msg = parseScript(decode(bytes));
412
+ return {
413
+ format: "ncpdp",
414
+ standard: "SCRIPT",
415
+ messageType: msg.body.kind ?? null,
416
+ version: msg.header.version ?? null,
417
+ warningCount: msg.warnings.length
418
+ };
419
+ }
420
+ case "mllp": {
421
+ const { payloads, warningCount } = await deframeMllp(bytes);
422
+ return { format: "mllp", frameCount: payloads.length, warningCount };
423
+ }
424
+ }
425
+ }
426
+ async function validateFormat(format, bytes) {
427
+ switch (format) {
428
+ case "fhir": {
429
+ const { parseResource, validateResource } = await import('@cosyte/fhir');
430
+ const { resource, issues } = parseResource(decode(bytes));
431
+ const validation = validateResource(resource);
432
+ const findings = [
433
+ ...issues.map((i) => finding(i.code, i.severity, i.expression)),
434
+ ...validation.issues.map((i) => finding(i.code, i.severity, i.expression, i.constraint))
435
+ ];
436
+ return { findings, valid: !anyError(findings) };
437
+ }
438
+ case "hl7": {
439
+ const { parseHL7 } = await import('@cosyte/hl7');
440
+ const msg = parseHL7(Buffer.from(bytes));
441
+ const findings = msg.warnings.map(
442
+ (w) => finding(w.code, "warning", formatHl7Position(w.position))
443
+ );
444
+ return { findings, valid: !anyError(findings) };
445
+ }
446
+ case "x12": {
447
+ const { parseX12 } = await loadOptional("x12", () => import('@cosyte/x12'));
448
+ const ix = parseX12(Buffer.from(bytes));
449
+ const findings = ix.warnings.map(
450
+ (w) => finding(w.code, "warning", valueFreeLocator(w.position))
451
+ );
452
+ return { findings, valid: !anyError(findings) };
453
+ }
454
+ case "astm": {
455
+ const { parseAstmRecords } = await loadOptional("astm", () => import('@cosyte/astm'));
456
+ const msg = parseAstmRecords(decode(bytes));
457
+ const findings = msg.warnings.map(
458
+ (w) => finding(w.code, "warning", valueFreeLocator(w.position))
459
+ );
460
+ return { findings, valid: !anyError(findings) };
461
+ }
462
+ case "ccda": {
463
+ const { parseCcda } = await loadOptional("ccda", () => import('@cosyte/ccda'));
464
+ const doc = parseCcda(decode(bytes));
465
+ const findings = doc.warnings.map(
466
+ (w) => finding(w.code, "warning", valueFreeLocator(w.position))
467
+ );
468
+ return { findings, valid: !anyError(findings) };
469
+ }
470
+ case "dicom": {
471
+ const { parseDicom } = await loadOptional("dicom", () => import('@cosyte/dicom'));
472
+ const findings = [];
473
+ parseDicom(Buffer.from(bytes), {
474
+ onWarning: (w) => findings.push(finding(w.code, "warning", valueFreeLocator(w.position)))
475
+ });
476
+ return { findings, valid: !anyError(findings) };
477
+ }
478
+ case "ncpdp": {
479
+ const { parseScript } = await loadOptional("ncpdp", () => import('@cosyte/ncpdp'));
480
+ const msg = parseScript(decode(bytes));
481
+ const findings = msg.warnings.map(
482
+ (w) => finding(w.code, "warning", valueFreeLocator(w.position))
483
+ );
484
+ return { findings, valid: !anyError(findings) };
485
+ }
486
+ case "mllp":
487
+ throw unsupportedInternal(format, "validate");
488
+ }
489
+ }
490
+ async function deframeMllp(bytes) {
491
+ if (bytes.lastIndexOf(11) > bytes.lastIndexOf(28)) {
492
+ throw new CliError(
493
+ CLI_CODES.CLI_PARSE_FAILED,
494
+ EXIT.DATAERR,
495
+ "truncated MLLP stream: an unterminated frame at the end of input (no closing FS/CR)"
496
+ );
497
+ }
498
+ const { FrameReader } = await loadOptional("mllp", () => import('@cosyte/mllp'));
499
+ const payloads = [];
500
+ let warningCount = 0;
501
+ const reader = new FrameReader({
502
+ onFrame: (payload) => payloads.push(Buffer.from(payload)),
503
+ onWarning: () => warningCount += 1
504
+ });
505
+ reader.push(Buffer.from(bytes));
506
+ return { payloads, warningCount };
507
+ }
508
+ function decode(bytes) {
509
+ return new TextDecoder("utf-8", { fatal: false }).decode(bytes);
510
+ }
511
+ function finding(code, severity, location, constraint) {
512
+ const loc = constraint !== void 0 ? `${location} (${constraint})` : location;
513
+ return { code, severity, location: loc };
514
+ }
515
+ function unsupportedInternal(format, op) {
516
+ return new CliError(
517
+ CLI_CODES.CLI_FORMAT_UNSUPPORTED,
518
+ EXIT.DATAERR,
519
+ `format '${format}' does not support \`${op}\``
520
+ );
521
+ }
522
+
523
+ // src/core/input.ts
524
+ async function resolveInput(source, formatOverride, deps, op) {
525
+ if (source === void 0) {
526
+ return fail(
527
+ new CliError(
528
+ CLI_CODES.CLI_USAGE,
529
+ EXIT.USAGE,
530
+ "missing <file> argument; pass a path or `-` to read stdin"
531
+ )
532
+ );
533
+ }
534
+ let bytes;
535
+ try {
536
+ bytes = source === "-" ? await deps.readStdin() : await deps.readFile(source);
537
+ } catch (e) {
538
+ if (e instanceof CliError) return fail(e);
539
+ throw e;
540
+ }
541
+ if (bytes.length === 0) {
542
+ return fail(new CliError(CLI_CODES.CLI_EMPTY_INPUT, EXIT.DATAERR, "input is empty"));
543
+ }
544
+ let format;
545
+ if (formatOverride !== void 0) {
546
+ const narrowed = asCosyteFormat(formatOverride);
547
+ if (narrowed === null) {
548
+ return fail(
549
+ new CliError(
550
+ CLI_CODES.CLI_USAGE,
551
+ EXIT.USAGE,
552
+ "unknown --format value; expected one of hl7, fhir, dicom, x12, ccda, ncpdp, astm, mllp"
553
+ )
554
+ );
555
+ }
556
+ format = narrowed;
557
+ } else {
558
+ const detected = detectFormat(bytes);
559
+ if (detected.format === null) return fail(detectionError(detected));
560
+ format = detected.format;
561
+ }
562
+ if (!supportsOp(format, op)) {
563
+ return fail(
564
+ new CliError(
565
+ CLI_CODES.CLI_FORMAT_UNSUPPORTED,
566
+ EXIT.DATAERR,
567
+ `format '${format}' does not support \`${op}\` in this CLI build (${op} supports: ${formatsSupporting(op).join(", ")})`
568
+ )
569
+ );
570
+ }
571
+ return { ok: true, input: { format, bytes } };
572
+ }
573
+ function fail(e) {
574
+ return { ok: false, result: errorResult(e) };
575
+ }
576
+
577
+ // src/core/phi.ts
578
+ var VALUE_FREE = { showValues: false };
579
+ var UNSAFE_EXCERPT_MAX = 200;
580
+ function unsafeInputSuffix(bytes, posture) {
581
+ if (!posture.showValues) return "";
582
+ const text = new TextDecoder("utf-8", { fatal: false }).decode(bytes.subarray(0, UNSAFE_EXCERPT_MAX)).replace(/[\r\n\t]+/g, " ").trim();
583
+ if (text.length === 0) return "";
584
+ return ` [unsafe-show-values] offending input (first ${String(UNSAFE_EXCERPT_MAX)} bytes): ${text}`;
585
+ }
586
+
587
+ // src/core/wrap.ts
588
+ function extractStableCode(e) {
589
+ if (typeof e === "object" && e !== null && "code" in e) {
590
+ const raw = e.code;
591
+ if (typeof raw === "string" && /^[A-Z][A-Z0-9_]*$/.test(raw)) return raw;
592
+ }
593
+ return null;
594
+ }
595
+ function parseFailureResult(format, bytes, posture, e) {
596
+ const code = extractStableCode(e);
597
+ const detail = code === null ? "" : ` (${code})`;
598
+ const suffix = unsafeInputSuffix(bytes, posture);
599
+ return {
600
+ stdout: "",
601
+ stderr: `cosyte: ${CLI_CODES.CLI_PARSE_FAILED}: the ${format} parser rejected the input${detail}${suffix}
602
+ `,
603
+ exit: EXIT.DATAERR
604
+ };
605
+ }
606
+
607
+ // src/commands/convert.ts
608
+ var CONVERT_OPTIONS = {
609
+ to: { type: "string" },
610
+ format: { type: "string" },
611
+ json: { type: "boolean", default: false },
612
+ quiet: { type: "boolean", default: false },
613
+ "no-color": { type: "boolean", default: false }
614
+ };
615
+ var SUPPORTED_TARGET = "fhir";
616
+ var ERROR_SEVERITIES2 = /* @__PURE__ */ new Set(["error", "fatal"]);
617
+ async function convertCommand(args, deps, posture = VALUE_FREE) {
618
+ let values;
619
+ let positionals;
620
+ try {
621
+ const parsed = util.parseArgs({ args, options: CONVERT_OPTIONS, allowPositionals: true });
622
+ values = parsed.values;
623
+ positionals = parsed.positionals;
624
+ } catch {
625
+ return errorResult(
626
+ new CliError(
627
+ CLI_CODES.CLI_USAGE,
628
+ EXIT.USAGE,
629
+ "invalid arguments to `convert` (see `cosyte convert --help`)"
630
+ )
631
+ );
632
+ }
633
+ if (values.to === void 0) {
634
+ return errorResult(
635
+ new CliError(CLI_CODES.CLI_USAGE, EXIT.USAGE, "convert requires --to fhir (the only target)")
636
+ );
637
+ }
638
+ if (values.to !== SUPPORTED_TARGET) {
639
+ return errorResult(
640
+ new CliError(
641
+ CLI_CODES.CLI_USAGE,
642
+ EXIT.USAGE,
643
+ `unsupported --to '${values.to}'; only --to fhir is supported (HL7 v2 \u2192 FHIR R4)`
644
+ )
645
+ );
646
+ }
647
+ const resolved = await resolveInput(positionals[0], values.format, deps, "parse");
648
+ if (!resolved.ok) return resolved.result;
649
+ const { format, bytes } = resolved.input;
650
+ if (format !== "hl7") {
651
+ return errorResult(
652
+ new CliError(
653
+ CLI_CODES.CLI_FORMAT_UNSUPPORTED,
654
+ EXIT.DATAERR,
655
+ `convert reads HL7 v2 and writes FHIR R4; the input is '${format}', not an HL7 v2 source`
656
+ )
657
+ );
658
+ }
659
+ const outcome = await runConvert(bytes, posture);
660
+ if (!outcome.ok) return outcome.result;
661
+ const conversion = outcome.conversion;
662
+ const { hasError, report } = convertOutcome(conversion.findings);
663
+ const exit = hasError ? EXIT.INVALID : EXIT.OK;
664
+ if (values.json === true) {
665
+ const body = {
666
+ format: SUPPORTED_TARGET,
667
+ bundle: JSON.parse(conversion.bundleText),
668
+ findings: conversion.findings
669
+ };
670
+ return { stdout: `${JSON.stringify(body)}
671
+ `, stderr: "", exit };
672
+ }
673
+ const stderr = values.quiet === true ? "" : report;
674
+ return { stdout: `${conversion.bundleText}
675
+ `, stderr, exit };
676
+ }
677
+ async function runConvert(bytes, posture) {
678
+ const [{ parseHL7 }, { toFhir }, { serializeResource }] = await Promise.all([
679
+ import('@cosyte/hl7'),
680
+ import('@cosyte/transform'),
681
+ import('@cosyte/fhir')
682
+ ]);
683
+ let msg;
684
+ try {
685
+ msg = parseHL7(Buffer.from(bytes));
686
+ } catch (e) {
687
+ return { ok: false, result: parseFailureResult("hl7", bytes, posture, e) };
688
+ }
689
+ const { bundle, issues } = toFhir(msg);
690
+ return {
691
+ ok: true,
692
+ conversion: {
693
+ bundleText: serializeResource(bundle),
694
+ findings: issues.map((i) => toFinding(i.code, i.severity, i.v2Location, i.fhirPath))
695
+ }
696
+ };
697
+ }
698
+ function toFinding(code, severity, v2Location, fhirPath) {
699
+ const location = fhirPath !== void 0 ? `${v2Location} \u2192 ${fhirPath}` : v2Location;
700
+ return { code, severity, location };
701
+ }
702
+ function convertOutcome(findings) {
703
+ const lines = findings.map((f) => `cosyte: convert: ${f.severity} ${f.code} at ${f.location}
704
+ `);
705
+ const errors = findings.filter((f) => ERROR_SEVERITIES2.has(f.severity)).length;
706
+ const others = findings.length - errors;
707
+ const summary = errors > 0 ? `cosyte: convert: hl7 \u2192 fhir produced ${String(errors)} error(s), ${String(others)} other finding(s)
708
+ ` : `cosyte: convert: hl7 \u2192 fhir OK (${String(findings.length)} finding(s))
709
+ `;
710
+ return { hasError: errors > 0, report: lines.join("") + summary };
711
+ }
712
+ var INSPECT_OPTIONS = {
713
+ format: { type: "string" },
714
+ json: { type: "boolean", default: false },
715
+ quiet: { type: "boolean", default: false },
716
+ "no-color": { type: "boolean", default: false }
717
+ };
718
+ async function inspectCommand(args, deps, posture = VALUE_FREE) {
719
+ let values;
720
+ let positionals;
721
+ try {
722
+ const parsed = util.parseArgs({ args, options: INSPECT_OPTIONS, allowPositionals: true });
723
+ values = parsed.values;
724
+ positionals = parsed.positionals;
725
+ } catch {
726
+ return errorResult(
727
+ new CliError(
728
+ CLI_CODES.CLI_USAGE,
729
+ EXIT.USAGE,
730
+ "invalid arguments to `inspect` (see `cosyte inspect --help`)"
731
+ )
732
+ );
733
+ }
734
+ const resolved = await resolveInput(positionals[0], values.format, deps, "inspect");
735
+ if (!resolved.ok) return resolved.result;
736
+ const { format, bytes } = resolved.input;
737
+ let summary;
738
+ try {
739
+ summary = await inspectFormat(format, bytes);
740
+ } catch (e) {
741
+ if (e instanceof CliError) return errorResult(e);
742
+ return parseFailureResult(format, bytes, posture, e);
743
+ }
744
+ const stdout = values.json === true ? `${JSON.stringify(summary)}
745
+ ` : renderSummary(summary);
746
+ return { stdout, stderr: "", exit: EXIT.OK };
747
+ }
748
+ function renderSummary(summary) {
749
+ const lines = [];
750
+ const countLines = (label, counts) => {
751
+ if (label.length > 0) lines.push(label);
752
+ for (const key of Object.keys(counts).sort()) lines.push(` ${key}: ${String(counts[key])}`);
753
+ };
754
+ switch (summary.format) {
755
+ case "hl7":
756
+ lines.push(`format: hl7`);
757
+ lines.push(`message type: ${summary.messageType ?? "(unknown)"}`);
758
+ lines.push(`version: ${summary.version ?? "(unknown)"}`);
759
+ lines.push(`segments: ${String(summary.segmentCount)}`);
760
+ countLines("", summary.segments);
761
+ lines.push(`warnings: ${String(summary.warningCount)}`);
762
+ break;
763
+ case "fhir":
764
+ lines.push(`format: fhir`);
765
+ lines.push(`resource type: ${summary.resourceType ?? "(unknown)"}`);
766
+ if (summary.entryCount !== void 0) {
767
+ lines.push(`bundle type: ${summary.bundleType ?? "(unknown)"}`);
768
+ lines.push(`entries: ${String(summary.entryCount)}`);
769
+ countLines("", summary.entryResourceTypes ?? {});
770
+ }
771
+ lines.push(`issues: ${String(summary.issueCount)}`);
772
+ break;
773
+ case "x12":
774
+ lines.push(`format: x12`);
775
+ lines.push(`groups: ${String(summary.groupCount)}`);
776
+ lines.push(`transactions: ${String(summary.transactionCount)}`);
777
+ countLines("transaction sets:", summary.transactionSetIds);
778
+ lines.push(`segments: ${String(summary.segmentCount)}`);
779
+ lines.push(`warnings: ${String(summary.warningCount)}`);
780
+ break;
781
+ case "astm":
782
+ lines.push(`format: astm`);
783
+ lines.push(`message kind: ${summary.messageKind}`);
784
+ lines.push(`records: ${String(summary.recordCount)}`);
785
+ countLines("", summary.recordTypes);
786
+ lines.push(`warnings: ${String(summary.warningCount)}`);
787
+ break;
788
+ case "ccda":
789
+ lines.push(`format: ccda`);
790
+ lines.push(`document type: ${summary.documentType ?? "(unknown)"}`);
791
+ lines.push(`sections: ${String(summary.sectionCount)}`);
792
+ if (summary.sectionCodes.length > 0) {
793
+ lines.push(`section codes: ${summary.sectionCodes.join(", ")}`);
794
+ }
795
+ lines.push(`warnings: ${String(summary.warningCount)}`);
796
+ break;
797
+ case "dicom":
798
+ lines.push(`format: dicom`);
799
+ lines.push(`sop class uid: ${summary.sopClassUid ?? "(unknown)"}`);
800
+ lines.push(`transfer syntax:${summary.transferSyntaxUid ?? " (unknown)"}`);
801
+ lines.push(`elements: ${String(summary.elementCount)}`);
802
+ lines.push(`warnings: ${String(summary.warningCount)}`);
803
+ break;
804
+ case "ncpdp":
805
+ lines.push(`format: ncpdp`);
806
+ lines.push(`standard: SCRIPT`);
807
+ lines.push(`message type: ${summary.messageType ?? "(unknown)"}`);
808
+ lines.push(`version: ${summary.version ?? "(unknown)"}`);
809
+ lines.push(`warnings: ${String(summary.warningCount)}`);
810
+ break;
811
+ case "mllp":
812
+ lines.push(`format: mllp`);
813
+ lines.push(`frames: ${String(summary.frameCount)}`);
814
+ lines.push(`warnings:${String(summary.warningCount)}`);
815
+ break;
816
+ }
817
+ return lines.join("\n") + "\n";
818
+ }
819
+ var PARSE_OPTIONS = {
820
+ format: { type: "string" },
821
+ ndjson: { type: "boolean", default: false },
822
+ json: { type: "boolean", default: false },
823
+ quiet: { type: "boolean", default: false },
824
+ "no-color": { type: "boolean", default: false }
825
+ };
826
+ async function parseCommand(args, deps, posture = VALUE_FREE) {
827
+ let values;
828
+ let positionals;
829
+ try {
830
+ const parsed = util.parseArgs({ args, options: PARSE_OPTIONS, allowPositionals: true });
831
+ values = parsed.values;
832
+ positionals = parsed.positionals;
833
+ } catch {
834
+ return errorResult(
835
+ new CliError(
836
+ CLI_CODES.CLI_USAGE,
837
+ EXIT.USAGE,
838
+ "invalid arguments to `parse` (see `cosyte parse --help`)"
839
+ )
840
+ );
841
+ }
842
+ const resolved = await resolveInput(positionals[0], values.format, deps, "parse");
843
+ if (!resolved.ok) return resolved.result;
844
+ const { format, bytes } = resolved.input;
845
+ if (format === "mllp" || values.ndjson === true) {
846
+ return await parseMulti(format, bytes, values.ndjson === true, values.quiet === true);
847
+ }
848
+ return await parseSingle(format, bytes, values.json === true, values.quiet === true, posture);
849
+ }
850
+ async function parseSingle(format, bytes, json, quiet, posture) {
851
+ let result;
852
+ try {
853
+ result = await parseFormat(format, bytes);
854
+ } catch (e) {
855
+ if (e instanceof CliError) return errorResult(e);
856
+ return parseFailureResult(format, bytes, posture, e);
857
+ }
858
+ const envelope = { format, model: result.model, warnings: result.warnings };
859
+ const stdout = (json ? JSON.stringify(envelope) : JSON.stringify(envelope, null, 2)) + "\n";
860
+ const n = envelope.warnings.length;
861
+ const stderr = n > 0 && !quiet ? `cosyte: parsed ${format} with ${String(n)} warning(s) (see .warnings in output)
862
+ ` : "";
863
+ return { stdout, stderr, exit: EXIT.OK };
864
+ }
865
+ async function parseMulti(format, bytes, ndjson, quiet) {
866
+ let records;
867
+ let recordFormat;
868
+ try {
869
+ if (format === "mllp") {
870
+ const { payloads } = await deframeMllp(bytes);
871
+ records = payloads;
872
+ recordFormat = "hl7";
873
+ } else {
874
+ records = splitLines(bytes);
875
+ recordFormat = format;
876
+ }
877
+ } catch (e) {
878
+ if (e instanceof CliError) return errorResult(e);
879
+ return parseFailureResult(format, bytes, VALUE_FREE, e);
880
+ }
881
+ if (records.length === 0) {
882
+ return errorResult(
883
+ new CliError(
884
+ CLI_CODES.CLI_PARSE_FAILED,
885
+ EXIT.DATAERR,
886
+ `no ${format === "mllp" ? "MLLP frame" : "record"} found in the input`
887
+ )
888
+ );
889
+ }
890
+ const lines = [];
891
+ let failed = 0;
892
+ let warnings = 0;
893
+ for (let i = 0; i < records.length; i += 1) {
894
+ const rec = records[i];
895
+ try {
896
+ const { model, warnings: ws } = await parseFormat(recordFormat, rec);
897
+ warnings += ws.length;
898
+ lines.push({ record: i, format: recordFormat, model, warnings: ws });
899
+ } catch (e) {
900
+ if (e instanceof CliError) return errorResult(e);
901
+ failed += 1;
902
+ lines.push({
903
+ record: i,
904
+ format: recordFormat,
905
+ error: extractStableCode(e) ?? "CLI_PARSE_FAILED"
906
+ });
907
+ }
908
+ }
909
+ const stdout = lines.map((l) => JSON.stringify(l)).join("\n") + "\n";
910
+ const exit = failed > 0 ? EXIT.DATAERR : EXIT.OK;
911
+ const stderr = quiet ? "" : `cosyte: parsed ${String(records.length)} ${recordFormat} record(s) (${String(warnings)} warning(s), ${String(failed)} failed)` + (ndjson ? " [ndjson]" : format === "mllp" ? " [mllp]" : "") + "\n";
912
+ return { stdout, stderr, exit };
913
+ }
914
+ function splitLines(bytes) {
915
+ const text = new TextDecoder("utf-8", { fatal: false }).decode(bytes);
916
+ const enc = new TextEncoder();
917
+ return text.split(/\r?\n/).filter((line) => line.trim().length > 0).map((line) => enc.encode(line));
918
+ }
919
+ var VALIDATE_OPTIONS = {
920
+ format: { type: "string" },
921
+ profile: { type: "string" },
922
+ json: { type: "boolean", default: false },
923
+ quiet: { type: "boolean", default: false },
924
+ "no-color": { type: "boolean", default: false }
925
+ };
926
+ var ERROR_SEVERITIES3 = /* @__PURE__ */ new Set(["error", "fatal"]);
927
+ async function validateCommand(args, deps, posture = VALUE_FREE) {
928
+ let values;
929
+ let positionals;
930
+ try {
931
+ const parsed = util.parseArgs({ args, options: VALIDATE_OPTIONS, allowPositionals: true });
932
+ values = parsed.values;
933
+ positionals = parsed.positionals;
934
+ } catch {
935
+ return errorResult(
936
+ new CliError(
937
+ CLI_CODES.CLI_USAGE,
938
+ EXIT.USAGE,
939
+ "invalid arguments to `validate` (see `cosyte validate --help`)"
940
+ )
941
+ );
942
+ }
943
+ if (values.profile !== void 0) {
944
+ return errorResult(
945
+ new CliError(
946
+ CLI_CODES.CLI_NOT_IMPLEMENTED,
947
+ EXIT.UNAVAILABLE,
948
+ "profile-based validation is not yet available: the CLI bundles no profiles and cannot yet load one; validating against `--profile` is gated on that surface. Re-run without --profile for base validation."
949
+ )
950
+ );
951
+ }
952
+ const resolved = await resolveInput(positionals[0], values.format, deps, "validate");
953
+ if (!resolved.ok) return resolved.result;
954
+ const { format, bytes } = resolved.input;
955
+ let verdict;
956
+ try {
957
+ verdict = await validateFormat(format, bytes);
958
+ } catch (e) {
959
+ if (e instanceof CliError) return errorResult(e);
960
+ return parseFailureResult(format, bytes, posture, e);
961
+ }
962
+ const exit = verdict.valid ? EXIT.OK : EXIT.INVALID;
963
+ if (values.json === true) {
964
+ const body = { format, valid: verdict.valid, findings: verdict.findings };
965
+ return { stdout: `${JSON.stringify(body)}
966
+ `, stderr: "", exit };
967
+ }
968
+ if (values.quiet === true) {
969
+ return { stdout: "", stderr: "", exit };
970
+ }
971
+ return { stdout: "", stderr: renderReport(format, verdict), exit };
972
+ }
973
+ function renderReport(format, verdict) {
974
+ const lines = verdict.findings.map(
975
+ (f) => `cosyte: validate: ${f.severity} ${f.code} at ${f.location}
976
+ `
977
+ );
978
+ const errors = verdict.findings.filter((f) => ERROR_SEVERITIES3.has(f.severity)).length;
979
+ const others = verdict.findings.length - errors;
980
+ const summary = verdict.valid ? `cosyte: validate: ${format} is valid (${String(verdict.findings.length)} finding(s))
981
+ ` : `cosyte: validate: ${format} is INVALID (${String(errors)} error(s), ${String(others)} other finding(s))
982
+ `;
983
+ return lines.join("") + summary;
984
+ }
985
+
986
+ // src/mcp/tools.ts
987
+ var CONTENT_PROP = {
988
+ content: {
989
+ type: "string",
990
+ description: "The raw healthcare message to operate on (as text; e.g. an HL7 v2 message or a FHIR JSON resource)."
991
+ }
992
+ };
993
+ var FORMAT_PROP = {
994
+ format: {
995
+ type: "string",
996
+ enum: ["hl7", "fhir", "dicom", "x12", "ccda", "ncpdp", "astm", "mllp"],
997
+ description: "Optional format override; omit to autodetect by content. A format whose parser does not support the requested operation returns a value-free CLI_FORMAT_UNSUPPORTED (never a fake)."
998
+ }
999
+ };
1000
+ var TOOL_DEFS = [
1001
+ {
1002
+ name: "parse",
1003
+ description: "Parse a healthcare message (HL7 v2 or FHIR R4) to typed JSON. Format is autodetected by content. Returns the parsed model plus value-free warnings.",
1004
+ inputSchema: {
1005
+ type: "object",
1006
+ properties: { ...CONTENT_PROP, ...FORMAT_PROP },
1007
+ required: ["content"],
1008
+ additionalProperties: false
1009
+ }
1010
+ },
1011
+ {
1012
+ name: "validate",
1013
+ description: "Validate a message and carry the verdict: ok=true with a valid result, or a result reporting value-free findings. The parsed-but-invalid verdict is a successful call (not a tool error).",
1014
+ inputSchema: {
1015
+ type: "object",
1016
+ properties: { ...CONTENT_PROP, ...FORMAT_PROP },
1017
+ required: ["content"],
1018
+ additionalProperties: false
1019
+ }
1020
+ },
1021
+ {
1022
+ name: "inspect",
1023
+ description: "Return a value-free structural summary of a message: its type, segment/entry counts, and a warning/issue count. Never includes a field value.",
1024
+ inputSchema: {
1025
+ type: "object",
1026
+ properties: { ...CONTENT_PROP, ...FORMAT_PROP },
1027
+ required: ["content"],
1028
+ additionalProperties: false
1029
+ }
1030
+ },
1031
+ {
1032
+ name: "convert",
1033
+ description: "Convert an HL7 v2 message to a FHIR R4 Bundle via @cosyte/transform. Returns the converted Bundle; an error-severity conversion issue is reported with ok=false.",
1034
+ inputSchema: {
1035
+ type: "object",
1036
+ properties: {
1037
+ ...CONTENT_PROP,
1038
+ to: {
1039
+ type: "string",
1040
+ enum: ["fhir"],
1041
+ description: "The conversion target (only FHIR R4 is supported today)."
1042
+ }
1043
+ },
1044
+ required: ["content"],
1045
+ additionalProperties: false
1046
+ }
1047
+ }
1048
+ ];
1049
+ function inlineDeps(content) {
1050
+ const bytes = new TextEncoder().encode(content);
1051
+ const noFile = new CliError(
1052
+ CLI_CODES.CLI_NO_INPUT,
1053
+ EXIT.NOINPUT,
1054
+ "the MCP tools operate on inline content, not a file path"
1055
+ );
1056
+ return {
1057
+ readStdin: () => Promise.resolve(bytes),
1058
+ // The MCP tools always feed inline content via stdin (`-`); a file read is defensive and unreachable.
1059
+ /* v8 ignore next -- unreachable: no MCP tool passes a file path, only inline `-` stdin content */
1060
+ readFile: () => Promise.reject(noFile)
1061
+ };
1062
+ }
1063
+ function stringArg(args, key) {
1064
+ const v = args[key];
1065
+ return typeof v === "string" ? v : null;
1066
+ }
1067
+ function toToolResult(result) {
1068
+ const hardError = result.stdout === "";
1069
+ const text = hardError ? result.stderr.trim() : result.stdout.trim();
1070
+ return {
1071
+ content: [
1072
+ { type: "text", text: text.length > 0 ? text : `cosyte: exit ${String(result.exit)}` }
1073
+ ],
1074
+ isError: hardError,
1075
+ structuredContent: { exit: result.exit, ok: !hardError }
1076
+ };
1077
+ }
1078
+ function usageError(message) {
1079
+ return {
1080
+ content: [{ type: "text", text: `cosyte: ${CLI_CODES.CLI_USAGE}: ${message}` }],
1081
+ isError: true,
1082
+ structuredContent: { exit: EXIT.USAGE, ok: false }
1083
+ };
1084
+ }
1085
+ function internalError(e) {
1086
+ const err = toCliError(e);
1087
+ return {
1088
+ content: [{ type: "text", text: `cosyte: ${err.code}: ${err.message}` }],
1089
+ isError: true,
1090
+ structuredContent: { exit: err.exit, ok: false }
1091
+ };
1092
+ }
1093
+ async function dispatchTool(name, args, deps) {
1094
+ const content = stringArg(args, "content");
1095
+ if (content === null) {
1096
+ return usageError("missing required 'content' argument (the message text to operate on)");
1097
+ }
1098
+ const runDeps = deps ?? inlineDeps(content);
1099
+ const format = stringArg(args, "format");
1100
+ const fmtFlag = format !== null ? ["--format", format] : [];
1101
+ try {
1102
+ switch (name) {
1103
+ case "parse":
1104
+ return toToolResult(await parseCommand(["-", "--json", ...fmtFlag], runDeps, VALUE_FREE));
1105
+ case "validate":
1106
+ return toToolResult(
1107
+ await validateCommand(["-", "--json", ...fmtFlag], runDeps, VALUE_FREE)
1108
+ );
1109
+ case "inspect":
1110
+ return toToolResult(await inspectCommand(["-", "--json", ...fmtFlag], runDeps, VALUE_FREE));
1111
+ case "convert": {
1112
+ const to = stringArg(args, "to") ?? "fhir";
1113
+ return toToolResult(await convertCommand(["-", "--to", to, "--json"], runDeps, VALUE_FREE));
1114
+ }
1115
+ default:
1116
+ return usageError(`unknown tool '${name}'`);
1117
+ }
1118
+ } catch (e) {
1119
+ return internalError(e);
1120
+ }
1121
+ }
1122
+
1123
+ // src/mcp/server.ts
1124
+ var SERVER_INFO = { name: "cosyte", version: VERSION };
1125
+ function createMcpServer() {
1126
+ const server = new index_js.Server(SERVER_INFO, { capabilities: { tools: {} } });
1127
+ server.setRequestHandler(types_js.ListToolsRequestSchema, () => ({
1128
+ tools: TOOL_DEFS.map((t) => ({
1129
+ name: t.name,
1130
+ description: t.description,
1131
+ inputSchema: {
1132
+ type: t.inputSchema.type,
1133
+ properties: { ...t.inputSchema.properties },
1134
+ ...t.inputSchema.required !== void 0 ? { required: [...t.inputSchema.required] } : {},
1135
+ ...t.inputSchema.additionalProperties !== void 0 ? { additionalProperties: t.inputSchema.additionalProperties } : {}
1136
+ }
1137
+ }))
1138
+ }));
1139
+ server.setRequestHandler(types_js.CallToolRequestSchema, async (request) => {
1140
+ const { name, arguments: args } = request.params;
1141
+ const result = await dispatchTool(name, args ?? {});
1142
+ return {
1143
+ content: result.content.map((c) => ({ type: c.type, text: c.text })),
1144
+ isError: result.isError,
1145
+ structuredContent: { exit: result.structuredContent.exit, ok: result.structuredContent.ok }
1146
+ };
1147
+ });
1148
+ return server;
1149
+ }
1150
+ async function startStdioServer() {
1151
+ const server = createMcpServer();
1152
+ await server.connect(new stdio_js.StdioServerTransport());
1153
+ }
1154
+
1155
+ exports.SERVER_INFO = SERVER_INFO;
1156
+ exports.TOOL_DEFS = TOOL_DEFS;
1157
+ exports.createMcpServer = createMcpServer;
1158
+ exports.dispatchTool = dispatchTool;
1159
+ exports.startStdioServer = startStdioServer;
1160
+ //# sourceMappingURL=mcp.cjs.map
1161
+ //# sourceMappingURL=mcp.cjs.map