@decocms/bindings 1.0.1-alpha.4 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/package.json +8 -11
- package/src/core/binder.ts +15 -76
- package/src/core/client/index.ts +10 -0
- package/src/core/client/mcp-client.ts +18 -5
- package/src/core/client/mcp.ts +48 -11
- package/src/core/client/proxy.ts +64 -48
- package/src/index.ts +57 -0
- package/src/well-known/assistant.ts +87 -0
- package/src/well-known/collections.ts +84 -99
- package/src/well-known/event-bus.ts +454 -0
- package/src/well-known/event-subscriber.ts +259 -0
- package/src/well-known/language-model.ts +217 -5
- package/src/well-known/mcp.ts +2 -1
- package/src/well-known/prompt.ts +110 -0
- package/src/well-known/registry.ts +128 -0
- package/src/well-known/workflow.ts +297 -0
- package/test/index.test.ts +3 -2
- package/test/mcp.test.ts +1 -1
- package/src/core/subset.ts +0 -514
- package/src/well-known/agent.ts +0 -60
- package/vitest.config.ts +0 -8
package/src/core/subset.ts
DELETED
|
@@ -1,514 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Structural JSON Schema Subset Check
|
|
3
|
-
*
|
|
4
|
-
* This module implements an algorithm to determine if one JSON Schema (A)
|
|
5
|
-
* is a subset of another (B). A ⊆ B means every value valid under A is also
|
|
6
|
-
* valid under B (A is more restrictive or equally restrictive).
|
|
7
|
-
*
|
|
8
|
-
* Core Axiom: A ⊆ B ⟺ Constraints(A) ⊇ Constraints(B)
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
12
|
-
type JSONSchema = Record<string, any>;
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Deep equality check for JSON values (for const/enum comparison)
|
|
16
|
-
*/
|
|
17
|
-
function deepEqual(x: unknown, y: unknown): boolean {
|
|
18
|
-
if (x === y) return true;
|
|
19
|
-
if (typeof x !== typeof y) return false;
|
|
20
|
-
if (x === null || y === null) return x === y;
|
|
21
|
-
if (typeof x !== "object") return false;
|
|
22
|
-
|
|
23
|
-
if (Array.isArray(x)) {
|
|
24
|
-
if (!Array.isArray(y)) return false;
|
|
25
|
-
if (x.length !== y.length) return false;
|
|
26
|
-
for (let i = 0; i < x.length; i++) {
|
|
27
|
-
if (!deepEqual(x[i], y[i])) return false;
|
|
28
|
-
}
|
|
29
|
-
return true;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (Array.isArray(y)) return false;
|
|
33
|
-
|
|
34
|
-
const xObj = x as Record<string, unknown>;
|
|
35
|
-
const yObj = y as Record<string, unknown>;
|
|
36
|
-
const xKeys = Object.keys(xObj);
|
|
37
|
-
const yKeys = Object.keys(yObj);
|
|
38
|
-
|
|
39
|
-
if (xKeys.length !== yKeys.length) return false;
|
|
40
|
-
|
|
41
|
-
for (const key of xKeys) {
|
|
42
|
-
if (!Object.prototype.hasOwnProperty.call(yObj, key)) return false;
|
|
43
|
-
if (!deepEqual(xObj[key], yObj[key])) return false;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return true;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Phase 1: Normalization
|
|
51
|
-
* Convert syntactic sugar to canonical form
|
|
52
|
-
*/
|
|
53
|
-
function normalize(schema: JSONSchema | boolean): JSONSchema {
|
|
54
|
-
// Boolean schemas
|
|
55
|
-
if (schema === true) return {};
|
|
56
|
-
if (schema === false) return { not: {} };
|
|
57
|
-
|
|
58
|
-
// Already an object
|
|
59
|
-
const s = { ...schema };
|
|
60
|
-
|
|
61
|
-
// Type arrays -> anyOf
|
|
62
|
-
if (Array.isArray(s.type)) {
|
|
63
|
-
const types = s.type as string[];
|
|
64
|
-
if (types.length === 1) {
|
|
65
|
-
s.type = types[0];
|
|
66
|
-
} else {
|
|
67
|
-
const { type: _type, ...rest } = s;
|
|
68
|
-
return {
|
|
69
|
-
anyOf: types.map((t) => normalize({ ...rest, type: t })),
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Integer is number with multipleOf: 1
|
|
75
|
-
if (s.type === "integer") {
|
|
76
|
-
s.type = "number";
|
|
77
|
-
if (s.multipleOf === undefined) {
|
|
78
|
-
s.multipleOf = 1;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return s;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Check if set A is a subset of set B (for required fields)
|
|
87
|
-
*/
|
|
88
|
-
function isSetSubset(a: string[], b: string[]): boolean {
|
|
89
|
-
const setB = new Set(b);
|
|
90
|
-
return a.every((item) => setB.has(item));
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Get effective minimum value from schema
|
|
95
|
-
*/
|
|
96
|
-
function getEffectiveMin(schema: JSONSchema): number {
|
|
97
|
-
const min = schema.minimum ?? -Infinity;
|
|
98
|
-
const exMin = schema.exclusiveMinimum;
|
|
99
|
-
|
|
100
|
-
if (typeof exMin === "number") {
|
|
101
|
-
return Math.max(min, exMin);
|
|
102
|
-
}
|
|
103
|
-
if (exMin === true && typeof schema.minimum === "number") {
|
|
104
|
-
return schema.minimum;
|
|
105
|
-
}
|
|
106
|
-
return min;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Get effective maximum value from schema
|
|
111
|
-
*/
|
|
112
|
-
function getEffectiveMax(schema: JSONSchema): number {
|
|
113
|
-
const max = schema.maximum ?? Infinity;
|
|
114
|
-
const exMax = schema.exclusiveMaximum;
|
|
115
|
-
|
|
116
|
-
if (typeof exMax === "number") {
|
|
117
|
-
return Math.min(max, exMax);
|
|
118
|
-
}
|
|
119
|
-
if (exMax === true && typeof schema.maximum === "number") {
|
|
120
|
-
return schema.maximum;
|
|
121
|
-
}
|
|
122
|
-
return max;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Check if multipleOfA is a multiple of multipleOfB
|
|
127
|
-
* (i.e., A's constraint is tighter)
|
|
128
|
-
*/
|
|
129
|
-
function isMultipleOf(a: number, b: number): boolean {
|
|
130
|
-
if (b === 0) return false;
|
|
131
|
-
// Handle floating point precision
|
|
132
|
-
const ratio = a / b;
|
|
133
|
-
return Math.abs(ratio - Math.round(ratio)) < 1e-10;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Check if A's enum values are all valid in B
|
|
138
|
-
*/
|
|
139
|
-
function isEnumSubset(enumA: unknown[], schemaB: JSONSchema): boolean {
|
|
140
|
-
// If B has enum, check set inclusion
|
|
141
|
-
if (schemaB.enum) {
|
|
142
|
-
return enumA.every((val) =>
|
|
143
|
-
schemaB.enum.some((bVal: unknown) => deepEqual(val, bVal)),
|
|
144
|
-
);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// If B has const, check if A's enum only contains that value
|
|
148
|
-
if (schemaB.const !== undefined) {
|
|
149
|
-
return enumA.length === 1 && deepEqual(enumA[0], schemaB.const);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// Otherwise, enum values must match B's type constraints
|
|
153
|
-
// This is a simplified check - full validation would require more
|
|
154
|
-
return true;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Main subset check: isSubset(A, B)
|
|
159
|
-
* Returns true if A ⊆ B (every value valid under A is valid under B)
|
|
160
|
-
*/
|
|
161
|
-
export function isSubset(
|
|
162
|
-
schemaA: JSONSchema | boolean,
|
|
163
|
-
schemaB: JSONSchema | boolean,
|
|
164
|
-
): boolean {
|
|
165
|
-
// Phase 1: Normalize
|
|
166
|
-
const a = normalize(schemaA);
|
|
167
|
-
const b = normalize(schemaB);
|
|
168
|
-
|
|
169
|
-
// Phase 2: Meta Logic - Universal Terminators
|
|
170
|
-
|
|
171
|
-
// If B is {} (Any), everything is a subset
|
|
172
|
-
if (Object.keys(b).length === 0) return true;
|
|
173
|
-
|
|
174
|
-
// If A is false (Never), empty set is subset of everything
|
|
175
|
-
if (a.not && Object.keys(a.not).length === 0) return true;
|
|
176
|
-
|
|
177
|
-
// Deep equality check
|
|
178
|
-
if (deepEqual(a, b)) return true;
|
|
179
|
-
|
|
180
|
-
// Phase 2: Unions and Intersections
|
|
181
|
-
|
|
182
|
-
// Left-side union (anyOf in A): all options must fit in B
|
|
183
|
-
if (a.anyOf) {
|
|
184
|
-
return (a.anyOf as JSONSchema[]).every((optA) => isSubset(optA, b));
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// Left-side union (oneOf in A): all options must fit in B
|
|
188
|
-
if (a.oneOf) {
|
|
189
|
-
return (a.oneOf as JSONSchema[]).every((optA) => isSubset(optA, b));
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// Right-side union (anyOf in B): A must fit in at least one option
|
|
193
|
-
if (b.anyOf) {
|
|
194
|
-
return (b.anyOf as JSONSchema[]).some((optB) => isSubset(a, optB));
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// Right-side union (oneOf in B): A must fit in at least one option
|
|
198
|
-
if (b.oneOf) {
|
|
199
|
-
return (b.oneOf as JSONSchema[]).some((optB) => isSubset(a, optB));
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// Right-side intersection (allOf in B): A must satisfy all
|
|
203
|
-
if (b.allOf) {
|
|
204
|
-
return (b.allOf as JSONSchema[]).every((optB) => isSubset(a, optB));
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// Left-side intersection (allOf in A): merge and compare
|
|
208
|
-
if (a.allOf) {
|
|
209
|
-
// Simplified: check if any single branch satisfies B
|
|
210
|
-
return (a.allOf as JSONSchema[]).some((optA) => isSubset(optA, b));
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// Phase 3: Type-specific logic
|
|
214
|
-
|
|
215
|
-
// Handle const in A
|
|
216
|
-
if (a.const !== undefined) {
|
|
217
|
-
if (b.const !== undefined) {
|
|
218
|
-
return deepEqual(a.const, b.const);
|
|
219
|
-
}
|
|
220
|
-
if (b.enum) {
|
|
221
|
-
return b.enum.some((v: unknown) => deepEqual(a.const, v));
|
|
222
|
-
}
|
|
223
|
-
// const must match B's type constraints
|
|
224
|
-
return isValueValidForType(a.const, b);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// Handle enum in A
|
|
228
|
-
if (a.enum) {
|
|
229
|
-
return isEnumSubset(a.enum, b);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// Type mismatch check
|
|
233
|
-
if (a.type && b.type && a.type !== b.type) {
|
|
234
|
-
return false;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// If B has a type but A doesn't, A might allow more types
|
|
238
|
-
if (b.type && !a.type) {
|
|
239
|
-
// A is more permissive (no type restriction) so it's not a subset
|
|
240
|
-
// unless A has other constraints that limit it
|
|
241
|
-
if (!a.enum && a.const === undefined) {
|
|
242
|
-
return false;
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
const type = a.type || b.type;
|
|
247
|
-
|
|
248
|
-
switch (type) {
|
|
249
|
-
case "object":
|
|
250
|
-
return isObjectSubset(a, b);
|
|
251
|
-
case "array":
|
|
252
|
-
return isArraySubset(a, b);
|
|
253
|
-
case "number":
|
|
254
|
-
return isNumberSubset(a, b);
|
|
255
|
-
case "string":
|
|
256
|
-
return isStringSubset(a, b);
|
|
257
|
-
case "boolean":
|
|
258
|
-
case "null":
|
|
259
|
-
// These types have no additional constraints
|
|
260
|
-
return true;
|
|
261
|
-
default:
|
|
262
|
-
// Unknown type or no type specified
|
|
263
|
-
return true;
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/**
|
|
268
|
-
* Check if a value would be valid for a schema's type
|
|
269
|
-
*/
|
|
270
|
-
function isValueValidForType(value: unknown, schema: JSONSchema): boolean {
|
|
271
|
-
if (!schema.type) return true;
|
|
272
|
-
|
|
273
|
-
const valueType = typeof value;
|
|
274
|
-
switch (schema.type) {
|
|
275
|
-
case "string":
|
|
276
|
-
return valueType === "string";
|
|
277
|
-
case "number":
|
|
278
|
-
return valueType === "number";
|
|
279
|
-
case "boolean":
|
|
280
|
-
return valueType === "boolean";
|
|
281
|
-
case "null":
|
|
282
|
-
return value === null;
|
|
283
|
-
case "object":
|
|
284
|
-
return valueType === "object" && value !== null && !Array.isArray(value);
|
|
285
|
-
case "array":
|
|
286
|
-
return Array.isArray(value);
|
|
287
|
-
default:
|
|
288
|
-
return true;
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
/**
|
|
293
|
-
* Object subset check
|
|
294
|
-
*/
|
|
295
|
-
function isObjectSubset(a: JSONSchema, b: JSONSchema): boolean {
|
|
296
|
-
// Required keys: A must require at least everything B requires
|
|
297
|
-
const aRequired = (a.required as string[]) || [];
|
|
298
|
-
const bRequired = (b.required as string[]) || [];
|
|
299
|
-
|
|
300
|
-
if (!isSetSubset(bRequired, aRequired)) {
|
|
301
|
-
return false;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// Property compatibility
|
|
305
|
-
const aProps = (a.properties as Record<string, JSONSchema>) || {};
|
|
306
|
-
const bProps = (b.properties as Record<string, JSONSchema>) || {};
|
|
307
|
-
|
|
308
|
-
// Check all properties defined in B
|
|
309
|
-
for (const key of Object.keys(bProps)) {
|
|
310
|
-
if (key in aProps) {
|
|
311
|
-
// Both have the property, check recursively
|
|
312
|
-
if (!isSubset(aProps[key], bProps[key])) {
|
|
313
|
-
return false;
|
|
314
|
-
}
|
|
315
|
-
} else {
|
|
316
|
-
// Property missing in A
|
|
317
|
-
// If A is closed (additionalProperties: false), A won't produce this key
|
|
318
|
-
// which means A values won't have this property, potentially violating B if B requires it
|
|
319
|
-
if (a.additionalProperties === false) {
|
|
320
|
-
// A is closed and doesn't have this property
|
|
321
|
-
// If B requires this property, A can't satisfy it
|
|
322
|
-
if (bRequired.includes(key)) {
|
|
323
|
-
return false;
|
|
324
|
-
}
|
|
325
|
-
// Otherwise, A just won't have this optional property, which is fine
|
|
326
|
-
} else {
|
|
327
|
-
// A is open, check if additionalProperties schema satisfies B's property
|
|
328
|
-
const aAdditional = a.additionalProperties;
|
|
329
|
-
if (aAdditional && typeof aAdditional === "object") {
|
|
330
|
-
if (!isSubset(aAdditional, bProps[key])) {
|
|
331
|
-
return false;
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
// If additionalProperties is true or undefined, any value is allowed
|
|
335
|
-
// which might not satisfy B's property schema
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// Check all properties defined in A (that A requires)
|
|
341
|
-
// If A requires a property, B must also define it (or have compatible additionalProperties)
|
|
342
|
-
for (const key of aRequired) {
|
|
343
|
-
if (key in aProps && !(key in bProps)) {
|
|
344
|
-
// A requires and defines this property, but B doesn't define it
|
|
345
|
-
// B must have additionalProperties that accepts A's property schema
|
|
346
|
-
if (b.additionalProperties === false) {
|
|
347
|
-
// B doesn't allow additional properties, so A's required property would be rejected
|
|
348
|
-
return false;
|
|
349
|
-
} else if (
|
|
350
|
-
b.additionalProperties &&
|
|
351
|
-
typeof b.additionalProperties === "object"
|
|
352
|
-
) {
|
|
353
|
-
// B has a schema for additional properties, check compatibility
|
|
354
|
-
if (!isSubset(aProps[key], b.additionalProperties)) {
|
|
355
|
-
return false;
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
// If B's additionalProperties is true or undefined, any value is allowed
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
// Additional properties constraint
|
|
363
|
-
if (b.additionalProperties === false) {
|
|
364
|
-
// B is closed, A must also be closed or not have extra properties
|
|
365
|
-
const aHasExtraProps = Object.keys(aProps).some((key) => !(key in bProps));
|
|
366
|
-
if (aHasExtraProps) {
|
|
367
|
-
return false;
|
|
368
|
-
}
|
|
369
|
-
// If A is open and B is closed, A could produce extra properties
|
|
370
|
-
if (a.additionalProperties !== false) {
|
|
371
|
-
return false;
|
|
372
|
-
}
|
|
373
|
-
} else if (
|
|
374
|
-
b.additionalProperties &&
|
|
375
|
-
typeof b.additionalProperties === "object"
|
|
376
|
-
) {
|
|
377
|
-
// B has a schema for additional properties
|
|
378
|
-
const aAdditional = a.additionalProperties;
|
|
379
|
-
if (aAdditional && typeof aAdditional === "object") {
|
|
380
|
-
if (!isSubset(aAdditional, b.additionalProperties)) {
|
|
381
|
-
return false;
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
return true;
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
/**
|
|
390
|
-
* Array subset check
|
|
391
|
-
*/
|
|
392
|
-
function isArraySubset(a: JSONSchema, b: JSONSchema): boolean {
|
|
393
|
-
// Items schema
|
|
394
|
-
if (a.items && b.items) {
|
|
395
|
-
if (Array.isArray(a.items) && Array.isArray(b.items)) {
|
|
396
|
-
// Both are tuples
|
|
397
|
-
if (a.items.length !== b.items.length) {
|
|
398
|
-
return false;
|
|
399
|
-
}
|
|
400
|
-
for (let i = 0; i < a.items.length; i++) {
|
|
401
|
-
if (!isSubset(a.items[i], b.items[i])) {
|
|
402
|
-
return false;
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
} else if (Array.isArray(a.items) && !Array.isArray(b.items)) {
|
|
406
|
-
// A is tuple, B is list
|
|
407
|
-
for (const itemSchema of a.items) {
|
|
408
|
-
if (!isSubset(itemSchema, b.items)) {
|
|
409
|
-
return false;
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
} else if (!Array.isArray(a.items) && Array.isArray(b.items)) {
|
|
413
|
-
// A is list, B is tuple - A is more permissive
|
|
414
|
-
return false;
|
|
415
|
-
} else {
|
|
416
|
-
// Both are lists
|
|
417
|
-
if (!isSubset(a.items, b.items)) {
|
|
418
|
-
return false;
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
} else if (b.items && !a.items) {
|
|
422
|
-
// B has items constraint but A doesn't
|
|
423
|
-
return false;
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
// Length constraints
|
|
427
|
-
const aMinItems = a.minItems ?? 0;
|
|
428
|
-
const bMinItems = b.minItems ?? 0;
|
|
429
|
-
if (aMinItems < bMinItems) {
|
|
430
|
-
return false;
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
const aMaxItems = a.maxItems ?? Infinity;
|
|
434
|
-
const bMaxItems = b.maxItems ?? Infinity;
|
|
435
|
-
if (aMaxItems > bMaxItems) {
|
|
436
|
-
return false;
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
// Uniqueness
|
|
440
|
-
if (b.uniqueItems && !a.uniqueItems) {
|
|
441
|
-
return false;
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
return true;
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
/**
|
|
448
|
-
* Number subset check
|
|
449
|
-
*/
|
|
450
|
-
function isNumberSubset(a: JSONSchema, b: JSONSchema): boolean {
|
|
451
|
-
// Minimum
|
|
452
|
-
const aMin = getEffectiveMin(a);
|
|
453
|
-
const bMin = getEffectiveMin(b);
|
|
454
|
-
if (aMin < bMin) {
|
|
455
|
-
return false;
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
// Maximum
|
|
459
|
-
const aMax = getEffectiveMax(a);
|
|
460
|
-
const bMax = getEffectiveMax(b);
|
|
461
|
-
if (aMax > bMax) {
|
|
462
|
-
return false;
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
// MultipleOf
|
|
466
|
-
if (b.multipleOf !== undefined) {
|
|
467
|
-
if (a.multipleOf === undefined) {
|
|
468
|
-
// A doesn't have multipleOf constraint, so it's more permissive
|
|
469
|
-
return false;
|
|
470
|
-
}
|
|
471
|
-
if (!isMultipleOf(a.multipleOf, b.multipleOf)) {
|
|
472
|
-
return false;
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
return true;
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
/**
|
|
480
|
-
* String subset check
|
|
481
|
-
*/
|
|
482
|
-
function isStringSubset(a: JSONSchema, b: JSONSchema): boolean {
|
|
483
|
-
// Length constraints
|
|
484
|
-
const aMinLength = a.minLength ?? 0;
|
|
485
|
-
const bMinLength = b.minLength ?? 0;
|
|
486
|
-
if (aMinLength < bMinLength) {
|
|
487
|
-
return false;
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
const aMaxLength = a.maxLength ?? Infinity;
|
|
491
|
-
const bMaxLength = b.maxLength ?? Infinity;
|
|
492
|
-
if (aMaxLength > bMaxLength) {
|
|
493
|
-
return false;
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
// Pattern (regex)
|
|
497
|
-
if (b.pattern) {
|
|
498
|
-
if (!a.pattern) {
|
|
499
|
-
// A has no pattern constraint, more permissive
|
|
500
|
-
return false;
|
|
501
|
-
}
|
|
502
|
-
// Exact match only (full regex subset check is computationally expensive)
|
|
503
|
-
if (a.pattern !== b.pattern) {
|
|
504
|
-
return false;
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
// Format (treat as informational, exact match required)
|
|
509
|
-
if (b.format && a.format !== b.format) {
|
|
510
|
-
return false;
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
return true;
|
|
514
|
-
}
|
package/src/well-known/agent.ts
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Agents Well-Known Binding
|
|
3
|
-
*
|
|
4
|
-
* Defines the interface for AI agent providers.
|
|
5
|
-
* Any MCP that implements this binding can provide configurable AI agents
|
|
6
|
-
* with custom instructions and tool access controls.
|
|
7
|
-
*
|
|
8
|
-
* This binding uses collection bindings for LIST and GET operations (read-only).
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { z } from "zod";
|
|
12
|
-
import type { Binder } from "../core/binder";
|
|
13
|
-
import {
|
|
14
|
-
BaseCollectionEntitySchema,
|
|
15
|
-
createCollectionBindings,
|
|
16
|
-
} from "./collections";
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Agent entity schema for AI agents
|
|
20
|
-
* Extends BaseCollectionEntitySchema with agent-specific fields
|
|
21
|
-
* Base schema already includes: id, title, created_at, updated_at, created_by, updated_by
|
|
22
|
-
*/
|
|
23
|
-
export const AgentSchema = BaseCollectionEntitySchema.extend({
|
|
24
|
-
// Agent-specific fields
|
|
25
|
-
description: z.string().describe("Brief description of the agent's purpose"),
|
|
26
|
-
instructions: z
|
|
27
|
-
.string()
|
|
28
|
-
.describe("System instructions that define the agent's behavior"),
|
|
29
|
-
tool_set: z
|
|
30
|
-
.record(z.string(), z.array(z.string()))
|
|
31
|
-
.describe(
|
|
32
|
-
"Map of connection IDs to arrays of allowed tool names for this agent",
|
|
33
|
-
),
|
|
34
|
-
avatar: z.string().url().describe("URL to the agent's avatar image"),
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* AGENT Collection Binding
|
|
39
|
-
*
|
|
40
|
-
* Collection bindings for agents (read-only).
|
|
41
|
-
* Provides LIST and GET operations for AI agents.
|
|
42
|
-
*/
|
|
43
|
-
export const AGENTS_COLLECTION_BINDING = createCollectionBindings(
|
|
44
|
-
"agent",
|
|
45
|
-
AgentSchema,
|
|
46
|
-
);
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* AGENTS Binding
|
|
50
|
-
*
|
|
51
|
-
* Defines the interface for AI agent providers.
|
|
52
|
-
* Any MCP that implements this binding can provide configurable AI agents.
|
|
53
|
-
*
|
|
54
|
-
* Required tools:
|
|
55
|
-
* - COLLECTION_AGENT_LIST: List available AI agents with their configurations
|
|
56
|
-
* - COLLECTION_AGENT_GET: Get a single agent by ID (includes instructions and tool_set)
|
|
57
|
-
*/
|
|
58
|
-
export const AGENTS_BINDING = [
|
|
59
|
-
...AGENTS_COLLECTION_BINDING,
|
|
60
|
-
] as const satisfies Binder;
|