@decocms/bindings 1.0.1-alpha.3 → 1.0.1-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -1
- package/src/core/binder.ts +4 -5
- package/src/well-known/collections.ts +77 -41
- package/src/well-known/mcp.ts +35 -0
- package/test/mcp.test.ts +40 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decocms/bindings",
|
|
3
|
-
"version": "1.0.1-alpha.
|
|
3
|
+
"version": "1.0.1-alpha.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "vitest run",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"./llm": "./src/well-known/language-model.ts",
|
|
20
20
|
"./connection": "./src/core/connection.ts",
|
|
21
21
|
"./client": "./src/core/client/index.ts",
|
|
22
|
+
"./mcp": "./src/well-known/mcp.ts",
|
|
22
23
|
"./agent": "./src/well-known/agent.ts"
|
|
23
24
|
},
|
|
24
25
|
"devDependencies": {
|
package/src/core/binder.ts
CHANGED
|
@@ -16,8 +16,7 @@ type JsonSchema = Record<string, unknown>;
|
|
|
16
16
|
/**
|
|
17
17
|
* Checks if a value is a Zod schema by looking for the _def property
|
|
18
18
|
*/
|
|
19
|
-
|
|
20
|
-
function isZodSchema(value: unknown): value is ZodType<any> {
|
|
19
|
+
function isZodSchema(value: unknown): value is ZodType<unknown> {
|
|
21
20
|
return (
|
|
22
21
|
value !== null &&
|
|
23
22
|
typeof value === "object" &&
|
|
@@ -34,15 +33,15 @@ function isZodSchema(value: unknown): value is ZodType<any> {
|
|
|
34
33
|
* @returns The JSON schema representation, or null if input is null/undefined
|
|
35
34
|
*/
|
|
36
35
|
export function normalizeToJsonSchema(
|
|
37
|
-
|
|
38
|
-
schema: ZodType<any> | JsonSchema | null | undefined,
|
|
36
|
+
schema: ZodType<unknown> | JsonSchema | null | undefined,
|
|
39
37
|
): JsonSchema | null {
|
|
40
38
|
if (schema == null) {
|
|
41
39
|
return null;
|
|
42
40
|
}
|
|
43
41
|
|
|
44
42
|
if (isZodSchema(schema)) {
|
|
45
|
-
|
|
43
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
44
|
+
return zodToJsonSchema(schema as any) as JsonSchema;
|
|
46
45
|
}
|
|
47
46
|
|
|
48
47
|
// Already a JSON schema
|
|
@@ -261,45 +261,71 @@ export function createCollectionBindings<
|
|
|
261
261
|
const upperName = collectionName.toUpperCase() as Uppercase<TName>;
|
|
262
262
|
const readOnly = options?.readOnly ?? false;
|
|
263
263
|
|
|
264
|
-
const bindings:
|
|
265
|
-
|
|
266
|
-
{
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
264
|
+
const bindings: ToolBinder[] = [
|
|
265
|
+
{
|
|
266
|
+
name: `COLLECTION_${upperName}_LIST` as const,
|
|
267
|
+
inputSchema: CollectionListInputSchema,
|
|
268
|
+
outputSchema: createCollectionListOutputSchema(entitySchema) as z.ZodType<
|
|
269
|
+
CollectionListOutput<z.infer<TEntitySchema>>
|
|
270
|
+
>,
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
name: `COLLECTION_${upperName}_GET` as const,
|
|
274
|
+
inputSchema: CollectionGetInputSchema,
|
|
275
|
+
outputSchema: createCollectionGetOutputSchema(entitySchema) as z.ZodType<
|
|
276
|
+
CollectionGetOutput<z.infer<TEntitySchema>>
|
|
277
|
+
>,
|
|
278
|
+
},
|
|
279
|
+
];
|
|
277
280
|
|
|
278
281
|
// Only include mutation operations if not read-only
|
|
279
282
|
if (!readOnly) {
|
|
280
283
|
bindings.push(
|
|
281
284
|
{
|
|
282
285
|
name: `COLLECTION_${upperName}_CREATE` as const,
|
|
283
|
-
inputSchema: createCollectionInsertInputSchema(
|
|
284
|
-
|
|
286
|
+
inputSchema: createCollectionInsertInputSchema(
|
|
287
|
+
entitySchema,
|
|
288
|
+
) as unknown as z.ZodType<
|
|
289
|
+
CollectionInsertInput<z.infer<TEntitySchema>>
|
|
290
|
+
>,
|
|
291
|
+
outputSchema: createCollectionInsertOutputSchema(
|
|
292
|
+
entitySchema,
|
|
293
|
+
) as unknown as z.ZodType<
|
|
294
|
+
CollectionInsertOutput<z.infer<TEntitySchema>>
|
|
295
|
+
>,
|
|
285
296
|
opt: true,
|
|
286
297
|
},
|
|
287
298
|
{
|
|
288
299
|
name: `COLLECTION_${upperName}_UPDATE` as const,
|
|
289
|
-
inputSchema: createCollectionUpdateInputSchema(
|
|
290
|
-
|
|
300
|
+
inputSchema: createCollectionUpdateInputSchema(
|
|
301
|
+
entitySchema,
|
|
302
|
+
) as unknown as z.ZodType<
|
|
303
|
+
CollectionUpdateInput<z.infer<TEntitySchema>>
|
|
304
|
+
>,
|
|
305
|
+
outputSchema: createCollectionUpdateOutputSchema(
|
|
306
|
+
entitySchema,
|
|
307
|
+
) as unknown as z.ZodType<
|
|
308
|
+
CollectionUpdateOutput<z.infer<TEntitySchema>>
|
|
309
|
+
>,
|
|
291
310
|
opt: true,
|
|
292
311
|
},
|
|
293
312
|
{
|
|
294
313
|
name: `COLLECTION_${upperName}_DELETE` as const,
|
|
295
314
|
inputSchema: CollectionDeleteInputSchema,
|
|
296
|
-
outputSchema: createCollectionDeleteOutputSchema(
|
|
315
|
+
outputSchema: createCollectionDeleteOutputSchema(
|
|
316
|
+
entitySchema,
|
|
317
|
+
) as unknown as z.ZodType<
|
|
318
|
+
CollectionDeleteOutput<z.infer<TEntitySchema>>
|
|
319
|
+
>,
|
|
297
320
|
opt: true,
|
|
298
321
|
},
|
|
299
322
|
);
|
|
300
323
|
}
|
|
301
324
|
|
|
302
|
-
return bindings
|
|
325
|
+
return bindings as unknown as readonly CollectionBinding<
|
|
326
|
+
TEntitySchema,
|
|
327
|
+
Uppercase<TName>
|
|
328
|
+
>[number][];
|
|
303
329
|
}
|
|
304
330
|
|
|
305
331
|
export type ReadOnlyCollectionBinding<
|
|
@@ -309,16 +335,12 @@ export type ReadOnlyCollectionBinding<
|
|
|
309
335
|
{
|
|
310
336
|
name: `COLLECTION_${TUpperName}_LIST`;
|
|
311
337
|
inputSchema: typeof CollectionListInputSchema;
|
|
312
|
-
outputSchema:
|
|
313
|
-
typeof createCollectionListOutputSchema<TEntitySchema>
|
|
314
|
-
>;
|
|
338
|
+
outputSchema: z.ZodType<CollectionListOutput<z.infer<TEntitySchema>>>;
|
|
315
339
|
},
|
|
316
340
|
{
|
|
317
341
|
name: `COLLECTION_${TUpperName}_GET`;
|
|
318
342
|
inputSchema: typeof CollectionGetInputSchema;
|
|
319
|
-
outputSchema:
|
|
320
|
-
typeof createCollectionGetOutputSchema<TEntitySchema>
|
|
321
|
-
>;
|
|
343
|
+
outputSchema: z.ZodType<CollectionGetOutput<z.infer<TEntitySchema>>>;
|
|
322
344
|
},
|
|
323
345
|
];
|
|
324
346
|
/**
|
|
@@ -328,33 +350,32 @@ export type CollectionBinding<
|
|
|
328
350
|
TEntitySchema extends BaseCollectionEntitySchemaType,
|
|
329
351
|
TUpperName extends Uppercase<string> = Uppercase<string>,
|
|
330
352
|
> = [
|
|
331
|
-
|
|
353
|
+
{
|
|
354
|
+
name: `COLLECTION_${TUpperName}_LIST`;
|
|
355
|
+
inputSchema: typeof CollectionListInputSchema;
|
|
356
|
+
outputSchema: z.ZodType<CollectionListOutput<z.infer<TEntitySchema>>>;
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
name: `COLLECTION_${TUpperName}_GET`;
|
|
360
|
+
inputSchema: typeof CollectionGetInputSchema;
|
|
361
|
+
outputSchema: z.ZodType<CollectionGetOutput<z.infer<TEntitySchema>>>;
|
|
362
|
+
},
|
|
332
363
|
{
|
|
333
364
|
name: `COLLECTION_${TUpperName}_CREATE`;
|
|
334
|
-
inputSchema:
|
|
335
|
-
|
|
336
|
-
>;
|
|
337
|
-
outputSchema: ReturnType<
|
|
338
|
-
typeof createCollectionInsertOutputSchema<TEntitySchema>
|
|
339
|
-
>;
|
|
365
|
+
inputSchema: z.ZodType<CollectionInsertInput<z.infer<TEntitySchema>>>;
|
|
366
|
+
outputSchema: z.ZodType<CollectionInsertOutput<z.infer<TEntitySchema>>>;
|
|
340
367
|
opt: true;
|
|
341
368
|
},
|
|
342
369
|
{
|
|
343
370
|
name: `COLLECTION_${TUpperName}_UPDATE`;
|
|
344
|
-
inputSchema:
|
|
345
|
-
|
|
346
|
-
>;
|
|
347
|
-
outputSchema: ReturnType<
|
|
348
|
-
typeof createCollectionUpdateOutputSchema<TEntitySchema>
|
|
349
|
-
>;
|
|
371
|
+
inputSchema: z.ZodType<CollectionUpdateInput<z.infer<TEntitySchema>>>;
|
|
372
|
+
outputSchema: z.ZodType<CollectionUpdateOutput<z.infer<TEntitySchema>>>;
|
|
350
373
|
opt: true;
|
|
351
374
|
},
|
|
352
375
|
{
|
|
353
376
|
name: `COLLECTION_${TUpperName}_DELETE`;
|
|
354
377
|
inputSchema: typeof CollectionDeleteInputSchema;
|
|
355
|
-
outputSchema:
|
|
356
|
-
typeof createCollectionDeleteOutputSchema<TEntitySchema>
|
|
357
|
-
>;
|
|
378
|
+
outputSchema: z.ZodType<CollectionDeleteOutput<z.infer<TEntitySchema>>>;
|
|
358
379
|
opt: true;
|
|
359
380
|
},
|
|
360
381
|
];
|
|
@@ -373,6 +394,21 @@ export type CollectionGetInput = z.infer<typeof CollectionGetInputSchema>;
|
|
|
373
394
|
export type CollectionDeleteInput = z.infer<typeof CollectionDeleteInputSchema>;
|
|
374
395
|
export type OrderByExpression = z.infer<typeof OrderByExpressionSchema>;
|
|
375
396
|
|
|
397
|
+
/**
|
|
398
|
+
* Type helper for insert input with generic item type
|
|
399
|
+
*/
|
|
400
|
+
export type CollectionInsertInput<T> = {
|
|
401
|
+
data: T;
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Type helper for update input with generic item type
|
|
406
|
+
*/
|
|
407
|
+
export type CollectionUpdateInput<T> = {
|
|
408
|
+
id: string;
|
|
409
|
+
data: Partial<T>;
|
|
410
|
+
};
|
|
411
|
+
|
|
376
412
|
/**
|
|
377
413
|
* Type helper for list output with generic item type
|
|
378
414
|
*/
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Well-Known Binding
|
|
3
|
+
*
|
|
4
|
+
* Defines the interface for retrieving MCP configuration.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { z } from "zod/v3";
|
|
8
|
+
import type { Binder } from "../core/binder";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* MCP Configuration Output Schema
|
|
12
|
+
*/
|
|
13
|
+
export const McpConfigurationOutputSchema = z.object({
|
|
14
|
+
scopes: z.array(z.string()).describe("List of scopes available"),
|
|
15
|
+
stateSchema: z
|
|
16
|
+
.record(z.string(), z.unknown())
|
|
17
|
+
.describe("JSON Schema (draft-07) defining the state structure"),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export type McpConfigurationOutput = z.infer<
|
|
21
|
+
typeof McpConfigurationOutputSchema
|
|
22
|
+
>;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* MCP Binding
|
|
26
|
+
*
|
|
27
|
+
* Tool to retrieve the MCP configuration including scopes and state schema.
|
|
28
|
+
*/
|
|
29
|
+
export const MCP_BINDING = [
|
|
30
|
+
{
|
|
31
|
+
name: "MCP_CONFIGURATION",
|
|
32
|
+
inputSchema: z.object({}),
|
|
33
|
+
outputSchema: McpConfigurationOutputSchema,
|
|
34
|
+
},
|
|
35
|
+
] as const satisfies Binder;
|
package/test/mcp.test.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
MCP_BINDING,
|
|
4
|
+
McpConfigurationOutputSchema,
|
|
5
|
+
} from "../src/well-known/mcp";
|
|
6
|
+
|
|
7
|
+
describe("MCP Binding", () => {
|
|
8
|
+
it("should match the expected structure", () => {
|
|
9
|
+
expect(MCP_BINDING).toHaveLength(1);
|
|
10
|
+
const tool = MCP_BINDING[0];
|
|
11
|
+
expect(tool.name).toBe("MCP_CONFIGURATION");
|
|
12
|
+
expect(tool.inputSchema).toBeDefined();
|
|
13
|
+
expect(tool.outputSchema).toBeDefined();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("should validate correct output", () => {
|
|
17
|
+
const validOutput = {
|
|
18
|
+
scopes: ["scope1", "scope2"],
|
|
19
|
+
stateSchema: {
|
|
20
|
+
type: "object",
|
|
21
|
+
properties: {
|
|
22
|
+
foo: { type: "string" },
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const result = McpConfigurationOutputSchema.safeParse(validOutput);
|
|
28
|
+
expect(result.success).toBe(true);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("should fail on invalid output", () => {
|
|
32
|
+
const invalidOutput = {
|
|
33
|
+
scopes: "not-an-array", // Invalid
|
|
34
|
+
stateSchema: { type: "object" },
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const result = McpConfigurationOutputSchema.safeParse(invalidOutput);
|
|
38
|
+
expect(result.success).toBe(false);
|
|
39
|
+
});
|
|
40
|
+
});
|