@evantahler/mcpx 0.21.3 → 0.21.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evantahler/mcpx",
3
- "version": "0.21.3",
3
+ "version": "0.21.5",
4
4
  "description": "A command-line interface for MCP servers. curl for MCP.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -10,9 +10,20 @@
10
10
  // that environment node_modules exists and onnxruntime-web is reachable
11
11
  // through normal module resolution.
12
12
 
13
+ // The relative `../../node_modules/...` paths only resolve from the local repo
14
+ // layout (and inside `bun build --compile`). When this file is shipped via npm,
15
+ // deps are hoisted, so consumer `tsc` runs hit TS2307. The `ts-ignore` directive
16
+ // below silences that for consumers; we avoid the stricter `expect-error` form
17
+ // because in the local repo the path resolves fine and there would be no error
18
+ // to expect. At runtime the dynamic import in semantic.ts is wrapped in
19
+ // try/catch and falls back to transformers.js's default WASM loader (issue #85).
20
+ // biome-ignore lint/suspicious/noTsIgnore: must stay as ts-ignore per comment above
21
+ // @ts-ignore - dynamic-only import
13
22
  import wasmMjsPath from "../../node_modules/onnxruntime-web/dist/ort-wasm-simd-threaded.asyncify.mjs" with {
14
23
  type: "file",
15
24
  };
25
+ // biome-ignore lint/suspicious/noTsIgnore: must stay as ts-ignore per comment above
26
+ // @ts-ignore - dynamic-only import
16
27
  import wasmBinPath from "../../node_modules/onnxruntime-web/dist/ort-wasm-simd-threaded.asyncify.wasm" with {
17
28
  type: "file",
18
29
  };
@@ -26,7 +26,7 @@ function validateWithSchema(
26
26
 
27
27
  if (!validate) {
28
28
  try {
29
- validate = ajv.compile(schema);
29
+ validate = ajv.compile(normalizeSchema(schema));
30
30
  validatorCache.set(cacheKey, validate);
31
31
  } catch (err) {
32
32
  const msg = err instanceof Error ? err.message : "unknown error";
@@ -60,6 +60,64 @@ export function validateElicitationResponse(
60
60
  return validateWithSchema(`__elicitation__${JSON.stringify(schema)}`, schema, input);
61
61
  }
62
62
 
63
+ type JsonPrimitive = string | number | boolean | null;
64
+
65
+ function isPrimitive(v: unknown): v is JsonPrimitive {
66
+ return v === null || ["string", "number", "boolean"].includes(typeof v);
67
+ }
68
+
69
+ function primitiveJsonType(v: JsonPrimitive): "string" | "number" | "boolean" | "null" {
70
+ if (v === null) return "null";
71
+ if (typeof v === "boolean") return "boolean";
72
+ if (typeof v === "number") return "number";
73
+ return "string";
74
+ }
75
+
76
+ /**
77
+ * Normalize a JSON Schema before handing it to Ajv. Rewrites the malformed
78
+ * shape `{ type: "array", enum: [<primitives>], items: { type: <matching> } }`
79
+ * — published by some real MCP servers — into `{ type: "array", items: { ..., enum: [...] } }`.
80
+ * Without this fix Ajv compares the whole array value against the primitive enum
81
+ * and rejects every input. Returns a deep clone; the input schema is untouched.
82
+ */
83
+ function normalizeSchema(schema: Record<string, unknown>): Record<string, unknown> {
84
+ return walk(schema) as Record<string, unknown>;
85
+ }
86
+
87
+ function walk(node: unknown): unknown {
88
+ if (Array.isArray(node)) {
89
+ return node.map(walk);
90
+ }
91
+ if (!node || typeof node !== "object") {
92
+ return node;
93
+ }
94
+
95
+ const out: Record<string, unknown> = {};
96
+ for (const [key, value] of Object.entries(node as Record<string, unknown>)) {
97
+ out[key] = walk(value);
98
+ }
99
+
100
+ if (out.type === "array" && Array.isArray(out.enum) && out.enum.every(isPrimitive)) {
101
+ const items = out.items;
102
+ const enumValues = out.enum as JsonPrimitive[];
103
+ if (items && typeof items === "object" && !Array.isArray(items)) {
104
+ const itemsObj = items as Record<string, unknown>;
105
+ const enumType = primitiveJsonType(enumValues[0]!);
106
+ const allSameType = enumValues.every((v) => primitiveJsonType(v) === enumType);
107
+ const itemsTypeMatches =
108
+ itemsObj.type === undefined ||
109
+ itemsObj.type === enumType ||
110
+ (Array.isArray(itemsObj.type) && itemsObj.type.includes(enumType));
111
+ if (allSameType && itemsTypeMatches && itemsObj.enum === undefined) {
112
+ out.items = { ...itemsObj, enum: enumValues };
113
+ delete out.enum;
114
+ }
115
+ }
116
+ }
117
+
118
+ return out;
119
+ }
120
+
63
121
  function formatAjvError(err: ErrorObject): ValidationError {
64
122
  const path = err.instancePath ? err.instancePath.replace(/^\//, "").replace(/\//g, ".") : "(root)";
65
123