@decocms/bindings 1.0.0 → 1.0.1-alpha-candy.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/package.json +2 -2
- package/src/core/binder.ts +5 -6
- package/src/core/client/index.ts +8 -0
- package/src/core/client/mcp-client.ts +1 -1
- package/src/core/client/mcp.ts +11 -9
- package/src/core/subset.ts +3 -3
- package/src/well-known/collections.ts +77 -66
- 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.
|
|
3
|
+
"version": "1.0.1-alpha-candy.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "vitest run",
|
|
@@ -14,11 +14,11 @@
|
|
|
14
14
|
},
|
|
15
15
|
"exports": {
|
|
16
16
|
".": "./src/index.ts",
|
|
17
|
-
"./models": "./src/well-known/models.ts",
|
|
18
17
|
"./collections": "./src/well-known/collections.ts",
|
|
19
18
|
"./llm": "./src/well-known/language-model.ts",
|
|
20
19
|
"./connection": "./src/core/connection.ts",
|
|
21
20
|
"./client": "./src/core/client/index.ts",
|
|
21
|
+
"./mcp": "./src/well-known/mcp.ts",
|
|
22
22
|
"./agent": "./src/well-known/agent.ts"
|
|
23
23
|
},
|
|
24
24
|
"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" &&
|
|
@@ -33,16 +32,16 @@ function isZodSchema(value: unknown): value is ZodType<any> {
|
|
|
33
32
|
* @param schema - A Zod schema or JSON schema
|
|
34
33
|
* @returns The JSON schema representation, or null if input is null/undefined
|
|
35
34
|
*/
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
schema: ZodType<any> | JsonSchema | null | undefined,
|
|
35
|
+
function normalizeToJsonSchema(
|
|
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
|
package/src/core/client/index.ts
CHANGED
package/src/core/client/mcp.ts
CHANGED
|
@@ -3,10 +3,11 @@ import { z } from "zod";
|
|
|
3
3
|
import type { MCPConnection } from "../connection";
|
|
4
4
|
import { createMCPClientProxy } from "./proxy";
|
|
5
5
|
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
export const isStreamableToolBinder = (
|
|
7
|
+
toolBinder: ToolBinder,
|
|
8
|
+
): toolBinder is ToolBinder<string, any, any, true> => {
|
|
9
|
+
return toolBinder.streamable === true;
|
|
10
|
+
};
|
|
10
11
|
|
|
11
12
|
// Default fetcher instance with API_SERVER_URL and API_HEADERS
|
|
12
13
|
export const MCPClient = new Proxy(
|
|
@@ -34,14 +35,15 @@ export const MCPClient = new Proxy(
|
|
|
34
35
|
},
|
|
35
36
|
);
|
|
36
37
|
|
|
38
|
+
export interface FetchOptions extends RequestInit {
|
|
39
|
+
path?: string;
|
|
40
|
+
segments?: string[];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Default fetcher instance with API_SERVER_URL and API_HEADERS
|
|
37
44
|
import type { ToolBinder } from "../binder";
|
|
38
45
|
export type { ToolBinder };
|
|
39
46
|
|
|
40
|
-
export const isStreamableToolBinder = (
|
|
41
|
-
toolBinder: ToolBinder,
|
|
42
|
-
): toolBinder is ToolBinder<string, any, any, true> => {
|
|
43
|
-
return toolBinder.streamable === true;
|
|
44
|
-
};
|
|
45
47
|
export type MCPClientStub<TDefinition extends readonly ToolBinder[]> = {
|
|
46
48
|
[K in TDefinition[number] as K["name"]]: K extends ToolBinder<
|
|
47
49
|
string,
|
package/src/core/subset.ts
CHANGED
|
@@ -309,7 +309,7 @@ function isObjectSubset(a: JSONSchema, b: JSONSchema): boolean {
|
|
|
309
309
|
for (const key of Object.keys(bProps)) {
|
|
310
310
|
if (key in aProps) {
|
|
311
311
|
// Both have the property, check recursively
|
|
312
|
-
if (!isSubset(aProps[key]
|
|
312
|
+
if (!isSubset(aProps[key]!, bProps[key]!)) {
|
|
313
313
|
return false;
|
|
314
314
|
}
|
|
315
315
|
} else {
|
|
@@ -327,7 +327,7 @@ function isObjectSubset(a: JSONSchema, b: JSONSchema): boolean {
|
|
|
327
327
|
// A is open, check if additionalProperties schema satisfies B's property
|
|
328
328
|
const aAdditional = a.additionalProperties;
|
|
329
329
|
if (aAdditional && typeof aAdditional === "object") {
|
|
330
|
-
if (!isSubset(aAdditional, bProps[key])) {
|
|
330
|
+
if (!isSubset(aAdditional, bProps[key]!)) {
|
|
331
331
|
return false;
|
|
332
332
|
}
|
|
333
333
|
}
|
|
@@ -351,7 +351,7 @@ function isObjectSubset(a: JSONSchema, b: JSONSchema): boolean {
|
|
|
351
351
|
typeof b.additionalProperties === "object"
|
|
352
352
|
) {
|
|
353
353
|
// B has a schema for additional properties, check compatibility
|
|
354
|
-
if (!isSubset(aProps[key]
|
|
354
|
+
if (!isSubset(aProps[key]!, b.additionalProperties)) {
|
|
355
355
|
return false;
|
|
356
356
|
}
|
|
357
357
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
1
|
+
import { z, type ZodType } from "zod";
|
|
2
2
|
import type { ToolBinder } from "../core/binder";
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -23,6 +23,7 @@ import type { ToolBinder } from "../core/binder";
|
|
|
23
23
|
export const BaseCollectionEntitySchema = z.object({
|
|
24
24
|
id: z.string().describe("Unique identifier for the entity"),
|
|
25
25
|
title: z.string().describe("Human-readable title for the entity"),
|
|
26
|
+
description: z.string().nullish().describe("Description of the entity"),
|
|
26
27
|
created_at: z.string().datetime(),
|
|
27
28
|
updated_at: z.string().datetime(),
|
|
28
29
|
created_by: z.string().optional(),
|
|
@@ -251,7 +252,7 @@ export interface CollectionBindingOptions {
|
|
|
251
252
|
* ```
|
|
252
253
|
*/
|
|
253
254
|
export function createCollectionBindings<
|
|
254
|
-
TEntitySchema extends
|
|
255
|
+
TEntitySchema extends z.AnyZodObject,
|
|
255
256
|
TName extends string,
|
|
256
257
|
>(
|
|
257
258
|
collectionName: TName,
|
|
@@ -261,19 +262,18 @@ export function createCollectionBindings<
|
|
|
261
262
|
const upperName = collectionName.toUpperCase() as Uppercase<TName>;
|
|
262
263
|
const readOnly = options?.readOnly ?? false;
|
|
263
264
|
|
|
264
|
-
const bindings:
|
|
265
|
-
|
|
266
|
-
{
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
{
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
];
|
|
265
|
+
const bindings: ToolBinder[] = [
|
|
266
|
+
{
|
|
267
|
+
name: `COLLECTION_${upperName}_LIST` as const,
|
|
268
|
+
inputSchema: CollectionListInputSchema,
|
|
269
|
+
outputSchema: createCollectionListOutputSchema(entitySchema),
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
name: `COLLECTION_${upperName}_GET` as const,
|
|
273
|
+
inputSchema: CollectionGetInputSchema,
|
|
274
|
+
outputSchema: createCollectionGetOutputSchema(entitySchema),
|
|
275
|
+
},
|
|
276
|
+
];
|
|
277
277
|
|
|
278
278
|
// Only include mutation operations if not read-only
|
|
279
279
|
if (!readOnly) {
|
|
@@ -299,71 +299,67 @@ export function createCollectionBindings<
|
|
|
299
299
|
);
|
|
300
300
|
}
|
|
301
301
|
|
|
302
|
-
return bindings
|
|
302
|
+
return bindings as unknown as CollectionBinding<
|
|
303
|
+
TEntitySchema,
|
|
304
|
+
Uppercase<TName>
|
|
305
|
+
>;
|
|
303
306
|
}
|
|
304
307
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
name: `COLLECTION_${TUpperName}_GET`;
|
|
318
|
-
inputSchema: typeof CollectionGetInputSchema;
|
|
319
|
-
outputSchema: ReturnType<
|
|
320
|
-
typeof createCollectionGetOutputSchema<TEntitySchema>
|
|
321
|
-
>;
|
|
322
|
-
},
|
|
323
|
-
];
|
|
308
|
+
// Helper type for tool definition
|
|
309
|
+
type ToolBinding<
|
|
310
|
+
Name extends string,
|
|
311
|
+
Input,
|
|
312
|
+
Output,
|
|
313
|
+
Opt extends boolean = false,
|
|
314
|
+
> = {
|
|
315
|
+
name: Name;
|
|
316
|
+
inputSchema: ZodType<Input>;
|
|
317
|
+
outputSchema: ZodType<Output>;
|
|
318
|
+
} & (Opt extends true ? { opt: true } : unknown);
|
|
319
|
+
|
|
324
320
|
/**
|
|
325
321
|
* Type helper to extract the collection binding type
|
|
326
322
|
*/
|
|
327
323
|
export type CollectionBinding<
|
|
328
|
-
TEntitySchema extends
|
|
324
|
+
TEntitySchema extends z.AnyZodObject,
|
|
329
325
|
TUpperName extends Uppercase<string> = Uppercase<string>,
|
|
326
|
+
TEntity = z.infer<TEntitySchema>,
|
|
330
327
|
> = [
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
},
|
|
328
|
+
ToolBinding<
|
|
329
|
+
`COLLECTION_${TUpperName}_LIST`,
|
|
330
|
+
CollectionListInput,
|
|
331
|
+
CollectionListOutput<TEntity>
|
|
332
|
+
>,
|
|
333
|
+
ToolBinding<
|
|
334
|
+
`COLLECTION_${TUpperName}_GET`,
|
|
335
|
+
CollectionGetInput,
|
|
336
|
+
CollectionGetOutput<TEntity>
|
|
337
|
+
>,
|
|
338
|
+
ToolBinding<
|
|
339
|
+
`COLLECTION_${TUpperName}_CREATE`,
|
|
340
|
+
CollectionInsertInput<TEntity>,
|
|
341
|
+
CollectionInsertOutput<TEntity>,
|
|
342
|
+
true
|
|
343
|
+
>,
|
|
344
|
+
ToolBinding<
|
|
345
|
+
`COLLECTION_${TUpperName}_UPDATE`,
|
|
346
|
+
CollectionUpdateInput<TEntity>,
|
|
347
|
+
CollectionUpdateOutput<TEntity>,
|
|
348
|
+
true
|
|
349
|
+
>,
|
|
350
|
+
ToolBinding<
|
|
351
|
+
`COLLECTION_${TUpperName}_DELETE`,
|
|
352
|
+
CollectionDeleteInput,
|
|
353
|
+
CollectionDeleteOutput<TEntity>,
|
|
354
|
+
true
|
|
355
|
+
>,
|
|
360
356
|
];
|
|
361
357
|
|
|
362
358
|
/**
|
|
363
359
|
* Type helper to extract tool names from a collection binding
|
|
364
360
|
*/
|
|
365
361
|
export type CollectionTools<
|
|
366
|
-
TEntitySchema extends
|
|
362
|
+
TEntitySchema extends z.AnyZodObject,
|
|
367
363
|
TUpperName extends Uppercase<string> = Uppercase<string>,
|
|
368
364
|
> = CollectionBinding<TEntitySchema, TUpperName>[number]["name"];
|
|
369
365
|
|
|
@@ -373,6 +369,21 @@ export type CollectionGetInput = z.infer<typeof CollectionGetInputSchema>;
|
|
|
373
369
|
export type CollectionDeleteInput = z.infer<typeof CollectionDeleteInputSchema>;
|
|
374
370
|
export type OrderByExpression = z.infer<typeof OrderByExpressionSchema>;
|
|
375
371
|
|
|
372
|
+
/**
|
|
373
|
+
* Type helper for insert input with generic item type
|
|
374
|
+
*/
|
|
375
|
+
export type CollectionInsertInput<T> = {
|
|
376
|
+
data: T;
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Type helper for update input with generic item type
|
|
381
|
+
*/
|
|
382
|
+
export type CollectionUpdateInput<T> = {
|
|
383
|
+
id: string;
|
|
384
|
+
data: Partial<T>;
|
|
385
|
+
};
|
|
386
|
+
|
|
376
387
|
/**
|
|
377
388
|
* Type helper for list output with generic item type
|
|
378
389
|
*/
|
|
@@ -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
|
+
});
|