import { camelCase, isValidVarName } from '@internals/utils' import type { KubbFile } from '@kubb/fabric-core/types' import type { SchemaObject } from '@kubb/oas' import type { OperationSchemas } from '@kubb/plugin-oas' import { isOptional } from '@kubb/plugin-oas/utils' import { File, FunctionParams } from '@kubb/react-fabric' import type { FabricReactNode } from '@kubb/react-fabric/types' type Props = { serverName: string serverVersion: string paramsCasing?: 'camelcase' operations: Array<{ tool: { name: string title?: string description: string } mcp: { name: string file: KubbFile.File } zod: { name: string file: KubbFile.File schemas: OperationSchemas } type: { schemas: OperationSchemas } }> } type GetParamsProps = { schemas: OperationSchemas paramsCasing?: 'camelcase' } function zodExprFromOasSchema(schema: SchemaObject): string { const types = Array.isArray(schema.type) ? schema.type : [schema.type] const baseType = types.find((t) => t && t !== 'null') const isNullableType = types.includes('null') let expr: string if (Array.isArray(schema.enum) && schema.enum.length > 0) { const values = schema.enum.map((v: unknown) => JSON.stringify(v)).join(', ') expr = `z.enum([${values}])` if (isNullableType) { expr = `${expr}.nullable()` } return expr } switch (baseType) { case 'integer': expr = 'z.coerce.number()' break case 'number': expr = 'z.number()' break case 'boolean': expr = 'z.boolean()' break case 'array': expr = 'z.array(z.unknown())' break default: expr = 'z.string()' } if (isNullableType) { expr = `${expr}.nullable()` } return expr } function getParams({ schemas, paramsCasing }: GetParamsProps) { const pathParamProperties = schemas.pathParams?.schema?.properties ?? {} const requiredFields = Array.isArray(schemas.pathParams?.schema?.required) ? schemas.pathParams.schema.required : [] const pathParamEntries = Object.entries(pathParamProperties).reduce>( (acc, [originalKey, propSchema]) => { const key = paramsCasing === 'camelcase' || !isValidVarName(originalKey) ? camelCase(originalKey) : originalKey acc[key] = { value: zodExprFromOasSchema(propSchema as SchemaObject), optional: !requiredFields.includes(originalKey), } return acc }, {}, ) return FunctionParams.factory({ data: { mode: 'object', children: { ...pathParamEntries, data: schemas.request?.name ? { value: schemas.request?.name, optional: isOptional(schemas.request?.schema), } : undefined, params: schemas.queryParams?.name ? { value: schemas.queryParams?.name, optional: isOptional(schemas.queryParams?.schema), } : undefined, headers: schemas.headerParams?.name ? { value: schemas.headerParams?.name, optional: isOptional(schemas.headerParams?.schema), } : undefined, }, }, }) } export function Server({ serverName, serverVersion, paramsCasing, operations }: Props): FabricReactNode { const registrations = operations .map(({ tool, mcp, zod }) => { const paramsClient = getParams({ schemas: zod.schemas, paramsCasing }) const outputSchema = zod.schemas.response?.name const config = [ tool.title ? `title: ${JSON.stringify(tool.title)}` : null, `description: ${JSON.stringify(tool.description)}`, outputSchema ? `outputSchema: { data: ${outputSchema} }` : null, ] .filter(Boolean) .join(',\n ') if (zod.schemas.request?.name || zod.schemas.headerParams?.name || zod.schemas.queryParams?.name || zod.schemas.pathParams?.name) { return ` server.registerTool(${JSON.stringify(tool.name)}, { ${config}, inputSchema: ${paramsClient.toObjectValue()}, }, async (${paramsClient.toObject()}) => { return ${mcp.name}(${paramsClient.toObject()}) }) ` } return ` server.registerTool(${JSON.stringify(tool.name)}, { ${config}, }, async () => { return ${mcp.name}(${paramsClient.toObject()}) }) ` }) .filter(Boolean) .join('\n') return ( <> {` export function getServer() { const server = new McpServer({ name: '${serverName}', version: '${serverVersion}', }) ${registrations} return server } `} {` export const server = getServer() `} {` export async function startServer() { try { const transport = new StdioServerTransport() await server.connect(transport) } catch (error) { console.error('Failed to start server:', error) process.exit(1) } } `} ) }