@decocms/bindings 1.0.0-test.1 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decocms/bindings",
3
- "version": "1.0.0-test.1",
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": {
@@ -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
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
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
- export function normalizeToJsonSchema(
37
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
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
- return zodToJsonSchema(schema) as JsonSchema;
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
@@ -1 +1,9 @@
1
1
  export { HTTPClientTransport } from "./http-client-transport";
2
+ export {
3
+ createMCPFetchStub,
4
+ isStreamableToolBinder,
5
+ MCPClient,
6
+ type CreateStubAPIOptions,
7
+ type MCPClientFetchStub,
8
+ type MCPClientStub,
9
+ } from "./mcp";
@@ -93,7 +93,7 @@ export const createServerClient = async (
93
93
  };
94
94
  };
95
95
 
96
- export const createTransport = (
96
+ const createTransport = (
97
97
  connection: MCPConnection,
98
98
  signal?: AbortSignal,
99
99
  extraHeaders?: Record<string, string>,
@@ -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 interface FetchOptions extends RequestInit {
7
- path?: string;
8
- segments?: string[];
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,
@@ -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], bProps[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], b.additionalProperties)) {
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 BaseCollectionEntitySchemaType,
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: CollectionBinding<TEntitySchema, Uppercase<TName>>[number][] =
265
- [
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
- ];
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 satisfies readonly ToolBinder[];
302
+ return bindings as unknown as CollectionBinding<
303
+ TEntitySchema,
304
+ Uppercase<TName>
305
+ >;
303
306
  }
304
307
 
305
- export type ReadOnlyCollectionBinding<
306
- TEntitySchema extends BaseCollectionEntitySchemaType,
307
- TUpperName extends Uppercase<string> = Uppercase<string>,
308
- > = [
309
- {
310
- name: `COLLECTION_${TUpperName}_LIST`;
311
- inputSchema: typeof CollectionListInputSchema;
312
- outputSchema: ReturnType<
313
- typeof createCollectionListOutputSchema<TEntitySchema>
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 BaseCollectionEntitySchemaType,
324
+ TEntitySchema extends z.AnyZodObject,
329
325
  TUpperName extends Uppercase<string> = Uppercase<string>,
326
+ TEntity = z.infer<TEntitySchema>,
330
327
  > = [
331
- ...ReadOnlyCollectionBinding<TEntitySchema, TUpperName>,
332
- {
333
- name: `COLLECTION_${TUpperName}_CREATE`;
334
- inputSchema: ReturnType<
335
- typeof createCollectionInsertInputSchema<TEntitySchema>
336
- >;
337
- outputSchema: ReturnType<
338
- typeof createCollectionInsertOutputSchema<TEntitySchema>
339
- >;
340
- opt: true;
341
- },
342
- {
343
- name: `COLLECTION_${TUpperName}_UPDATE`;
344
- inputSchema: ReturnType<
345
- typeof createCollectionUpdateInputSchema<TEntitySchema>
346
- >;
347
- outputSchema: ReturnType<
348
- typeof createCollectionUpdateOutputSchema<TEntitySchema>
349
- >;
350
- opt: true;
351
- },
352
- {
353
- name: `COLLECTION_${TUpperName}_DELETE`;
354
- inputSchema: typeof CollectionDeleteInputSchema;
355
- outputSchema: ReturnType<
356
- typeof createCollectionDeleteOutputSchema<TEntitySchema>
357
- >;
358
- opt: true;
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 BaseCollectionEntitySchemaType,
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;
@@ -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
+ });