@zackbart/connecta 0.14.2 → 0.15.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/validate.js CHANGED
@@ -7,6 +7,17 @@ import { MAX_ARGUMENT_VALIDATION_ISSUES } from "./errors.js";
7
7
  // connector are collectable, the same pattern compactSchema uses.
8
8
  const validators = new WeakMap();
9
9
  const REQUIRED_PROPERTY_RE = /^Instance does not have required property "([^"]+)"\.$/;
10
+ const CONTAINER_VALIDATION_KEYWORDS = new Set([
11
+ "properties",
12
+ "items",
13
+ "allOf",
14
+ "anyOf",
15
+ "oneOf",
16
+ "if",
17
+ "not",
18
+ "patternProperties",
19
+ "additionalProperties",
20
+ ]);
10
21
  function decodePointerPart(value) {
11
22
  return value.replaceAll("~1", "/").replaceAll("~0", "~");
12
23
  }
@@ -31,6 +42,85 @@ function argumentPath(location) {
31
42
  return "/";
32
43
  return location.startsWith("#") ? location.slice(1) || "/" : "/";
33
44
  }
45
+ function validationUnitKey(unit) {
46
+ return JSON.stringify([
47
+ unit.keyword,
48
+ unit.keywordLocation,
49
+ unit.instanceLocation,
50
+ unit.error,
51
+ ]);
52
+ }
53
+ function childPropertyName(parentLocation, childLocation) {
54
+ const prefix = parentLocation === "#" ? "#/" : `${parentLocation}/`;
55
+ if (!childLocation.startsWith(prefix))
56
+ return undefined;
57
+ const encoded = childLocation.slice(prefix.length);
58
+ return !encoded.includes("/") ? decodePointerPart(encoded) : undefined;
59
+ }
60
+ function schemaDeclaresProperty(schema, property) {
61
+ if (schema === null || typeof schema !== "object" || Array.isArray(schema)) {
62
+ return false;
63
+ }
64
+ const record = schema;
65
+ const properties = record.properties;
66
+ if (properties !== null &&
67
+ typeof properties === "object" &&
68
+ !Array.isArray(properties) &&
69
+ Object.hasOwn(properties, property)) {
70
+ return true;
71
+ }
72
+ const patterns = record.patternProperties;
73
+ if (patterns === null ||
74
+ typeof patterns !== "object" ||
75
+ Array.isArray(patterns)) {
76
+ return false;
77
+ }
78
+ for (const pattern of Object.keys(patterns)) {
79
+ try {
80
+ if (new RegExp(pattern).test(property))
81
+ return true;
82
+ }
83
+ catch {
84
+ // The validator owns schema support. An unusable pattern cannot prove
85
+ // that this additional-properties branch is a duplicate.
86
+ }
87
+ }
88
+ return false;
89
+ }
90
+ function isDuplicateAdditionalPropertiesBranch(schema, units, index) {
91
+ const unit = units[index];
92
+ const wrapper = units[index - 1];
93
+ if (unit?.keyword !== "false" ||
94
+ wrapper?.keyword !== "additionalProperties" ||
95
+ !wrapper.keywordLocation.endsWith("/additionalProperties")) {
96
+ return false;
97
+ }
98
+ const property = childPropertyName(wrapper.instanceLocation, unit.instanceLocation);
99
+ if (property === undefined)
100
+ return false;
101
+ const parentSchemaLocation = wrapper.keywordLocation.slice(0, -"/additionalProperties".length);
102
+ return schemaDeclaresProperty(pointerValue(schema, parentSchemaLocation || "#"), property);
103
+ }
104
+ function normalizedValidationUnits(schema, units) {
105
+ const seen = new Set();
106
+ return units.filter((unit, index) => {
107
+ if (CONTAINER_VALIDATION_KEYWORDS.has(unit.keyword))
108
+ return false;
109
+ if (isDuplicateAdditionalPropertiesBranch(schema, units, index)) {
110
+ return false;
111
+ }
112
+ const key = validationUnitKey(unit);
113
+ if (seen.has(key))
114
+ return false;
115
+ seen.add(key);
116
+ return true;
117
+ });
118
+ }
119
+ function agentFacingValidationError(unit) {
120
+ return unit.keyword === "false"
121
+ ? "Value is not allowed by the declared schema."
122
+ : unit.error;
123
+ }
34
124
  function expectedType(schema, unit) {
35
125
  if (unit.keyword === "type") {
36
126
  const value = pointerValue(schema, unit.keywordLocation);
@@ -66,19 +156,8 @@ function expectedType(schema, unit) {
66
156
  return fixed[unit.keyword];
67
157
  }
68
158
  function validationDetails(schema, units) {
69
- const leafUnits = units.filter((unit) => ![
70
- "properties",
71
- "items",
72
- "allOf",
73
- "anyOf",
74
- "oneOf",
75
- "if",
76
- "not",
77
- "patternProperties",
78
- "additionalProperties",
79
- ].includes(unit.keyword));
80
159
  const issues = [];
81
- for (const unit of leafUnits) {
160
+ for (const unit of units) {
82
161
  const missing = unit.keyword === "required"
83
162
  ? REQUIRED_PROPERTY_RE.exec(unit.error)?.[1]
84
163
  : undefined;
@@ -164,12 +243,13 @@ export function validateToolInput(schema, args, opts) {
164
243
  return opts.failClosed ? unevaluableSchema(opts.address) : null;
165
244
  }
166
245
  if (result && !result.valid) {
167
- const units = result.errors.filter((u) => u.instanceLocation !== "#");
168
- const detail = (units.length > 0 ? units : result.errors)
169
- .slice(0, 3)
170
- .map((u) => `${u.instanceLocation}: ${u.error}`)
246
+ const units = normalizedValidationUnits(schema, result.errors);
247
+ const nestedUnits = units.filter((unit) => unit.instanceLocation !== "#");
248
+ const detail = (nestedUnits.length > 0 ? nestedUnits : units)
249
+ .slice(0, MAX_ARGUMENT_VALIDATION_ISSUES)
250
+ .map((unit) => `${unit.instanceLocation}: ${agentFacingValidationError(unit)}`)
171
251
  .join("; ");
172
- return new ConnectorCallError("invalid_args", `Invalid arguments for "${opts.address}": ${detail || "input does not match the tool's inputSchema"}`, { validation: validationDetails(schema, result.errors) });
252
+ return new ConnectorCallError("invalid_args", `Invalid arguments for "${opts.address}": ${detail || "input does not match the tool's inputSchema"}`, { validation: validationDetails(schema, units) });
173
253
  }
174
254
  return null;
175
255
  }
@@ -1 +1 @@
1
- {"version":3,"file":"validate.js","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAKjD,OAAO,EAAE,8BAA8B,EAAE,MAAM,aAAa,CAAC;AAsC7D,8EAA8E;AAC9E,uEAAuE;AACvE,0EAA0E;AAC1E,kEAAkE;AAClE,MAAM,UAAU,GAAG,IAAI,OAAO,EAAgC,CAAC;AAC/D,MAAM,oBAAoB,GACxB,wDAAwD,CAAC;AAS3D,SAAS,iBAAiB,CAAC,KAAa;IACtC,OAAO,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;AAC3D,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAa;IACtC,OAAO,KAAK,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;AAC3D,CAAC;AAED,SAAS,YAAY,CAAC,KAAc,EAAE,OAAe;IACnD,IAAI,OAAO,KAAK,GAAG;QAAE,OAAO,KAAK,CAAC;IAClC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IAChD,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,iBAAiB,CAAC,EAAE,CAAC;QACtE,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,OAAO,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAC;QACtE,OAAO,GAAI,OAAmC,CAAC,IAAI,CAAC,CAAC;IACvD,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,YAAY,CAAC,QAAgB;IACpC,IAAI,QAAQ,KAAK,GAAG;QAAE,OAAO,GAAG,CAAC;IACjC,OAAO,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;AACnE,CAAC;AAED,SAAS,YAAY,CAAC,MAAkB,EAAE,IAAoB;IAC5D,IAAI,IAAI,CAAC,OAAO,KAAK,MAAM,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;QACzD,IAAI,OAAO,KAAK,KAAK,QAAQ;YAAE,OAAO,KAAK,CAAC;QAC5C,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,EAAE,CAAC;YAC5E,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC;IACD,IAAI,IAAI,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;QAChC,MAAM,OAAO,GAAG,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC3D,IAAI,CAAC,OAAO;YAAE,OAAO,SAAS,CAAC;QAC/B,MAAM,cAAc,GAAG,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;QACvE,MAAM,KAAK,GAAG,YAAY,CACxB,MAAM,EACN,GAAG,cAAc,eAAe,iBAAiB,CAAC,OAAO,CAAC,OAAO,CAClE,CAAC;QACF,IAAI,OAAO,KAAK,KAAK,QAAQ;YAAE,OAAO,KAAK,CAAC;QAC5C,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,EAAE,CAAC;YAC5E,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC3B,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,KAAK,GAA2B;QACpC,oBAAoB,EAAE,0BAA0B;QAChD,IAAI,EAAE,4BAA4B;QAClC,KAAK,EAAE,uBAAuB;QAC9B,SAAS,EAAE,6BAA6B;QACxC,SAAS,EAAE,6BAA6B;QACxC,OAAO,EAAE,sBAAsB;QAC/B,OAAO,EAAE,sBAAsB;QAC/B,OAAO,EAAE,6BAA6B;KACvC,CAAC;IACF,OAAO,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AAC7B,CAAC;AAED,SAAS,iBAAiB,CACxB,MAAkB,EAClB,KAAuB;IAEvB,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,CAC5B,CAAC,IAAI,EAAE,EAAE,CACP,CAAC;QACC,YAAY;QACZ,OAAO;QACP,OAAO;QACP,OAAO;QACP,OAAO;QACP,IAAI;QACJ,KAAK;QACL,mBAAmB;QACnB,sBAAsB;KACvB,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAC3B,CAAC;IACF,MAAM,MAAM,GAA8B,EAAE,CAAC;IAC7C,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;QAC7B,MAAM,OAAO,GACX,IAAI,CAAC,OAAO,KAAK,UAAU;YACzB,CAAC,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;YAC5C,CAAC,CAAC,SAAS,CAAC;QAChB,MAAM,IAAI,GACR,OAAO,KAAK,SAAS;YACnB,CAAC,CAAC,GAAG,YAAY,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,iBAAiB,CAAC,OAAO,CAAC,EAAE;YAC3F,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;QAC1C,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC;QAC9E,MAAM,QAAQ,GACZ,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC;YAC1B,CAAC,IAAI,KAAK,sBAAsB;gBAC9B,CAAC,CAAC,0BAA0B;gBAC5B,CAAC,CAAC,gCAAgC,CAAC,CAAC;QACxC,MAAM,KAAK,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;QACvC,IACE,CAAC,MAAM,CAAC,IAAI,CACV,CAAC,QAAQ,EAAE,EAAE,CACX,QAAQ,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI;YAC5B,QAAQ,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI;YAC5B,QAAQ,CAAC,QAAQ,KAAK,KAAK,CAAC,QAAQ,CACvC,EACD,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;IACD,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,8BAA8B,CAAC;QACvD,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,8BAA8B;YAChD,CAAC,CAAC,EAAE,SAAS,EAAE,IAAa,EAAE;YAC9B,CAAC,CAAC,EAAE,CAAC;KACR,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CAAC,OAAe;IACxC,OAAO,IAAI,kBAAkB,CAC3B,cAAc,EACd,kCAAkC,OAAO,2CAA2C,CACrF,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CACxB,MAAkB,EAClB,OAAe,EACf,MAAc,EACd,GAAY;IAEZ,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC7B,MAAM,CAAC,IAAI,CACT,oBAAoB,OAAO,kDACzB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CACjD,iCAAiC,CAClC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,UAAU,iBAAiB,CAC/B,MAAkB,EAClB,IAAa,EACb,IAA8B;IAE9B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,OAAO,CAAC;IACtC,IAAI,SAAS,GAAG,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACvC,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5B,IAAI,CAAC;YACH,SAAS,GAAG,IAAI,SAAS,CAAC,MAAe,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;YAC7D,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QACpC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,iBAAiB,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;YACrD,SAAS,GAAG,IAAI,CAAC;QACnB,CAAC;IACH,CAAC;IACD,4EAA4E;IAC5E,0EAA0E;IAC1E,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;QACvB,OAAO,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAClE,CAAC;IACD,IAAI,MAAM,CAAC;IACX,IAAI,CAAC;QACH,MAAM,GAAG,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACpC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,uEAAuE;QACvE,iBAAiB,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;QACrD,OAAO,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAClE,CAAC;IACD,IAAI,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,gBAAgB,KAAK,GAAG,CAAC,CAAC;QACtE,MAAM,MAAM,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC;aACtD,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;aACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,gBAAgB,KAAK,CAAC,CAAC,KAAK,EAAE,CAAC;aAC/C,IAAI,CAAC,IAAI,CAAC,CAAC;QACd,OAAO,IAAI,kBAAkB,CAC3B,cAAc,EACd,0BAA0B,IAAI,CAAC,OAAO,MAAM,MAAM,IAAI,6CAA6C,EAAE,EACrG,EAAE,UAAU,EAAE,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,CACzD,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CACjC,MAAkB,EAClB,IAAgC;IAEhC,IAAI,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC;QAAE,OAAO;IACnC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,OAAO,CAAC;IACtC,IAAI,CAAC;QACH,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,SAAS,CAAC,MAAe,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC;IAC3E,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,iBAAiB,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;IACvD,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"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"}
package/dist/version.d.ts CHANGED
@@ -4,5 +4,5 @@
4
4
  * a bump that forgets this file fails the build rather than shipping a stale
5
5
  * version to `/health` and to downstream MCP handshakes.
6
6
  */
7
- export declare const CONNECTA_VERSION = "0.14.2";
7
+ export declare const CONNECTA_VERSION = "0.15.0";
8
8
  //# sourceMappingURL=version.d.ts.map
package/dist/version.js CHANGED
@@ -4,5 +4,5 @@
4
4
  * a bump that forgets this file fails the build rather than shipping a stale
5
5
  * version to `/health` and to downstream MCP handshakes.
6
6
  */
7
- export const CONNECTA_VERSION = "0.14.2";
7
+ export const CONNECTA_VERSION = "0.15.0";
8
8
  //# sourceMappingURL=version.js.map
@@ -219,21 +219,21 @@ const page = await connecta.search({
219
219
  });
220
220
  ```
221
221
 
222
- **S1.** Returns one flat page: `{ tools, total, offset, limit, hasMore }`, plus `nextOffset` when more remains and `matchMode: "partial"` when no tool matched every term. Each entry in `tools` carries `address`, `name`, and — when requested — `description`, `inputSchema`, `outputSchema`, `annotations`, and the connector's `guide`. Compact shapes omit property prose, put required fields first, and cap each shape at 1,024 UTF-8 bytes; capped shapes remain structurally valid with `unknown` types plus `/* truncated */`, and carry `inputSchemaTruncated` or `outputSchemaTruncated`. Use `connecta.describe` (or JSON search) for omitted exact constraints.
222
+ **S1.** Returns one flat page: `{ tools, total, offset, limit, hasMore }`, plus `nextOffset` when more remains and `matchMode: "partial"` when no tool matched every term. Top-level `search_tools` is different: it returns `{ connectors: [{ id, tools }], total, offset, limit, hasMore }`. Complete matches normally precede partial matches, but a partial candidate whose complete normalized tool name occurs in the normalized raw query competes by score; conversational cleanup applies only to scoring terms. Other candidates covering at least two terms fill the page after every complete match; when no complete match exists, the existing any-term fallback remains. Each entry in `tools` carries `address`, `name`, and — when requested — `description`, `inputSchema`, `outputSchema`, `annotations`, and the connector's `guide`. Tool rows expose neither lexical scores nor per-result coverage. An empty or whitespace-only query browses. Non-empty input with no ASCII lexical terms returns no tools and bounded no-match analysis; mixed input searches with its ASCII terms. Compact shapes omit property prose, put required fields first, and cap each shape at 1,024 UTF-8 bytes. Each enum node gets 256 of those bytes. About three near-cap enum nodes can therefore coexist while leaving the final quarter for surrounding syntax; the unchanged global fallback still applies above 1,024 bytes. A capped enum preserves whole values before `unknown` and an exact omitted-value count, while an empty enum renders as `never`. Either cap carries `inputSchemaTruncated` or `outputSchemaTruncated`; a shape-wide cap remains structurally valid with `unknown` types plus `/* truncated */`. Small enums remain complete. Use `connecta.describe` (or JSON search) for omitted exact constraints.
223
223
 
224
224
  **S1a.** `connector` loads only the named catalog; omit it only when the integration is ambiguous, because an unscoped search fans out across every configured connector. `safety: "readOnly"` returns exactly the tools available through `connecta.call`, connector shortcuts, and `connecta.batch`; `"approvalRequired"` returns the complementary fail-closed class, including false, missing, and contradictory annotations. Omitted or `"all"` preserves the complete catalog. These filters grant no authority and change no admission decision.
225
225
 
226
- **S2.** A requested object schema carries `inputKeys`, `requiredInputKeys`, and `outputKeys`:
227
- the same names the rendered schema shows, ready to check before
228
- building arguments. Match inputs, truncation, safety, and outputs, not lexical
226
+ **S2.** A requested object schema carries `inputKeys`, `requiredInputKeys`, and `outputKeys`: the same names the rendered schema shows, ready to check before building arguments. Match inputs, truncation, safety, and outputs, not lexical
229
227
  rank; search distinct operations separately and use `outputKeys`, not guessed roots. A non-object schema — a union, an array, an
230
228
  unresolvable `$ref` — carries no lists rather than empty ones, because absent
231
229
  means "read the schema" where `[]` would claim the tool takes no fields. The
232
230
  lists come from the same walk that renders the compact schema, so a top-level
233
231
  `$ref` resolves and an `allOf` composes rather than reporting an empty list
234
- beside a schema that plainly shows fields; an object with no properties is the
235
- one case where `[]` is the truth. A truncated schema omits the corresponding
236
- key list rather than repeating a large partial inventory. `search_tools`
232
+ beside a schema that plainly shows fields. A zero-input object keeps `inputKeys:
233
+ []` and `requiredInputKeys: []`; an output object with no declared properties
234
+ omits `outputKeys` because it declares no useful inventory. A
235
+ truncated schema omits the corresponding key list rather than repeating a
236
+ large partial inventory. `search_tools`
237
237
  carries the same metadata whenever schemas are requested. Code-mode callers
238
238
  can set `includeSchemaKeys: false` to buy the bytes back.
239
239
 
@@ -384,7 +384,7 @@ type beats keeping the prose.
384
384
 
385
385
  **E7.** `retryable` for `unknown_address`, `unknown_tool`, `ambiguous_tool_alias`, and `destructive_tool_requires_approval` is pinned false, never inferred from an address containing `503`, `429`, or `temporar`. The first two carry `nextAction: { function: "connecta.search", arguments: { query, connector?, includeSchemas: "compact" } }` — the same scoped discovery the top-level record names, keyed to the surface the caller actually has. A program cannot call `search_tools`, so it is never told to. Both the message and the derived `query` clamp the address to 512 UTF-8 bytes with a `…` marker: the address is caller-authored and lands in the message, the query, the text content, and `structuredContent`, so an invented 50 KB one would otherwise produce a refusal orders of magnitude past the deployment's result cap. A clipped address still identifies the mistake; a short one — the common case — is exact and untagged.
386
386
 
387
- **E8.** A remote MCP tool whose advertised schema rejects the call fails before provider dispatch with `invalid_args`, carrying bounded, value-free `{ path, code, expected }` findings and scoped search recovery keyed `function: "connecta.search"` like every other in-program miss. Unsupported schemas pass through; unrecognized provider prose remains `connector_call_failed`.
387
+ **E8.** A remote MCP tool whose advertised schema rejects the call fails before provider dispatch with `invalid_args`, carrying bounded, value-free `{ path, code, expected }` findings and scoped search recovery keyed `function: "connecta.search"` like every other in-program miss. A declared property reports the schema keyword that failed, never the validator's duplicate `additionalProperties` branch; a truly undeclared property still reports `additionalProperties`. Unsupported schemas pass through; unrecognized provider prose remains `connector_call_failed`.
388
388
 
389
389
  ## Results and projection
390
390
 
@@ -840,7 +840,7 @@ the upstream `Executor` shape assignable.
840
840
  | `A3` | `test/guest-api-contract.test.ts`, `test/execute.test.ts` (colliding alias) |
841
841
  | `A4` | `test/execute.test.ts` (namespace collisions, reserved namespace) |
842
842
  | `A5` | verdict; `A1`–`A3` are its enforcement |
843
- | `S1`, `S2` | `test/guest-api-contract.test.ts` (flat page, connector guides, schema keys, and the unfiltered browse that replaces `list_connectors`), `test/execute.test.ts` (guide pagination/partial/no-match behavior and `$ref`/`allOf`) |
843
+ | `S1`, `S2` | `test/guest-api-contract.test.ts` (flat page, connector guides, schema keys, and the unfiltered browse that replaces `list_connectors`), `test/execute.test.ts` (guide pagination/partial/no-match behavior and `$ref`/`allOf`), `test/meta-tools.test.ts` (mixed complete/partial ranking and stable pagination) |
844
844
  | `S3` | `test/guest-api-contract.test.ts` (typed uncaught bound), `test/execute.test.ts` (count limits, fan-out bound) |
845
845
  | `S4` | `test/guest-api-contract.test.ts` (unknown address in `describe`) |
846
846
  | `S5` | `test/guest-api-contract.test.ts`, `test/execute.test.ts` (`unwrapMcpResult`) |
@@ -46,6 +46,18 @@ read-only calls and returns typed outcomes, and an unfiltered
46
46
  `connecta.search({})` browses every catalog a program can reach. Live connector
47
47
  probing is an operator concern: the operator pages and `/health` own it.
48
48
 
49
+ The three discovery routes use deliberately different envelopes. These are
50
+ their smallest successful one-tool shapes:
51
+
52
+ ```js
53
+ // Top-level search_tools
54
+ { connectors: [{ id: "ci", tools: [{ name: "get_run", address: "ci.get_run" }] }], total: 1, offset: 0, limit: 8, hasMore: false }
55
+
56
+ // Inside execute_code
57
+ { tools: [{ name: "get_run", address: "ci.get_run" }], total: 1, offset: 0, limit: 8, hasMore: false } // connecta.search
58
+ { tools: [{ name: "get_run", address: "ci.get_run", inputSchema: "{ runId: integer }" }] } // connecta.describe
59
+ ```
60
+
49
61
  ## Discovery context
50
62
 
51
63
  Start an unknown-address lookup with two to four distinctive action/object
@@ -60,8 +72,10 @@ or setting it to `"all"`, preserves the complete configured catalog. This is
60
72
  only a discovery filter: it neither grants authority nor changes invocation admission.
61
73
  `includeSchemas: "compact"` adds each match's input and any declared output
62
74
  shape. Bounded plain-object schemas also expose `inputKeys`,
63
- `requiredInputKeys`, and `outputKeys`; a truncated shape omits its corresponding
64
- list rather than repeating a large partial inventory. Matches carry declared
75
+ `requiredInputKeys`, and `outputKeys`; a zero-input object keeps
76
+ `requiredInputKeys: []`, while an output object with no declared properties
77
+ omits `outputKeys`. A truncated shape omits its corresponding list rather than
78
+ repeating a large partial inventory. Matches carry declared
65
79
  behavior annotations. Lexical rank is only one signal: select a candidate whose
66
80
  required inputs are available, whose schema is complete enough for the call,
67
81
  and whose safety and declared outputs fit the work. A reducer uses `outputKeys`
@@ -75,11 +89,17 @@ Compact search is deliberately a routing view, not a second copy of connector
75
89
  documentation. Tool purposes are capped at 160 characters, connector
76
90
  descriptions and property prose are omitted, required input fields render
77
91
  before optional ones, and each input or output shape is capped at 1,024 UTF-8
78
- bytes. A capped object becomes a valid required-first shape with `unknown`
79
- types; other shapes become `unknown /* truncated */`. The match also carries
92
+ bytes. Within that unchanged total, each enum node may spend at most 256 UTF-8
93
+ bytes. This lets about three near-cap enum nodes coexist while reserving the
94
+ remaining quarter for surrounding syntax; the global fallback still applies
95
+ when the complete shape exceeds 1,024 bytes. A large enum keeps the longest
96
+ whole-value prefix that fits, then adds `unknown` and a comment with the exact
97
+ omitted-value count. An empty enum renders as the valid `never` type. A capped
98
+ object becomes a valid required-first shape with `unknown` types; other shapes
99
+ become `unknown /* truncated */`. Either cap marks the match with
80
100
  `inputSchemaTruncated` or `outputSchemaTruncated`; repeat the search with
81
101
  `includeSchemas: "json"` or use the existing describe path when exact
82
- constraints matter.
102
+ constraints matter. Small enums and both exact paths remain complete.
83
103
 
84
104
  ## Connector guide selection
85
105
 
@@ -176,13 +196,34 @@ set of inflectional variants preserves singular/plural and verb-form recall
176
196
  without allowing arbitrary mid-word substring matches. Ranking weights each
177
197
  query term by its document frequency across the available catalogs in that
178
198
  search, so a rare domain term outranks a ubiquitous action while action terms
179
- still distinguish `get`, `list`, `search`, and write operations. If no tool
180
- covers every non-conversational term, the same scorer falls back to any-term
181
- matching and marks the result `matchMode: "partial"`.
182
-
183
- Every partial or no-match lexical search also returns bounded `queryAnalysis`;
184
- an all-term result needs no recovery advice. `representedTerms` occur in the
185
- current page, `otherResultTerms` occur only in another result, and
199
+ still distinguish `get`, `list`, `search`, and write operations. The scorer
200
+ always evaluates useful near-matches instead of letting one broad all-term
201
+ description hide them. Complete matches rank before ordinary partial matches;
202
+ a partial candidate whose complete normalized tool name occurs in the
203
+ normalized raw query competes with complete matches by score, and other
204
+ candidates covering at least two terms fill the remaining page after them.
205
+ Conversational cleanup applies only to scoring terms, never to the exact-name
206
+ phrase check. If no tool covers every non-conversational term, the same scorer
207
+ preserves the wider any-term fallback and marks the result
208
+ `matchMode: "partial"`.
209
+
210
+ Returned tool rows expose neither lexical scores nor per-result query coverage.
211
+ The mixed complete/partial scorer still ranks rare domain terms, action terms,
212
+ and exact tool-name phrases. Select from the returned purpose, address, schema,
213
+ safety, and output shape. Page-level `queryAnalysis` remains the recovery path
214
+ when no single result covers every term or no match exists.
215
+
216
+ Only an empty or whitespace-only query browses. A non-empty query that
217
+ normalizes to no ASCII lexical terms returns no tools instead of unrelated
218
+ browse results. Its bounded `queryAnalysis.unmatchedTerms` contains the clipped
219
+ raw query and guidance asks for ASCII action/object terms. A mixed query still
220
+ searches with its ASCII terms; unsupported characters do not become false
221
+ matches or per-tool coverage terms.
222
+
223
+ Every partial or no-match lexical search also returns bounded page-level
224
+ `queryAnalysis`; an all-term result needs no recovery advice.
225
+ `representedTerms` occur in the current page, `otherResultTerms` occur only in
226
+ another result, and
186
227
  `unmatchedTerms` have no lexical match in the catalogs that answered. Partial
187
228
  results explain that no single tool covered every term and recommend splitting
188
229
  distinct intents. A true negative says that no matching capability is
@@ -331,7 +372,9 @@ exist. `nextAction` points to discovery scoped to the same connector and tool
331
372
  name when the compact schema is needed — routed like any other miss, so a
332
373
  program is sent to `connecta.search` and a top-level call to `search_tools` —
333
374
  while `retry` says to correct the listed arguments and reissue the original
334
- operation. A schema the local
375
+ operation. A declared property reports only its failed schema keyword, while a
376
+ truly undeclared property reports `additionalProperties`; validator-internal
377
+ duplicate `additionalProperties` branches never reach the caller. A schema the local
335
378
  validator cannot evaluate passes through to the provider. Provider error prose
336
379
  is not parsed or guessed, so an unknown format remains
337
380
  `connector_call_failed`.
package/ethos.md CHANGED
@@ -91,6 +91,7 @@ proposing one without a new argument is not.
91
91
  | `get_result` paging for program results | refused | paging rewards the unprojected return code mode exists to remove; a program can shrink anything ([#223](https://github.com/zackbart/connecta/issues/223)) |
92
92
  | Stabilized workflows (programs → versioned scripts/skills) | gated | earns a surface only once real traffic shows programs that actually recur ([#225](https://github.com/zackbart/connecta/issues/225)) |
93
93
  | Semantic tool search | gated | keyword search has not been shown to be the thing failing; earns its way in through [#222](https://github.com/zackbart/connecta/issues/222)'s harness ([#27](https://github.com/zackbart/connecta/issues/27)) |
94
+ | Per-result lexical query coverage | removed | verbose, indexed, and trailing shapes did not earn their response-token cost: the coverage-off arm beat the verbose wire, the first compact wire regressed efficiency, and the trailing wire failed its precommitted 30-run clean-route gate (13/30 vs 9/30, +13.3 pp, Fisher p=0.422); preserve the mixed complete/partial ranking from [#326](https://github.com/zackbart/connecta/issues/326), but do not revive serialized coverage without new causal evidence ([#322](https://github.com/zackbart/connecta/issues/322), [#323](https://github.com/zackbart/connecta/issues/323)) |
94
95
  | MRTR / `input_required` passthrough | gated | statelessly relayable via `requestState`, but no host or downstream emits it yet; fails loudly until adoption evidence ([#176](https://github.com/zackbart/connecta/issues/176)) |
95
96
  | Native Tasks for oversized results | refused | tasks solve duration, `get_result` solves size; paging on a polling extension adds round trips for nothing ([#176](https://github.com/zackbart/connecta/issues/176)) |
96
97
  | Downstream `ttlMs` cache hints | gated | fixed TTL + fingerprint is battle-tested and catalog reads are ~3 ms; earns its way in with refresh-churn evidence ([#176](https://github.com/zackbart/connecta/issues/176)) |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zackbart/connecta",
3
- "version": "0.14.2",
3
+ "version": "0.15.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "description": "One MCP to rule them all — a single MCP endpoint aggregating many downstream connectors behind a code-first surface of seven meta-tools.",
@@ -45,11 +45,29 @@ export const DEFAULT_SEARCH_LIMIT = 8;
45
45
  export const MAX_SEARCH_LIMIT = 100;
46
46
  export const MAX_DESCRIBE_ADDRESSES = 100;
47
47
  export const MAX_DISCOVERY_RESULT_BYTES = 256_000;
48
- const MAX_QUERY_ANALYSIS_TERMS = 8;
49
- const MAX_QUERY_ANALYSIS_TERM_LENGTH = 64;
48
+ const MAX_QUERY_TERMS = 8;
49
+ const MAX_QUERY_TERM_LENGTH = 64;
50
50
 
51
51
  const encoder = new TextEncoder();
52
52
 
53
+ /** Clip one echoed query term without splitting a non-BMP code point. */
54
+ function boundedQueryTerm(term: string): {
55
+ text: string;
56
+ truncated: boolean;
57
+ } {
58
+ const characters: string[] = [];
59
+ for (const character of term) {
60
+ characters.push(character);
61
+ if (characters.length > MAX_QUERY_TERM_LENGTH) {
62
+ return {
63
+ text: `${characters.slice(0, MAX_QUERY_TERM_LENGTH - 1).join("")}…`,
64
+ truncated: true,
65
+ };
66
+ }
67
+ }
68
+ return { text: characters.join(""), truncated: false };
69
+ }
70
+
53
71
  /**
54
72
  * The discovery route a routing failure should send a caller back through. Same
55
73
  * catalog logic serves both the top-level `search_tools` path and the
@@ -261,7 +279,9 @@ function schemaKeyMetadata(
261
279
  requiredInputKeys: inputKeys.required,
262
280
  }
263
281
  : {}),
264
- ...(outputKeys ? { outputKeys: outputKeys.properties } : {}),
282
+ ...(outputKeys && outputKeys.properties.length > 0
283
+ ? { outputKeys: outputKeys.properties }
284
+ : {}),
265
285
  };
266
286
  }
267
287
 
@@ -647,6 +667,9 @@ export class CatalogService {
647
667
  tool: ToolDef;
648
668
  score: number;
649
669
  order: number;
670
+ exactName: boolean;
671
+ matchedTermCount: number;
672
+ complete: boolean;
650
673
  }> = [];
651
674
  let matchMode: "all" | "partial" = "all";
652
675
  const statistics = lexicalCorpusStatistics(
@@ -655,8 +678,19 @@ export class CatalogService {
655
678
  ),
656
679
  retrievalQuery,
657
680
  );
681
+ const trimmedQuery = query.trim();
682
+ const isBrowse = trimmedQuery.length === 0;
683
+ const queryTerms = lexicalQueryTerms(retrievalQuery);
684
+ const queryTermCount = queryTerms.length;
685
+ const unsearchableQuery = !isBrowse && queryTermCount === 0;
686
+ const analysisTerms = unsearchableQuery ? [trimmedQuery] : queryTerms;
687
+ const analyzedTerms = analysisTerms.slice(0, MAX_QUERY_TERMS);
688
+ const displayTerm = (term: string) => boundedQueryTerm(term).text;
689
+ const queryMetadataTruncated =
690
+ analysisTerms.length > analyzedTerms.length ||
691
+ analyzedTerms.some((term) => boundedQueryTerm(term).truncated);
658
692
  const collectMatches = (mode: "all" | "partial") => {
659
- matches.length = 0;
693
+ const collected: typeof matches = [];
660
694
  let orderBase = 0;
661
695
  searchableCatalogs.forEach((catalog, connectorIndex) => {
662
696
  const connector = connectors[connectorIndex];
@@ -669,25 +703,59 @@ export class CatalogService {
669
703
  retrievalQuery,
670
704
  mode,
671
705
  statistics,
706
+ query,
672
707
  )) {
673
- matches.push({
708
+ collected.push({
674
709
  connector,
675
710
  tool: ranked.tool,
676
711
  score: ranked.score,
677
712
  order: orderBase + ranked.order,
713
+ exactName: ranked.exactName,
714
+ matchedTermCount: ranked.matchedTermCount,
715
+ complete:
716
+ isBrowse || ranked.matchedTermCount === queryTermCount,
678
717
  });
679
718
  }
680
719
  }
681
720
  orderBase +=
682
721
  catalog.status === "fulfilled" ? catalog.value.length : 1;
683
722
  });
723
+ return collected;
684
724
  };
685
- collectMatches("all");
686
- if (query.trim() && matches.length === 0) {
725
+ // A non-empty query that normalizes to no lexical terms is not a browse.
726
+ // Ranking an empty phrase would otherwise return every tool as an
727
+ // unrelated zero-score match, with no coverage to explain the result.
728
+ const rankedMatches = unsearchableQuery
729
+ ? []
730
+ : collectMatches(isBrowse ? "all" : "partial");
731
+ const completeMatchCount = rankedMatches.filter(
732
+ (match) => match.complete,
733
+ ).length;
734
+ matches.push(
735
+ ...rankedMatches.filter(
736
+ (match) =>
737
+ match.complete ||
738
+ completeMatchCount === 0 ||
739
+ match.exactName ||
740
+ match.matchedTermCount >= 2,
741
+ ),
742
+ );
743
+ if (!isBrowse && !unsearchableQuery && completeMatchCount === 0) {
687
744
  matchMode = "partial";
688
- collectMatches(matchMode);
689
745
  }
690
- matches.sort((a, b) => b.score - a.score || a.order - b.order);
746
+ // Complete matches and exact tool-name phrases share the first rank tier.
747
+ // This lets a strong action/object name beat a weak description-only
748
+ // decoy. Other partial matches fill the remaining page only after every
749
+ // complete match, regardless of a rare-term score spike.
750
+ matches.sort((a, b) => {
751
+ const aFirstTier = a.complete || a.exactName;
752
+ const bFirstTier = b.complete || b.exactName;
753
+ return (
754
+ Number(bFirstTier) - Number(aFirstTier) ||
755
+ b.score - a.score ||
756
+ a.order - b.order
757
+ );
758
+ });
691
759
  const pageMatches = matches.slice(offset, offset + limit);
692
760
  const entries = pageMatches.map((match) => {
693
761
  const input = match.tool.inputSchema ?? { type: "object" };
@@ -769,12 +837,6 @@ export class CatalogService {
769
837
  offset + entries.length < matches.length
770
838
  ? offset + entries.length
771
839
  : undefined;
772
- const queryTerms = lexicalQueryTerms(retrievalQuery);
773
- const analyzedTerms = queryTerms.slice(0, MAX_QUERY_ANALYSIS_TERMS);
774
- const displayTerm = (term: string) =>
775
- term.length <= MAX_QUERY_ANALYSIS_TERM_LENGTH
776
- ? term
777
- : `${term.slice(0, MAX_QUERY_ANALYSIS_TERM_LENGTH - 1)}…`;
778
840
  const pageTools = new Set(pageMatches.map((match) => match.tool));
779
841
  const matchingTools = (term: string) =>
780
842
  new Set([
@@ -844,19 +906,24 @@ export class CatalogService {
844
906
  args.connector && !scopedConnector
845
907
  ? `Connector "${args.connector}" is not configured in this deployment. Omit connector to search all configured tools.`
846
908
  : undefined;
847
- // Term-bearing searches report analysis only when the scorer had to
848
- // degrade; a browse has no terms to analyse and normally reports none at
849
- // all. But neither "this catalog is unavailable" nor "there is no such
850
- // connector" is a statement about terms, and answering either browse with
851
- // an empty entry list alone is indistinguishable from a connector that
852
- // simply exposes no tools. The term partitions stay empty on those paths
853
- // because there were no terms — the scope fields carry the whole message.
909
+ // Searchable queries report analysis when the scorer had to degrade. A
910
+ // non-empty query with no searchable terms reports the bounded raw input
911
+ // as unmatched instead of silently becoming a browse. A real browse has
912
+ // no terms to analyse and normally reports none at all. Scope failures are
913
+ // the exception, because an empty result alone looks like a connector that
914
+ // correctly exposes no tools.
854
915
  const reportsQueryAnalysis =
855
- queryTerms.length > 0
916
+ unsearchableQuery ||
917
+ (queryTerms.length > 0
856
918
  ? matchMode === "partial"
857
- : unknownConnectorGuidance !== undefined || unavailableCatalogs > 0;
919
+ : unknownConnectorGuidance !== undefined || unavailableCatalogs > 0);
858
920
  const guidance =
859
- queryTerms.length === 0
921
+ unsearchableQuery
922
+ ? (unknownConnectorGuidance ??
923
+ (scopedConnector && unavailableCatalogs > 0
924
+ ? `Connector "${scopedConnector.id}" could not be searched because its catalog was unavailable. Inspect catalogError for the typed reason and recovery detail.`
925
+ : "The query contained no searchable lexical terms. Use 2–4 ASCII action/object terms, or browse with an empty query."))
926
+ : queryTerms.length === 0
860
927
  ? // A browse has no terms to advise about, so it stays silent unless
861
928
  // the scope itself failed: the guidance on a scoped miss recommends
862
929
  // browsing with an empty query, and that advice must not lead into a
@@ -901,10 +968,7 @@ export class CatalogService {
901
968
  representedTerms,
902
969
  otherResultTerms,
903
970
  unmatchedTerms,
904
- ...(queryTerms.length > analyzedTerms.length ||
905
- analyzedTerms.some(
906
- (term) => term.length > MAX_QUERY_ANALYSIS_TERM_LENGTH,
907
- )
971
+ ...(queryMetadataTruncated
908
972
  ? { truncated: true as const }
909
973
  : {}),
910
974
  ...(args.connector ? { connectorScope: args.connector } : {}),