mcpengine/servers/webflow/src/tools/collections.ts

70 lines
3.6 KiB
TypeScript

import { z } from 'zod';
import type { WebflowClient } from '../client/webflow-client.js';
const ListCollectionsInput = z.object({ site_id: z.string().describe('Site ID') });
const GetCollectionInput = z.object({ collection_id: z.string().describe('Collection ID') });
const ListCollectionFieldsInput = z.object({ collection_id: z.string().describe('Collection ID') });
export default [
{
name: 'webflow_list_collections',
description: 'List all CMS collections for a Webflow site. Use when browsing available collections, getting collection IDs for item operations, or auditing site CMS structure. Returns collection metadata including ID, name, slug, and field definitions.',
inputSchema: zodToJsonSchema(ListCollectionsInput),
handler: async (input: unknown, client: WebflowClient) => {
const validated = ListCollectionsInput.parse(input);
const result = await client.listCollections(validated.site_id);
return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] };
},
},
{
name: 'webflow_get_collection',
description: 'Retrieve detailed information about a specific CMS collection including all field definitions, validation rules, and collection settings. Use when inspecting collection schema before creating or updating items, or verifying field types and requirements.',
inputSchema: zodToJsonSchema(GetCollectionInput),
handler: async (input: unknown, client: WebflowClient) => {
const validated = GetCollectionInput.parse(input);
const result = await client.getCollection(validated.collection_id);
return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] };
},
},
{
name: 'webflow_list_collection_fields',
description: 'List all fields in a CMS collection with detailed schema information including field types, validation rules, required status, and options. Use when building forms, validating data before creation, or understanding collection structure. Essential for correct item creation.',
inputSchema: zodToJsonSchema(ListCollectionFieldsInput),
handler: async (input: unknown, client: WebflowClient) => {
const validated = ListCollectionFieldsInput.parse(input);
const collection = await client.getCollection(validated.collection_id);
return { content: [{ type: 'text' as const, text: JSON.stringify(collection.fields || [], null, 2) }] };
},
},
];
function zodToJsonSchema(schema: z.ZodType<any>): any {
const shape = (schema as any)._def?.shape?.();
if (!shape) return { type: 'object', properties: {}, required: [] };
const properties: Record<string, any> = {};
const required: string[] = [];
for (const [key, value] of Object.entries(shape)) {
const zodField = value as z.ZodType<any>;
properties[key] = { type: getZodType(zodField), description: (zodField as any)._def?.description };
if (!isOptional(zodField)) required.push(key);
}
return { type: 'object', properties, required };
}
function getZodType(schema: z.ZodType<any>): string {
const typeName = (schema as any)._def?.typeName;
if (typeName === 'ZodString') return 'string';
if (typeName === 'ZodNumber') return 'number';
if (typeName === 'ZodBoolean') return 'boolean';
if (typeName === 'ZodArray') return 'array';
if (typeName === 'ZodObject') return 'object';
if (typeName === 'ZodOptional') return getZodType((schema as any)._def.innerType);
if (typeName === 'ZodDefault') return getZodType((schema as any)._def.innerType);
return 'string';
}
function isOptional(schema: z.ZodType<any>): boolean {
const typeName = (schema as any)._def?.typeName;
return typeName === 'ZodOptional' || typeName === 'ZodDefault';
}