UNPKG

@fireberry/mcp-server

Version:

MCP Server for hassle-free usage of Fireberry's API.

152 lines (140 loc) 6.2 kB
import { z } from 'zod'; import { FIELD_NAME_MAX_LENGTH_LIMIT, LABEL_MAX_LENGTH_LIMIT } from '../../constants.js'; export const FieldTypeNames = { text: 'text', emailAddress: 'emailaddress', url: 'url', phoneNumber: 'phonenumber', number: 'number', textarea: 'textarea', date: 'date', dateTime: 'datetime', lookup: 'lookup', picklist: 'picklist', } as const; export type FieldTypeNamesForCreate = (typeof FieldTypeNames)[keyof typeof FieldTypeNames]; // Base schema for common field parameters const baseFieldSchema = z.object({ objectType: z.int().describe('The object type to add the field to'), fieldName: z.optional( z .string() .min(1) .max(FIELD_NAME_MAX_LENGTH_LIMIT) .regex(/^pcf[a-zA-Z0-9]+$/, 'Field name must start with `pcf` and can only contain letters and numbers') .describe( 'System field name (sql column name). Must start with `pcf` or the field will not be created. can only contain letters and numbers' ) ), label: z.string().min(1).max(LABEL_MAX_LENGTH_LIMIT).describe('Field name used for system displays.'), }); const dateFieldSchema = baseFieldSchema.extend({ fieldType: z.literal(FieldTypeNames.date).describe('The type of field to create'), }); const dateTimeFieldSchema = baseFieldSchema.extend({ fieldType: z.literal(FieldTypeNames.dateTime).describe('The type of field to create'), }); const phoneNumberFieldSchema = baseFieldSchema.extend({ fieldType: z.literal(FieldTypeNames.phoneNumber).describe('The type of field to create'), }); const emailAddressFieldSchema = baseFieldSchema.extend({ fieldType: z.literal(FieldTypeNames.emailAddress).describe('The type of field to create'), }); const urlFieldSchema = baseFieldSchema.extend({ fieldType: z.literal(FieldTypeNames.url).describe('The type of field to create'), }); const textareaFieldSchema = baseFieldSchema.extend({ fieldType: z.literal(FieldTypeNames.textarea).describe('The type of field to create'), }); const textFieldSchema = baseFieldSchema.extend({ fieldType: z.literal(FieldTypeNames.text).describe('The type of field to create'), }); const lookupFieldSchema = baseFieldSchema.extend({ fieldType: z.literal(FieldTypeNames.lookup).describe('The type of field to create'), relatedObjectType: z.int32().describe('The number of the object type this field will relate records from.'), }); const picklistOptionSchema = z.object({ order: z.int32().describe('Number from lowest (top) to highest (bottom) to set the order. Start at 1.'), color: z.string().describe('Set a hex color code, start with #. Defaults as black.'), label: z.string().describe('The name displayed for this option.'), value: z.int32().describe('The whole number used to identify this option, must be unique.'), }); const picklistFieldSchema = baseFieldSchema.extend({ fieldType: z.literal(FieldTypeNames.picklist).describe('The type of field to create'), options: z.array(picklistOptionSchema).nonempty().describe('The values of the picklist'), }); const numberFieldSchema = baseFieldSchema.extend({ fieldType: z.literal(FieldTypeNames.number).describe('The type of field to create'), precision: z.int().min(0).max(4).default(0).describe('Number between 0-4 to set the amount of digits after the decimal point'), }); /** * Schema for creating a field, used for the `CallToolRequestSchema`. * After the MCP server is updated to support anyOf, we can use this instead of `fieldCreateToolInputSchemaForRegister` schema for the register tool. * The schema is used for parsing the arguments of the tool, after the tool is called. * If the tool is called with an invalid field type, the MCP server will return an informative error so the agent can try again with a valid field schema. */ export const fieldCreateToolInputSchemaForCall = z.discriminatedUnion('fieldType', [ textFieldSchema, numberFieldSchema, dateFieldSchema, dateTimeFieldSchema, phoneNumberFieldSchema, emailAddressFieldSchema, urlFieldSchema, textareaFieldSchema, lookupFieldSchema, picklistFieldSchema, ]); /** * Schema for the registering the field create tool in `ListToolsRequestSchema` * For some reason, the JSON schema generated by zod for descriminated unions is not valid for the MCP server. (lookup for json schema anyOf) * This is a workaround to use the `fieldCreateToolInputSchemaForCall` schema for the register tool. */ export const fieldCreateToolInputSchemaForRegister = baseFieldSchema.extend({ fieldType: z.optional(z.string().describe(`REQUIRED The type of field to create ${Object.values(FieldTypeNames).join(',')}`)), precision: z.optional( z .int() .min(0) .max(4) .default(0) .describe( 'optional only for `number` fields, forbidden for others) Number between 0-4 to set the amount of digits after the decimal point' ) ), relatedObjectType: z.optional( z .int32() .describe( '(required only for `lookup` fields, forbidden for others) The number of the object type this field will relate records from.' ) ), options: z.optional( z .array(picklistOptionSchema) .nonempty() .describe('(required only for `picklist` fields, forbidden for others) The values of the picklist') ), }); export type CreateFieldInputSchema<T extends FieldTypeNamesForCreate> = Extract< z.infer<typeof fieldCreateToolInputSchemaForCall>, { fieldType: T } >; export const fieldCreateResponseSchema = z.discriminatedUnion('success', [ z.object({ success: z.literal(true), data: z.object({ systemField: z.array( z.object({ fieldname: z.string(), }) ), }), message: z.string(), }), z.object({ success: z.optional(z.literal(false)), Message: z.string(), }), ]); export type CreateField = z.infer<typeof fieldCreateResponseSchema>;