stitchkit 0.34.0 → 0.35.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.
- package/dist/cli.js +1 -1
- package/dist/{index-jr8vf7g8.js → index-bfabej0h.js} +101 -14
- package/dist/tools/flatten.d.ts.map +1 -1
- package/dist/tools/mcp.d.ts +9 -0
- package/dist/tools/mcp.d.ts.map +1 -1
- package/dist/tools/untyped-properties.d.ts +17 -0
- package/dist/tools/untyped-properties.d.ts.map +1 -0
- package/dist/tools.d.ts +1 -0
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +85 -3
- package/llms-full.txt +35 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -229,30 +229,97 @@ function stripAnnotations(node) {
|
|
|
229
229
|
stripAnnotations(value);
|
|
230
230
|
}
|
|
231
231
|
}
|
|
232
|
-
function
|
|
232
|
+
function hasInvisibleConstraint(schema) {
|
|
233
233
|
if (!(schema instanceof z3.ZodType))
|
|
234
234
|
return false;
|
|
235
235
|
const def = schema.def;
|
|
236
|
-
if (isRecord(def) &&
|
|
236
|
+
if (isRecord(def) && def.type !== "unknown" && def.type !== "any") {
|
|
237
|
+
if (normalizedJson(schema) === "{}")
|
|
238
|
+
return true;
|
|
239
|
+
}
|
|
240
|
+
if (schema instanceof z3.ZodCatch)
|
|
241
|
+
return true;
|
|
242
|
+
if (isRecord(def) && def.coerce === true)
|
|
237
243
|
return true;
|
|
244
|
+
if (isRecord(def) && Array.isArray(def.checks)) {
|
|
245
|
+
for (const check of def.checks) {
|
|
246
|
+
if (isInvisibleCheck(check))
|
|
247
|
+
return true;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
238
250
|
if (schema instanceof z3.ZodOptional || schema instanceof z3.ZodNullable || schema instanceof z3.ZodDefault) {
|
|
239
|
-
return
|
|
251
|
+
return hasInvisibleConstraint(schema.unwrap());
|
|
240
252
|
}
|
|
241
253
|
if (schema instanceof z3.ZodPipe)
|
|
242
|
-
return
|
|
243
|
-
if (schema instanceof z3.ZodObject)
|
|
244
|
-
return Object.values(schema.shape).some(
|
|
254
|
+
return true;
|
|
255
|
+
if (schema instanceof z3.ZodObject) {
|
|
256
|
+
return Object.values(schema.shape).some(hasInvisibleConstraint);
|
|
257
|
+
}
|
|
245
258
|
if (schema instanceof z3.ZodArray)
|
|
246
|
-
return
|
|
259
|
+
return hasInvisibleConstraint(schema.element);
|
|
247
260
|
if (schema instanceof z3.ZodUnion)
|
|
248
|
-
return schema.def.options.some(
|
|
261
|
+
return schema.def.options.some(hasInvisibleConstraint);
|
|
249
262
|
if (schema instanceof z3.ZodRecord)
|
|
250
|
-
return
|
|
263
|
+
return hasInvisibleConstraint(schema.valueType);
|
|
251
264
|
if (schema instanceof z3.ZodIntersection) {
|
|
252
|
-
return
|
|
265
|
+
return hasInvisibleConstraint(schema.def.left) || hasInvisibleConstraint(schema.def.right);
|
|
253
266
|
}
|
|
254
267
|
return false;
|
|
255
268
|
}
|
|
269
|
+
var INVISIBLE_CHECK_KINDS = new Set(["custom", "overwrite"]);
|
|
270
|
+
function acceptsMoreThanItsType(schema) {
|
|
271
|
+
if (!(schema instanceof z3.ZodType))
|
|
272
|
+
return false;
|
|
273
|
+
if (schema instanceof z3.ZodCatch)
|
|
274
|
+
return true;
|
|
275
|
+
if (isRecord(schema.def) && schema.def.coerce === true)
|
|
276
|
+
return true;
|
|
277
|
+
if (schema instanceof z3.ZodOptional || schema instanceof z3.ZodNullable || schema instanceof z3.ZodDefault) {
|
|
278
|
+
return acceptsMoreThanItsType(schema.unwrap());
|
|
279
|
+
}
|
|
280
|
+
if (schema instanceof z3.ZodPipe)
|
|
281
|
+
return acceptsMoreThanItsType(schema.def.in);
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
function isInvisibleCheck(check) {
|
|
285
|
+
if (!isRecord(check))
|
|
286
|
+
return false;
|
|
287
|
+
const inner = check._zod;
|
|
288
|
+
const def = isRecord(inner) ? inner.def : undefined;
|
|
289
|
+
const kind = isRecord(def) ? def.check : undefined;
|
|
290
|
+
return typeof kind === "string" && INVISIBLE_CHECK_KINDS.has(kind);
|
|
291
|
+
}
|
|
292
|
+
function projectToBaseType(schema) {
|
|
293
|
+
if (!(schema instanceof z3.ZodType))
|
|
294
|
+
return z3.unknown();
|
|
295
|
+
if (schema instanceof z3.ZodNullable) {
|
|
296
|
+
return z3.nullable(projectToBaseType(schema.unwrap()));
|
|
297
|
+
}
|
|
298
|
+
if (schema instanceof z3.ZodOptional || schema instanceof z3.ZodDefault) {
|
|
299
|
+
return projectToBaseType(schema.unwrap());
|
|
300
|
+
}
|
|
301
|
+
if (schema instanceof z3.ZodPipe)
|
|
302
|
+
return projectToBaseType(schema.def.in);
|
|
303
|
+
if (schema instanceof z3.ZodCatch)
|
|
304
|
+
return z3.unknown();
|
|
305
|
+
const coerces = isRecord(schema.def) && schema.def.coerce === true;
|
|
306
|
+
if (schema instanceof z3.ZodNumber)
|
|
307
|
+
return coerces ? z3.coerce.number() : z3.number();
|
|
308
|
+
if (schema instanceof z3.ZodString)
|
|
309
|
+
return coerces ? z3.coerce.string() : z3.string();
|
|
310
|
+
if (schema instanceof z3.ZodBoolean && coerces)
|
|
311
|
+
return z3.coerce.boolean();
|
|
312
|
+
if (schema instanceof z3.ZodEnum || schema instanceof z3.ZodLiteral) {
|
|
313
|
+
return stringLiteralOrEnumValues(schema) === null ? z3.unknown() : z3.string();
|
|
314
|
+
}
|
|
315
|
+
if (schema instanceof z3.ZodBoolean)
|
|
316
|
+
return z3.boolean();
|
|
317
|
+
if (schema instanceof z3.ZodArray)
|
|
318
|
+
return z3.array(z3.unknown());
|
|
319
|
+
if (schema instanceof z3.ZodObject)
|
|
320
|
+
return z3.looseObject({});
|
|
321
|
+
return z3.unknown();
|
|
322
|
+
}
|
|
256
323
|
function normalizedJson(schema) {
|
|
257
324
|
if (!(schema instanceof z3.ZodType))
|
|
258
325
|
return "{}";
|
|
@@ -272,7 +339,17 @@ function mergeCollidingFields(schemas) {
|
|
|
272
339
|
if (values.length <= 1) {
|
|
273
340
|
if (only === undefined)
|
|
274
341
|
return z3.unknown();
|
|
275
|
-
|
|
342
|
+
if (schemas.length <= 1)
|
|
343
|
+
return only;
|
|
344
|
+
const hazards2 = schemas.map(acceptsMoreThanItsType);
|
|
345
|
+
if (hazards2.some(Boolean) && !hazards2.every(Boolean))
|
|
346
|
+
return z3.unknown();
|
|
347
|
+
if (schemas.some(hasInvisibleConstraint)) {
|
|
348
|
+
if (normalizedJson(only) === "{}")
|
|
349
|
+
return only;
|
|
350
|
+
return projectToBaseType(only);
|
|
351
|
+
}
|
|
352
|
+
return only;
|
|
276
353
|
}
|
|
277
354
|
const merged = [];
|
|
278
355
|
let allEnum = true;
|
|
@@ -286,9 +363,19 @@ function mergeCollidingFields(schemas) {
|
|
|
286
363
|
}
|
|
287
364
|
if (allEnum) {
|
|
288
365
|
const uniq = [...new Set(merged)];
|
|
289
|
-
const [
|
|
290
|
-
if (
|
|
291
|
-
return z3.enum([
|
|
366
|
+
const [first2, ...rest] = uniq;
|
|
367
|
+
if (first2 !== undefined)
|
|
368
|
+
return z3.enum([first2, ...rest]);
|
|
369
|
+
}
|
|
370
|
+
const hazards = schemas.map(acceptsMoreThanItsType);
|
|
371
|
+
if (hazards.some(Boolean) && !hazards.every(Boolean))
|
|
372
|
+
return z3.unknown();
|
|
373
|
+
const projected = values.map(projectToBaseType);
|
|
374
|
+
const first = projected[0];
|
|
375
|
+
if (first !== undefined) {
|
|
376
|
+
const shape = normalizedJson(first);
|
|
377
|
+
if (shape !== "{}" && projected.every((p) => normalizedJson(p) === shape))
|
|
378
|
+
return first;
|
|
292
379
|
}
|
|
293
380
|
return z3.unknown();
|
|
294
381
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flatten.d.ts","sourceRoot":"","sources":["../../src/tools/flatten.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,CAAC,CAAC,qBAAqB,GAC7B,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAiD5B;
|
|
1
|
+
{"version":3,"file":"flatten.d.ts","sourceRoot":"","sources":["../../src/tools/flatten.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,CAAC,CAAC,qBAAqB,GAC7B,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAiD5B;AA6TD;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,OAAO,CAqDpE"}
|
package/dist/tools/mcp.d.ts
CHANGED
|
@@ -48,6 +48,15 @@ export interface McpMountConfig {
|
|
|
48
48
|
export declare function validateMcpSchemas(services: ServiceDef[], onIncompatibleSchema?: IncompatibleSchemaPolicy, logger?: StitchLogger, options?: {
|
|
49
49
|
extend?: ToolExtend;
|
|
50
50
|
flattenUnionInput?: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Also fail a tool whose advertised schema has a property with no type
|
|
53
|
+
* information — nothing for a model to obey. Off by default because a
|
|
54
|
+
* contract may legitimately declare `z.unknown()`; `allowUntyped` lists the
|
|
55
|
+
* dotted paths that are deliberate, and anything else is a finding. → ADR 0044.
|
|
56
|
+
*/
|
|
57
|
+
requireTypedProperties?: boolean;
|
|
58
|
+
/** Dotted paths (`tool.property`) that are deliberately unconstrained. */
|
|
59
|
+
allowUntyped?: readonly string[];
|
|
51
60
|
}): void;
|
|
52
61
|
export declare function mountMcp(mcpServer: McpServer, services: ServiceDef | ServiceDef[], config?: McpMountConfig): void;
|
|
53
62
|
/**
|
package/dist/tools/mcp.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../../src/tools/mcp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIpE,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAChE,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,aAAa,EAGnB,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,KAAK,cAAc,EAAsB,MAAM,WAAW,CAAC;AACpE,OAAO,EAKL,KAAK,UAAU,EAChB,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../../src/tools/mcp.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** One property the model would be shown with no idea what it is. */
|
|
2
|
+
export interface UntypedProperty {
|
|
3
|
+
/** Dotted path from the tool's argument root, e.g. `operations.partIndex`. */
|
|
4
|
+
path: string;
|
|
5
|
+
/** Its `description`, when it has one — usually the only clue present. */
|
|
6
|
+
description?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Every property in a JSON Schema that carries no type information, deep.
|
|
10
|
+
*
|
|
11
|
+
* Walks `properties`, `items`, `additionalProperties`, `$defs` / `definitions`
|
|
12
|
+
* and the `allOf` / `anyOf` / `oneOf` branches — an intersection root emits
|
|
13
|
+
* `allOf` with no root `properties` at all, so reading the top level alone would
|
|
14
|
+
* inspect nothing and pass.
|
|
15
|
+
*/
|
|
16
|
+
export declare function findUntypedProperties(schema: unknown, prefix?: string): UntypedProperty[];
|
|
17
|
+
//# sourceMappingURL=untyped-properties.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"untyped-properties.d.ts","sourceRoot":"","sources":["../../src/tools/untyped-properties.ts"],"names":[],"mappings":"AAkBA,qEAAqE;AACrE,MAAM,WAAW,eAAe;IAC9B,8EAA8E;IAC9E,IAAI,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AASD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,SAAK,GAAG,eAAe,EAAE,CA+DrF"}
|
package/dist/tools.d.ts
CHANGED
|
@@ -22,5 +22,6 @@ export { type ImplementRemoteOptions, implementRemote } from './tools/remote';
|
|
|
22
22
|
export { createToolLogger, type ToolCallRecord, type ToolLoggerConfig, } from './tools/tool-logger';
|
|
23
23
|
export { createToolkit, type Toolkit } from './tools/toolkit';
|
|
24
24
|
export { summarizeTransports, type TransportCounts, type TransportSummary, } from './tools/transports';
|
|
25
|
+
export { findUntypedProperties, type UntypedProperty } from './tools/untyped-properties';
|
|
25
26
|
export { type McpAnnotations, type McpMediaContent, mountViewFile, resolveMedia, type ViewFileOptions, } from './tools/view-file';
|
|
26
27
|
//# sourceMappingURL=tools.d.ts.map
|
package/dist/tools.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,gBAAgB,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AACrF,OAAO,EAAE,KAAK,SAAS,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxD,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,YAAY,EACV,WAAW,EACX,eAAe,EACf,aAAa,EACb,aAAa,EACb,UAAU,GACX,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,yBAAyB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAC/E,OAAO,EAAE,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC7E,OAAO,EACL,cAAc,EACd,KAAK,wBAAwB,EAC7B,KAAK,cAAc,EACnB,KAAK,oBAAoB,EACzB,QAAQ,EACR,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,2BAA2B,EAC3B,kBAAkB,EAClB,KAAK,SAAS,EACd,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACnB,kBAAkB,GACnB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,gBAAgB,EAAE,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC9E,OAAO,EAAE,oBAAoB,EAAE,KAAK,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACpF,OAAO,EACL,KAAK,kBAAkB,EACvB,YAAY,EACZ,KAAK,aAAa,EAClB,KAAK,UAAU,GAChB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,KAAK,kBAAkB,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAChF,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC1E,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpE,OAAO,EACL,2BAA2B,EAC3B,uBAAuB,EACvB,KAAK,uBAAuB,EAC5B,4BAA4B,EAC5B,qBAAqB,GACtB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,kBAAkB,EAClB,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAChB,KAAK,gBAAgB,GACtB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,KAAK,sBAAsB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC9E,OAAO,EACL,gBAAgB,EAChB,KAAK,cAAc,EACnB,KAAK,gBAAgB,GACtB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,KAAK,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC9D,OAAO,EACL,mBAAmB,EACnB,KAAK,eAAe,EACpB,KAAK,gBAAgB,GACtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,aAAa,EACb,YAAY,EACZ,KAAK,eAAe,GACrB,MAAM,mBAAmB,CAAC"}
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,gBAAgB,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AACrF,OAAO,EAAE,KAAK,SAAS,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxD,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,YAAY,EACV,WAAW,EACX,eAAe,EACf,aAAa,EACb,aAAa,EACb,UAAU,GACX,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,yBAAyB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAC/E,OAAO,EAAE,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC7E,OAAO,EACL,cAAc,EACd,KAAK,wBAAwB,EAC7B,KAAK,cAAc,EACnB,KAAK,oBAAoB,EACzB,QAAQ,EACR,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,2BAA2B,EAC3B,kBAAkB,EAClB,KAAK,SAAS,EACd,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACnB,kBAAkB,GACnB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,gBAAgB,EAAE,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC9E,OAAO,EAAE,oBAAoB,EAAE,KAAK,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACpF,OAAO,EACL,KAAK,kBAAkB,EACvB,YAAY,EACZ,KAAK,aAAa,EAClB,KAAK,UAAU,GAChB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,KAAK,kBAAkB,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAChF,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC1E,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpE,OAAO,EACL,2BAA2B,EAC3B,uBAAuB,EACvB,KAAK,uBAAuB,EAC5B,4BAA4B,EAC5B,qBAAqB,GACtB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,kBAAkB,EAClB,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAChB,KAAK,gBAAgB,GACtB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,KAAK,sBAAsB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC9E,OAAO,EACL,gBAAgB,EAChB,KAAK,cAAc,EACnB,KAAK,gBAAgB,GACtB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,KAAK,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC9D,OAAO,EACL,mBAAmB,EACnB,KAAK,eAAe,EACpB,KAAK,gBAAgB,GACtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,qBAAqB,EAAE,KAAK,eAAe,EAAE,MAAM,4BAA4B,CAAC;AACzF,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,aAAa,EACb,YAAY,EACZ,KAAK,eAAe,GACrB,MAAM,mBAAmB,CAAC"}
|
package/dist/tools.js
CHANGED
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
readCapped,
|
|
22
22
|
toolResultFromError,
|
|
23
23
|
writeDownload
|
|
24
|
-
} from "./index-
|
|
24
|
+
} from "./index-bfabej0h.js";
|
|
25
25
|
import {
|
|
26
26
|
toJsonSchema
|
|
27
27
|
} from "./index-0ed3bx43.js";
|
|
@@ -139,6 +139,77 @@ function inlineMcpAppBundle(html) {
|
|
|
139
139
|
return html.replace(EXT_APPS_BUNDLE_PLACEHOLDER, () => bundle);
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
+
// src/tools/untyped-properties.ts
|
|
143
|
+
var TYPE_KEYWORDS = ["type", "enum", "const", "anyOf", "oneOf", "allOf", "$ref", "not"];
|
|
144
|
+
function saysWhatItIs(schema) {
|
|
145
|
+
return TYPE_KEYWORDS.some((keyword) => schema[keyword] !== undefined);
|
|
146
|
+
}
|
|
147
|
+
function findUntypedProperties(schema, prefix = "") {
|
|
148
|
+
if (!isRecord(schema))
|
|
149
|
+
return [];
|
|
150
|
+
const found = [];
|
|
151
|
+
const properties = schema.properties;
|
|
152
|
+
if (isRecord(properties)) {
|
|
153
|
+
for (const [key, value] of Object.entries(properties)) {
|
|
154
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
155
|
+
if (isRecord(value)) {
|
|
156
|
+
if (!saysWhatItIs(value)) {
|
|
157
|
+
const description = value.description;
|
|
158
|
+
found.push({
|
|
159
|
+
path,
|
|
160
|
+
...typeof description === "string" && { description }
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
found.push(...findUntypedProperties(value, path));
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
for (const key of ["items", "additionalProperties"]) {
|
|
168
|
+
const child = schema[key];
|
|
169
|
+
if (isRecord(child))
|
|
170
|
+
found.push(...findUntypedProperties(child, prefix));
|
|
171
|
+
if (Array.isArray(child)) {
|
|
172
|
+
for (const entry of child) {
|
|
173
|
+
if (isRecord(entry))
|
|
174
|
+
found.push(...findUntypedProperties(entry, prefix));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
for (const key of ["prefixItems"]) {
|
|
179
|
+
const child = schema[key];
|
|
180
|
+
if (Array.isArray(child)) {
|
|
181
|
+
for (const entry of child) {
|
|
182
|
+
if (isRecord(entry))
|
|
183
|
+
found.push(...findUntypedProperties(entry, prefix));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
const patterned = schema.patternProperties;
|
|
188
|
+
if (isRecord(patterned)) {
|
|
189
|
+
for (const value of Object.values(patterned)) {
|
|
190
|
+
if (isRecord(value))
|
|
191
|
+
found.push(...findUntypedProperties(value, prefix));
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
for (const key of ["$defs", "definitions"]) {
|
|
195
|
+
const container = schema[key];
|
|
196
|
+
if (!isRecord(container))
|
|
197
|
+
continue;
|
|
198
|
+
for (const definition of Object.values(container)) {
|
|
199
|
+
if (isRecord(definition))
|
|
200
|
+
found.push(...findUntypedProperties(definition, prefix));
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
for (const key of ["allOf", "anyOf", "oneOf"]) {
|
|
204
|
+
const branches = schema[key];
|
|
205
|
+
if (Array.isArray(branches)) {
|
|
206
|
+
for (const branch of branches)
|
|
207
|
+
found.push(...findUntypedProperties(branch, prefix));
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return found;
|
|
211
|
+
}
|
|
212
|
+
|
|
142
213
|
// src/tools/mcp.ts
|
|
143
214
|
function textBlock(text) {
|
|
144
215
|
return [{ type: "text", text }];
|
|
@@ -187,7 +258,7 @@ function reportIncompatible(message, policy, logger, failures) {
|
|
|
187
258
|
}
|
|
188
259
|
function throwIfFailures(failures) {
|
|
189
260
|
if (failures.length > 0) {
|
|
190
|
-
throw new Error(`[stitchkit] ${failures.length}
|
|
261
|
+
throw new Error(`[stitchkit] ${failures.length} problem(s) with MCP tool schemas:
|
|
191
262
|
- ${failures.join(`
|
|
192
263
|
- `)}`);
|
|
193
264
|
}
|
|
@@ -217,9 +288,19 @@ function prepareMcpTool(mountable, policy, logger, failures, seen) {
|
|
|
217
288
|
function validateMcpSchemas(services, onIncompatibleSchema = "throw", logger, options) {
|
|
218
289
|
const seen = new Set;
|
|
219
290
|
const failures = [];
|
|
291
|
+
const allowed = new Set(options?.allowUntyped ?? []);
|
|
220
292
|
for (const service of services) {
|
|
221
293
|
for (const mountable of collectTools(service, "MCP", options)) {
|
|
222
|
-
prepareMcpTool(mountable, onIncompatibleSchema, logger, failures, seen);
|
|
294
|
+
const prepared = prepareMcpTool(mountable, onIncompatibleSchema, logger, failures, seen);
|
|
295
|
+
if (!prepared || !options?.requireTypedProperties)
|
|
296
|
+
continue;
|
|
297
|
+
for (const untyped of findUntypedProperties(toJsonSchema(mountable.schema, "input"))) {
|
|
298
|
+
const path = `${mountable.name}.${untyped.path}`;
|
|
299
|
+
if (allowed.has(path))
|
|
300
|
+
continue;
|
|
301
|
+
const clue = untyped.description ? ` (only a description: "${untyped.description}")` : "";
|
|
302
|
+
reportIncompatible(`MCP tool "${mountable.name}" — property "${untyped.path}" carries no type, enum or $ref${clue}. ` + "A model is given no way to know what to send. Widen the contract, or list it in `allowUntyped` if it is deliberately free-form.", onIncompatibleSchema === "skip" ? "warn" : onIncompatibleSchema, logger, failures);
|
|
303
|
+
}
|
|
223
304
|
}
|
|
224
305
|
}
|
|
225
306
|
throwIfFailures(failures);
|
|
@@ -1410,6 +1491,7 @@ export {
|
|
|
1410
1491
|
implementRemote,
|
|
1411
1492
|
flattenUnionsDeep,
|
|
1412
1493
|
flattenDiscriminatedUnion,
|
|
1494
|
+
findUntypedProperties,
|
|
1413
1495
|
createToolkit,
|
|
1414
1496
|
createToolLogger,
|
|
1415
1497
|
createStdioMcpServer,
|
package/llms-full.txt
CHANGED
|
@@ -1524,6 +1524,30 @@ cannot become a tool. `onIncompatibleSchema` decides what happens:
|
|
|
1524
1524
|
`validateMcpSchemas(services)` runs the same check on its own — useful in a
|
|
1525
1525
|
startup assertion or a test.
|
|
1526
1526
|
|
|
1527
|
+
#### Is every property actually usable by a model?
|
|
1528
|
+
|
|
1529
|
+
A tool schema is the only instruction a model gets about the shape of its
|
|
1530
|
+
arguments. A property that carries a `description` and no `type` / `enum` /
|
|
1531
|
+
`anyOf` / `$ref` tells it nothing — and nothing fails: the schema converts, the
|
|
1532
|
+
mount succeeds, the tool is advertised, and the model then guesses, retrying the
|
|
1533
|
+
same wrong guess because the error does not say what the right one would be.
|
|
1534
|
+
|
|
1535
|
+
```ts
|
|
1536
|
+
validateMcpSchemas(services, 'throw', logger, {
|
|
1537
|
+
flattenUnionInput: true, // mirror the live mount
|
|
1538
|
+
requireTypedProperties: true,
|
|
1539
|
+
allowUntyped: ['docs_create.payload'], // deliberately free-form
|
|
1540
|
+
})
|
|
1541
|
+
```
|
|
1542
|
+
|
|
1543
|
+
Off by default, because a contract may legitimately declare `z.unknown()`.
|
|
1544
|
+
`allowUntyped` takes dotted `tool.property` paths — an entry there is a decision,
|
|
1545
|
+
anything else is a finding. Pass the **same** `extend` / `flattenUnionInput` the
|
|
1546
|
+
mount uses, or the check vets a different document than the one advertised.
|
|
1547
|
+
|
|
1548
|
+
`findUntypedProperties(jsonSchema)` is the same walk, exported on its own if you
|
|
1549
|
+
want to assert on a schema you built elsewhere.
|
|
1550
|
+
|
|
1527
1551
|
### Discriminated unions for weaker models — `flattenUnionInput`
|
|
1528
1552
|
|
|
1529
1553
|
A discriminated union in a tool's input becomes a JSON Schema `oneOf` / `anyOf`.
|
|
@@ -1533,6 +1557,15 @@ the field or mangle its strings. Set `flattenUnionInput: true` (on
|
|
|
1533
1557
|
union as a **single flat object** instead — the discriminator becomes an enum and
|
|
1534
1558
|
each variant's fields become optional with a `Required if <disc> = …` hint.
|
|
1535
1559
|
|
|
1560
|
+
**A field several variants declare keeps its type.** Flattening puts every
|
|
1561
|
+
variant's fields side by side, so a key two variants share has to be advertised
|
|
1562
|
+
as one thing. Where they agree, that is what you get; where they disagree — a
|
|
1563
|
+
`.refine()` only one of them carries, two different bounds on the same number, an
|
|
1564
|
+
enum against a free string — the *constraint* is dropped and the **type** is not.
|
|
1565
|
+
A field that is a number in every variant is advertised as a number, not as a
|
|
1566
|
+
bare description. Only genuinely different kinds (a string in one variant, a
|
|
1567
|
+
number in another) fall back to unconstrained. → ADR 0044
|
|
1568
|
+
|
|
1536
1569
|
It is **deep**: unions are flattened at every depth — top level, object fields,
|
|
1537
1570
|
array items, and through `optional` / `nullable` / `default` / intersection
|
|
1538
1571
|
wrappers — so no `oneOf` survives anywhere (e.g. a `content.parts[]` that is an
|
|
@@ -3921,6 +3954,8 @@ Server-only. Turns contracts into MCP and AI-agent tools. Needs the
|
|
|
3921
3954
|
| `ViewFileOptions` | _type_ | options for `mountViewFile` |
|
|
3922
3955
|
| `McpAnnotations` | _type_ | MCP annotations on a media result |
|
|
3923
3956
|
| `CollectToolsConfig` | _type_ | options for `collectTools` |
|
|
3957
|
+
| `findUntypedProperties` | function | every property in a JSON Schema with no `type`/`enum`/`$ref` — what a model is shown and cannot obey ([guide](../guide/mcp-and-agents.md)) |
|
|
3958
|
+
| `UntypedProperty` | _type_ | one such property — `{ path, description? }` |
|
|
3924
3959
|
| `ToolNameEntry` | _type_ | one `listToolNames` row — `{ name, service, method, transports }` |
|
|
3925
3960
|
| `IncompatibleSchemaPolicy` | _type_ | `'throw' \| 'skip' \| 'warn'` |
|
|
3926
3961
|
| `McpMediaContent` | _type_ | a multimodal MCP content item |
|
package/package.json
CHANGED