@bevel-software/platform-mcp-core 0.25.2 → 0.27.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/call-guards.d.ts +47 -0
- package/dist/call-guards.d.ts.map +1 -0
- package/dist/call-guards.js +215 -0
- package/dist/call-guards.js.map +1 -0
- package/dist/dispatch.d.ts +5 -1
- package/dist/dispatch.d.ts.map +1 -1
- package/dist/dispatch.js +19 -2
- package/dist/dispatch.js.map +1 -1
- package/dist/get-has-no-body.d.ts +49 -0
- package/dist/get-has-no-body.d.ts.map +1 -0
- package/dist/get-has-no-body.js +104 -0
- package/dist/get-has-no-body.js.map +1 -0
- package/dist/index.d.ts +7 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -2
- package/dist/index.js.map +1 -1
- package/dist/mcp-app.d.ts +118 -0
- package/dist/mcp-app.d.ts.map +1 -0
- package/dist/mcp-app.js +126 -0
- package/dist/mcp-app.js.map +1 -0
- package/dist/meta-tools.d.ts +1 -1
- package/dist/meta-tools.d.ts.map +1 -1
- package/dist/meta-tools.js +28 -6
- package/dist/meta-tools.js.map +1 -1
- package/dist/proxied-tool.d.ts +33 -3
- package/dist/proxied-tool.d.ts.map +1 -1
- package/dist/proxied-tool.js +234 -21
- package/dist/proxied-tool.js.map +1 -1
- package/dist/results.d.ts +20 -1
- package/dist/results.d.ts.map +1 -1
- package/dist/results.js +117 -3
- package/dist/results.js.map +1 -1
- package/dist/schema-validity.d.ts +50 -0
- package/dist/schema-validity.d.ts.map +1 -0
- package/dist/schema-validity.js +254 -0
- package/dist/schema-validity.js.map +1 -0
- package/dist/tool-interface.d.ts +137 -0
- package/dist/tool-interface.d.ts.map +1 -0
- package/dist/tool-interface.js +640 -0
- package/dist/tool-interface.js.map +1 -0
- package/dist/utcp-namespace.d.ts +14 -0
- package/dist/utcp-namespace.d.ts.map +1 -1
- package/dist/utcp-namespace.js +7 -2
- package/dist/utcp-namespace.js.map +1 -1
- package/package.json +2 -1
- package/src/call-guards.ts +237 -0
- package/src/dispatch.ts +18 -1
- package/src/get-has-no-body.ts +110 -0
- package/src/index.ts +45 -0
- package/src/mcp-app.ts +194 -0
- package/src/meta-tools.ts +31 -6
- package/src/proxied-tool.ts +237 -19
- package/src/results.ts +129 -3
- package/src/schema-validity.ts +264 -0
- package/src/tool-interface.ts +673 -0
- package/src/utcp-namespace.ts +7 -2
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Is a connected tool's input schema valid JSON Schema?
|
|
3
|
+
*
|
|
4
|
+
* AI clients ask this question themselves, and when the answer is no they drop
|
|
5
|
+
* the tool WITHOUT SAYING SO: the tool is simply missing for the agent, with
|
|
6
|
+
* nothing in Hexis to look at. So Hexis asks it first, when a server's tools
|
|
7
|
+
* are loaded, and keeps such a tool off every agent surface with a marker the
|
|
8
|
+
* server's owner can read.
|
|
9
|
+
*
|
|
10
|
+
* The check is the JSON Schema META-SCHEMA (2020-12, the draft the MCP
|
|
11
|
+
* specification names), run by `ajv` — the same validator the MCP SDK and the
|
|
12
|
+
* clients use, so a tool is hidden on exactly the violations a client would
|
|
13
|
+
* also refuse, and the reason is quoted in the words the client reports. The
|
|
14
|
+
* three refusals this started from reproduce to the character:
|
|
15
|
+
*
|
|
16
|
+
* /properties/…/socialLinks/items/anyOf must be an array
|
|
17
|
+
* /properties/…/value/anyOf/0/required/0 must be a string
|
|
18
|
+
* /properties/…/value/properties/table/type must be equal to one of the allowed values
|
|
19
|
+
*
|
|
20
|
+
* One thing the meta-schema does not cover is checked beside it: a `pattern`
|
|
21
|
+
* (or a `patternProperties` key) that is not a compilable regular expression.
|
|
22
|
+
* The meta-schema says only that it is a string, while a client COMPILES it and
|
|
23
|
+
* drops the tool when that throws — see {@link regexDefect}.
|
|
24
|
+
*
|
|
25
|
+
* NOT a repair. Nothing here rewrites a schema — the proxy passes a connected
|
|
26
|
+
* server's schema through as sent, and an invalid one is reported, never fixed.
|
|
27
|
+
*/
|
|
28
|
+
/** Where a schema is not valid JSON Schema, and why. */
|
|
29
|
+
export interface SchemaDefect {
|
|
30
|
+
/** JSON Pointer into the schema, e.g. `/properties/value/required/0`. */
|
|
31
|
+
path: string;
|
|
32
|
+
/** The violation in the validator's own words, e.g. `must be a string`. */
|
|
33
|
+
reason: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The first place `schema` is not valid JSON Schema, or `null` when it is.
|
|
37
|
+
*
|
|
38
|
+
* FAIL-OPEN: a schema the validator itself cannot process (an unknown
|
|
39
|
+
* `$schema` dialect, say) is reported as having no defect. A validator fault
|
|
40
|
+
* is not evidence against the tool, and the cost of being wrong here is one
|
|
41
|
+
* tool hidden from every agent.
|
|
42
|
+
*/
|
|
43
|
+
export declare function inputSchemaDefect(schema: unknown): SchemaDefect | null;
|
|
44
|
+
/**
|
|
45
|
+
* The sentence the tool's owner reads, wherever the marker is shown — the tool
|
|
46
|
+
* setup page and `list_tool_setup` say the same thing in the same words,
|
|
47
|
+
* because they are the same finding.
|
|
48
|
+
*/
|
|
49
|
+
export declare function schemaDefectMarker(defect: SchemaDefect): string;
|
|
50
|
+
//# sourceMappingURL=schema-validity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-validity.d.ts","sourceRoot":"","sources":["../src/schema-validity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAIH,wDAAwD;AACxD,MAAM,WAAW,YAAY;IAC3B,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAC;IACb,2EAA2E;IAC3E,MAAM,EAAE,MAAM,CAAC;CAChB;AAsCD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,OAAO,GAAG,YAAY,GAAG,IAAI,CAwBtE;AA8ID;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAE/D"}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Is a connected tool's input schema valid JSON Schema?
|
|
3
|
+
*
|
|
4
|
+
* AI clients ask this question themselves, and when the answer is no they drop
|
|
5
|
+
* the tool WITHOUT SAYING SO: the tool is simply missing for the agent, with
|
|
6
|
+
* nothing in Hexis to look at. So Hexis asks it first, when a server's tools
|
|
7
|
+
* are loaded, and keeps such a tool off every agent surface with a marker the
|
|
8
|
+
* server's owner can read.
|
|
9
|
+
*
|
|
10
|
+
* The check is the JSON Schema META-SCHEMA (2020-12, the draft the MCP
|
|
11
|
+
* specification names), run by `ajv` — the same validator the MCP SDK and the
|
|
12
|
+
* clients use, so a tool is hidden on exactly the violations a client would
|
|
13
|
+
* also refuse, and the reason is quoted in the words the client reports. The
|
|
14
|
+
* three refusals this started from reproduce to the character:
|
|
15
|
+
*
|
|
16
|
+
* /properties/…/socialLinks/items/anyOf must be an array
|
|
17
|
+
* /properties/…/value/anyOf/0/required/0 must be a string
|
|
18
|
+
* /properties/…/value/properties/table/type must be equal to one of the allowed values
|
|
19
|
+
*
|
|
20
|
+
* One thing the meta-schema does not cover is checked beside it: a `pattern`
|
|
21
|
+
* (or a `patternProperties` key) that is not a compilable regular expression.
|
|
22
|
+
* The meta-schema says only that it is a string, while a client COMPILES it and
|
|
23
|
+
* drops the tool when that throws — see {@link regexDefect}.
|
|
24
|
+
*
|
|
25
|
+
* NOT a repair. Nothing here rewrites a schema — the proxy passes a connected
|
|
26
|
+
* server's schema through as sent, and an invalid one is reported, never fixed.
|
|
27
|
+
*/
|
|
28
|
+
import Ajv2020 from 'ajv/dist/2020.js';
|
|
29
|
+
/**
|
|
30
|
+
* One validator for the process. `strict: false` keeps this to the
|
|
31
|
+
* meta-schema: ajv's strict mode objects to things no client refuses (an
|
|
32
|
+
* unknown keyword, a `required` naming an undeclared property), and hiding a
|
|
33
|
+
* tool that works is worse than listing one whose schema is merely unusual.
|
|
34
|
+
*
|
|
35
|
+
* `validateFormats: false` is the same rule applied to the META-schema's own
|
|
36
|
+
* `format` annotations (`$id` and `$ref` as `uri-reference`, `pattern` as
|
|
37
|
+
* `regex`). Switching them on is INERT, which is worth knowing before anyone
|
|
38
|
+
* reaches for it: `validateSchema` does not assert the meta-schema's formats
|
|
39
|
+
* at all, so every schema gets the same verdict with `validateFormats: true`
|
|
40
|
+
* as with it off — including `pattern: "["`, which ajv-formats' own `regex`
|
|
41
|
+
* function rejects when called directly. The switch changes configuration,
|
|
42
|
+
* not behaviour; `__tests__/schema-validity.test.ts` pins that.
|
|
43
|
+
*
|
|
44
|
+
* It would be the wrong thing to want in any case. An AI client never
|
|
45
|
+
* meta-validates the schema document: the MCP SDK's validator runs
|
|
46
|
+
* `{ strict: false, validateFormats: true, validateSchema: false }`
|
|
47
|
+
* (`validation/ajv-provider.js`) — formats on the INSTANCE, the schema
|
|
48
|
+
* document itself never checked against the meta-schema — so a malformed
|
|
49
|
+
* `$id` or `$ref` compiles there without complaint, and flagging one here
|
|
50
|
+
* would hide a tool every client accepts. The one URI-valued construct a
|
|
51
|
+
* client really refuses is a `$ref` it cannot RESOLVE (`can't resolve
|
|
52
|
+
* reference …` out of `compile`), and no format assertion catches that one
|
|
53
|
+
* either; none is ever offered, because `sanitizeInputSchema` replaces an
|
|
54
|
+
* unresolvable or non-local `$ref` with `{}` before the listing goes out.
|
|
55
|
+
* Each half is pinned where it belongs: what this check does NOT flag in
|
|
56
|
+
* `__tests__/schema-validity.test.ts`, and what the proxy offers in place of
|
|
57
|
+
* such a `$ref` in `__tests__/schema-pass-through.test.ts`.
|
|
58
|
+
*
|
|
59
|
+
* So the one format that does decide a client's verdict is `regex` — a client
|
|
60
|
+
* COMPILES a `pattern` — and because the meta-check misses it under either
|
|
61
|
+
* setting, it is checked directly, below.
|
|
62
|
+
*/
|
|
63
|
+
const ajv = new Ajv2020({ strict: false, allErrors: false, validateFormats: false });
|
|
64
|
+
/**
|
|
65
|
+
* The first place `schema` is not valid JSON Schema, or `null` when it is.
|
|
66
|
+
*
|
|
67
|
+
* FAIL-OPEN: a schema the validator itself cannot process (an unknown
|
|
68
|
+
* `$schema` dialect, say) is reported as having no defect. A validator fault
|
|
69
|
+
* is not evidence against the tool, and the cost of being wrong here is one
|
|
70
|
+
* tool hidden from every agent.
|
|
71
|
+
*/
|
|
72
|
+
export function inputSchemaDefect(schema) {
|
|
73
|
+
// MCP requires an OBJECT input schema. `true`/`false` are legal JSON Schema
|
|
74
|
+
// but not legal here, and an array or a string is neither. Only `undefined`
|
|
75
|
+
// is ABSENT: a server that advertises `"inputSchema": null` has declared one
|
|
76
|
+
// and declared it wrong, which is a thing its owner wants to hear about.
|
|
77
|
+
if (schema === undefined)
|
|
78
|
+
return null; // absent: `toListedTool` supplies `{}`
|
|
79
|
+
if (schema === null || typeof schema !== 'object' || Array.isArray(schema)) {
|
|
80
|
+
return { path: '/', reason: 'must be an object' };
|
|
81
|
+
}
|
|
82
|
+
let valid;
|
|
83
|
+
try {
|
|
84
|
+
valid = ajv.validateSchema(schema);
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
if (!valid) {
|
|
90
|
+
const first = ajv.errors?.[0];
|
|
91
|
+
if (!first)
|
|
92
|
+
return null;
|
|
93
|
+
return {
|
|
94
|
+
path: first.instancePath || '/',
|
|
95
|
+
reason: withArticle(first.message ?? 'is not valid JSON Schema'),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
return regexDefect(schema);
|
|
99
|
+
}
|
|
100
|
+
/** JSON Schema keywords whose value is a schema, or a list of schemas. */
|
|
101
|
+
const SCHEMA_VALUED_KEYWORDS = [
|
|
102
|
+
'additionalItems',
|
|
103
|
+
'additionalProperties',
|
|
104
|
+
'allOf',
|
|
105
|
+
'anyOf',
|
|
106
|
+
'contains',
|
|
107
|
+
'contentSchema',
|
|
108
|
+
'else',
|
|
109
|
+
'if',
|
|
110
|
+
'items',
|
|
111
|
+
'not',
|
|
112
|
+
'oneOf',
|
|
113
|
+
'prefixItems',
|
|
114
|
+
'propertyNames',
|
|
115
|
+
'then',
|
|
116
|
+
'unevaluatedItems',
|
|
117
|
+
'unevaluatedProperties',
|
|
118
|
+
];
|
|
119
|
+
/**
|
|
120
|
+
* Keywords whose value is a MAP of schemas: the keys are names, the values
|
|
121
|
+
* schemas. `$defs`/`definitions` are NOT walked as such: an entry there is a
|
|
122
|
+
* schema only once a reference reaches it (ajv compiles nothing else, and the
|
|
123
|
+
* listing the proxy serves drops the block and inlines only what a `$ref`
|
|
124
|
+
* reaches), so the walk follows the references instead — see
|
|
125
|
+
* {@link regexDefect}.
|
|
126
|
+
*/
|
|
127
|
+
const SCHEMA_MAP_KEYWORDS = ['dependentSchemas', 'patternProperties', 'properties'];
|
|
128
|
+
/**
|
|
129
|
+
* A bound on the walk below, for a schema whose shape is the sender's choice.
|
|
130
|
+
* It caps the QUEUE, so neither depth nor breadth can make the check cost more
|
|
131
|
+
* than this many entries.
|
|
132
|
+
*/
|
|
133
|
+
const MAX_REGEX_CHECK_NODES = 50_000;
|
|
134
|
+
/**
|
|
135
|
+
* The first regex-bearing keyword in `schema` that does not compile, or `null`.
|
|
136
|
+
*
|
|
137
|
+
* The meta-schema does NOT catch these: `{ "type": "string", "pattern": "[" }`
|
|
138
|
+
* is a perfectly well-formed schema document. But a client does not merely read
|
|
139
|
+
* a `pattern` — it COMPILES it (the MCP SDK's own validator is `ajv.compile`),
|
|
140
|
+
* so that schema throws there and the tool is dropped just as silently as one
|
|
141
|
+
* the meta-schema rejects. Hexis has to find it for the same reason it finds
|
|
142
|
+
* the others.
|
|
143
|
+
*
|
|
144
|
+
* Tested with the `u` flag, which is how ajv compiles a `pattern`
|
|
145
|
+
* (`unicodeRegExp`, on by default) — so the verdict matches the validator the
|
|
146
|
+
* clients actually run rather than a looser reading. The walk is iterative and
|
|
147
|
+
* only ever descends through keywords whose value IS a schema, so a tool's own
|
|
148
|
+
* field named `pattern` (or a `pattern` string inside a `const` value) is data
|
|
149
|
+
* and is left alone.
|
|
150
|
+
*
|
|
151
|
+
* A `$defs`/`definitions` entry is reached only through a local `$ref` (or
|
|
152
|
+
* `$dynamicRef`) that names it, and reported at ITS OWN path — the place in
|
|
153
|
+
* the schema the server sent, which is what its owner edits. An entry nothing
|
|
154
|
+
* references is never compiled by a client and never listed by the proxy, so
|
|
155
|
+
* a bad regex in it hides no tool anywhere and is not a defect here: this
|
|
156
|
+
* module's contract is "hidden on exactly what a client would refuse".
|
|
157
|
+
*/
|
|
158
|
+
function regexDefect(schema) {
|
|
159
|
+
const queue = [{ node: schema, path: '' }];
|
|
160
|
+
// Each node once, by path: two references to one entry are one schema.
|
|
161
|
+
const seen = new Set(['']);
|
|
162
|
+
// The cap bounds what is ENQUEUED, not only what is dequeued: a wide schema
|
|
163
|
+
// reaches its limit by breadth rather than depth, and a queue entry costs a
|
|
164
|
+
// path string that the node it describes does not. Past the cap the check
|
|
165
|
+
// simply stops looking, which is the fail-open rule this whole file follows.
|
|
166
|
+
const enqueue = (node, path) => {
|
|
167
|
+
if (seen.has(path) || queue.length >= MAX_REGEX_CHECK_NODES)
|
|
168
|
+
return;
|
|
169
|
+
seen.add(path);
|
|
170
|
+
queue.push({ node, path });
|
|
171
|
+
};
|
|
172
|
+
for (let i = 0; i < queue.length; i += 1) {
|
|
173
|
+
const { node, path } = queue[i];
|
|
174
|
+
if (typeof node.pattern === 'string' && !compiles(node.pattern)) {
|
|
175
|
+
return { path: `${path}/pattern`, reason: 'must be a valid regular expression' };
|
|
176
|
+
}
|
|
177
|
+
for (const keyword of ['$ref', '$dynamicRef']) {
|
|
178
|
+
const reference = node[keyword];
|
|
179
|
+
if (typeof reference !== 'string' || !reference.startsWith('#/'))
|
|
180
|
+
continue;
|
|
181
|
+
const target = resolveLocalPointer(schema, reference);
|
|
182
|
+
if (isSchemaObject(target))
|
|
183
|
+
enqueue(target, reference.slice(1));
|
|
184
|
+
}
|
|
185
|
+
for (const keyword of SCHEMA_MAP_KEYWORDS) {
|
|
186
|
+
const map = node[keyword];
|
|
187
|
+
if (!isSchemaObject(map))
|
|
188
|
+
continue;
|
|
189
|
+
for (const [key, value] of Object.entries(map)) {
|
|
190
|
+
if (keyword === 'patternProperties' && !compiles(key)) {
|
|
191
|
+
return { path: `${path}/patternProperties/${pointerPart(key)}`, reason: 'must be a valid regular expression' };
|
|
192
|
+
}
|
|
193
|
+
if (isSchemaObject(value))
|
|
194
|
+
enqueue(value, `${path}/${keyword}/${pointerPart(key)}`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
for (const keyword of SCHEMA_VALUED_KEYWORDS) {
|
|
198
|
+
const value = node[keyword];
|
|
199
|
+
if (Array.isArray(value)) {
|
|
200
|
+
value.forEach((item, index) => {
|
|
201
|
+
if (isSchemaObject(item))
|
|
202
|
+
enqueue(item, `${path}/${keyword}/${index}`);
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
else if (isSchemaObject(value)) {
|
|
206
|
+
enqueue(value, `${path}/${keyword}`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
function isSchemaObject(value) {
|
|
213
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
214
|
+
}
|
|
215
|
+
/** The node a local JSON Pointer (`#/$defs/x`) names in `root`, or undefined. */
|
|
216
|
+
function resolveLocalPointer(root, pointer) {
|
|
217
|
+
let node = root;
|
|
218
|
+
for (const part of pointer.slice(2).split('/')) {
|
|
219
|
+
if (!isSchemaObject(node) && !Array.isArray(node))
|
|
220
|
+
return undefined;
|
|
221
|
+
const key = part.replace(/~1/g, '/').replace(/~0/g, '~');
|
|
222
|
+
// Own members only: `#/__proto__` must name nothing, not `Object.prototype`.
|
|
223
|
+
if (!Object.prototype.hasOwnProperty.call(node, key))
|
|
224
|
+
return undefined;
|
|
225
|
+
node = node[key];
|
|
226
|
+
}
|
|
227
|
+
return node;
|
|
228
|
+
}
|
|
229
|
+
function compiles(pattern) {
|
|
230
|
+
try {
|
|
231
|
+
new RegExp(pattern, 'u');
|
|
232
|
+
return true;
|
|
233
|
+
}
|
|
234
|
+
catch {
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
/** A JSON Pointer path segment: `~` and `/` in a name have to be escaped. */
|
|
239
|
+
function pointerPart(name) {
|
|
240
|
+
return name.replace(/~/g, '~0').replace(/\//g, '~1');
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* The sentence the tool's owner reads, wherever the marker is shown — the tool
|
|
244
|
+
* setup page and `list_tool_setup` say the same thing in the same words,
|
|
245
|
+
* because they are the same finding.
|
|
246
|
+
*/
|
|
247
|
+
export function schemaDefectMarker(defect) {
|
|
248
|
+
return `Hidden from agents: its schema is invalid at ${defect.path} (${defect.reason}).`;
|
|
249
|
+
}
|
|
250
|
+
/** ajv says `must be string`; a sentence a person reads says `must be a string`. */
|
|
251
|
+
function withArticle(message) {
|
|
252
|
+
return message.replace(/^must be (array|boolean|integer|null|number|object|string)$/, (_full, type) => `must be a${/^[aeiou]/.test(type) ? 'n' : ''} ${type}`);
|
|
253
|
+
}
|
|
254
|
+
//# sourceMappingURL=schema-validity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-validity.js","sourceRoot":"","sources":["../src/schema-validity.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A tool's INTERFACE, in the two forms an agent needs it: the one-line call
|
|
3
|
+
* example that opens every description, and the argument list a refusal shows
|
|
4
|
+
* when a call did not match.
|
|
5
|
+
*
|
|
6
|
+
* Both are derived from the tool's own input schema and nothing else. A
|
|
7
|
+
* hand-written example drifts from the schema the moment either changes, and a
|
|
8
|
+
* connected server's tools would have none at all — so there is one generator
|
|
9
|
+
* here, used by the platform's own tools, by a deployment's, and by every
|
|
10
|
+
* connected server's alike.
|
|
11
|
+
*
|
|
12
|
+
* Pure: no IO, no clock, no client. The surfaces that list tools call
|
|
13
|
+
* {@link withCallExample}; the argument check beside it (`call-guards.ts`)
|
|
14
|
+
* calls {@link argumentsDoNotMatchMessage}.
|
|
15
|
+
*/
|
|
16
|
+
/** What every tool description starts with, on a line of its own. */
|
|
17
|
+
export declare const CALL_LINE_PREFIX = "Call: ";
|
|
18
|
+
/** The `kind` an arguments-do-not-match refusal carries, for a client that branches on it. */
|
|
19
|
+
export declare const ARGUMENTS_DO_NOT_MATCH_KIND = "arguments-do-not-match";
|
|
20
|
+
/**
|
|
21
|
+
* The sentence that leads the mismatches when every argument was wrapped in a
|
|
22
|
+
* `body` the tool does not have — the single most common wrong call, because
|
|
23
|
+
* the platform's own tools DO take their arguments that way and a connector
|
|
24
|
+
* tool's arguments are flat.
|
|
25
|
+
*/
|
|
26
|
+
export declare const BODY_AT_TOP_LEVEL_LINE = "This tool takes its arguments at the top level, not under \"body\".";
|
|
27
|
+
/**
|
|
28
|
+
* The mirror of {@link BODY_AT_TOP_LEVEL_LINE}: the same mistake the other way
|
|
29
|
+
* round, by an agent that learned the flat shape and used it on a tool whose
|
|
30
|
+
* arguments ride a `body` envelope. The platform's route-hosted tools take that
|
|
31
|
+
* envelope, and the arguments of a flat call reach their route as query
|
|
32
|
+
* parameters instead of a body — which is how the route tells this apart from a
|
|
33
|
+
* call that simply left everything out, and can say which it was.
|
|
34
|
+
*/
|
|
35
|
+
export declare const ARGS_UNDER_BODY_LINE = "This tool takes its arguments under \"body\", not at the top level.";
|
|
36
|
+
type Dict = Record<string, unknown>;
|
|
37
|
+
/**
|
|
38
|
+
* The arguments the call example passes: one placeholder per REQUIRED
|
|
39
|
+
* argument, in the order the schema requires them, and nothing else.
|
|
40
|
+
*
|
|
41
|
+
* This is the example as a VALUE, which is what makes the example testable —
|
|
42
|
+
* every tool the platform declares is checked with it against its own schema,
|
|
43
|
+
* so an example an agent copies is one the check accepts.
|
|
44
|
+
*/
|
|
45
|
+
export declare function exampleArguments(inputs: unknown, depth?: number): Dict;
|
|
46
|
+
/**
|
|
47
|
+
* The call example for one tool: the namespace this connection exposes, the
|
|
48
|
+
* tool's name, and its required arguments with a placeholder each, in the shape
|
|
49
|
+
* the tool really takes.
|
|
50
|
+
*
|
|
51
|
+
* `utcpName` is the tool's registered UTCP name (`<manual>.<tool>`); the
|
|
52
|
+
* namespace and name come from the same mapping the chain runtime uses, so the
|
|
53
|
+
* example is literally callable inside `call_tool_chain`. Optional arguments
|
|
54
|
+
* are left out — the example is the shortest call that can work, not a catalog.
|
|
55
|
+
*/
|
|
56
|
+
export declare function callExample(utcpName: string, inputs: unknown): string;
|
|
57
|
+
/** The `Call:` line as it appears at the top of a description. */
|
|
58
|
+
export declare function callLine(utcpName: string, inputs: unknown): string;
|
|
59
|
+
/**
|
|
60
|
+
* A description with its call example ahead of it. Idempotent: a description
|
|
61
|
+
* that already opens with a `Call:` line keeps the one it has, so a surface
|
|
62
|
+
* that lists the same tool through two layers cannot stack two examples.
|
|
63
|
+
*/
|
|
64
|
+
export declare function withCallExample(description: string | undefined, utcpName: string, inputs: unknown): string;
|
|
65
|
+
/**
|
|
66
|
+
* Split a description into its `Call:` line and the rest, so a caller that
|
|
67
|
+
* prepends text of its own (the knowledge-base tools' purpose prefix) can keep
|
|
68
|
+
* the example first — the line only does its job if it is the first thing read.
|
|
69
|
+
*/
|
|
70
|
+
export declare function splitCallLine(description: string): {
|
|
71
|
+
call: string | null;
|
|
72
|
+
rest: string;
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* The tool's interface as lines: every argument with its type, whether it is
|
|
76
|
+
* required, and its description — the top level, then one level below it,
|
|
77
|
+
* indented. This is what an agent needs in order to correct its call, and it
|
|
78
|
+
* is the schema's own content, never prose about it.
|
|
79
|
+
*/
|
|
80
|
+
export declare function describeInterface(inputs: unknown, depth?: number, indent?: string): string[];
|
|
81
|
+
/**
|
|
82
|
+
* The whole refusal: one sentence that the arguments do not match, the
|
|
83
|
+
* mismatches one per line, the interface, and the call example last — the order
|
|
84
|
+
* an agent reads it in, ending with the line it can copy.
|
|
85
|
+
*/
|
|
86
|
+
export declare function argumentsDoNotMatchMessage(toolName: string, utcpName: string, inputs: unknown, mismatches: string[], options?: {
|
|
87
|
+
/**
|
|
88
|
+
* The schema the CALL EXAMPLE is generated from, when that is not the
|
|
89
|
+
* schema the arguments were checked against. A route-hosted tool is checked
|
|
90
|
+
* against its FLAT arguments — the ones its handler receives, and so the
|
|
91
|
+
* ones the mismatch lines and the interface name — while the example still
|
|
92
|
+
* has to show the `{ body: { … } }` envelope an agent actually types.
|
|
93
|
+
* Default: one schema for both.
|
|
94
|
+
*/
|
|
95
|
+
exampleInputs?: unknown;
|
|
96
|
+
}): string;
|
|
97
|
+
/**
|
|
98
|
+
* A compiled check for one input schema: either the rules to check a call
|
|
99
|
+
* against, or the reason this schema cannot be used for checking.
|
|
100
|
+
*
|
|
101
|
+
* Compiled once per distinct schema and kept (see {@link checkFor}), so the
|
|
102
|
+
* check costs a walk of the arguments and nothing else per call.
|
|
103
|
+
*/
|
|
104
|
+
export type CompiledCheck = {
|
|
105
|
+
checkable: false;
|
|
106
|
+
reason: string;
|
|
107
|
+
} | {
|
|
108
|
+
checkable: true;
|
|
109
|
+
check: (args: Dict) => string[];
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* Compile one input schema into a check, or decide it cannot be checked.
|
|
113
|
+
*
|
|
114
|
+
* Deliberately conservative: anything this module cannot reason about with
|
|
115
|
+
* certainty — a combinator, a `$ref`, a schema that is not an object — switches
|
|
116
|
+
* the check OFF rather than guessing. A checker that refuses valid calls would
|
|
117
|
+
* take tools away from every agent at once, so every rule here is one a call
|
|
118
|
+
* cannot satisfy by any reading of the schema.
|
|
119
|
+
*/
|
|
120
|
+
export declare function compileCheck(inputs: unknown, depth?: number): CompiledCheck;
|
|
121
|
+
/**
|
|
122
|
+
* Could this pattern backtrack catastrophically? True for a group that repeats
|
|
123
|
+
* (`*`, `+`, `{…}`) and itself contains a quantifier or an
|
|
124
|
+
* alternation — `(a+)+`, `(a|a)*`, `(\w+\s?)*` — and for a backreference.
|
|
125
|
+
*
|
|
126
|
+
* JavaScript's matcher backtracks, so such a pattern, which comes from a
|
|
127
|
+
* schema someone else wrote, could hold the event loop for seconds on one
|
|
128
|
+
* caller's string. The test is a conservative over-approximation (it flags
|
|
129
|
+
* some patterns that would in fact be fast); a flagged pattern is simply not
|
|
130
|
+
* asserted, which is what this module does with any keyword it cannot judge
|
|
131
|
+
* safely.
|
|
132
|
+
*/
|
|
133
|
+
export declare function patternMayBacktrack(source: string): boolean;
|
|
134
|
+
/** The compiled check for one input schema, compiled at most once per schema. */
|
|
135
|
+
export declare function checkFor(inputs: unknown): CompiledCheck;
|
|
136
|
+
export {};
|
|
137
|
+
//# sourceMappingURL=tool-interface.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-interface.d.ts","sourceRoot":"","sources":["../src/tool-interface.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;GAcG;AAEH,qEAAqE;AACrE,eAAO,MAAM,gBAAgB,WAAW,CAAC;AAEzC,8FAA8F;AAC9F,eAAO,MAAM,2BAA2B,2BAA2B,CAAC;AAEpE;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,wEAAsE,CAAC;AAE1G;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,wEAAsE,CAAC;AAqBxG,KAAK,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAqGpC;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,SAAI,GAAG,IAAI,CAiBjE;AA4BD;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,CAErE;AAED,kEAAkE;AAClE,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,CAElE;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,CAI1G;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAKxF;AAYD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,SAAI,EAAE,MAAM,SAAK,GAAG,MAAM,EAAE,CAmBnF;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,OAAO,EACf,UAAU,EAAE,MAAM,EAAE,EACpB,OAAO,GAAE;IACP;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACpB,GACL,MAAM,CASR;AAED;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GACrB;IAAE,SAAS,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,SAAS,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,EAAE,CAAA;CAAE,CAAC;AAkBzD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,SAAI,GAAG,aAAa,CAmFtE;AAmHD;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CA6C3D;AA+CD,iFAAiF;AACjF,wBAAgB,QAAQ,CAAC,MAAM,EAAE,OAAO,GAAG,aAAa,CAOvD"}
|