Skip to main content
Glama

mcp-google-sheets

generateFromSpec.ts7.84 kB
/* eslint-disable prefer-const */ /* eslint-disable @typescript-eslint/no-explicit-any */ import dotenv from 'dotenv'; dotenv.config({ path: './assemblyai.env', }); import { existsSync, mkdirSync, unlinkSync, writeFileSync } from 'fs'; import { dirname, join } from 'path'; import OpenAPIParser from '@readme/openapi-parser'; import { mergician } from 'mergician'; import { titleCase } from 'title-case'; const generatedPath = './src/lib/generated/'; type Generators = { props?: (schemas: any) => any; }; const generateMap: Record<string, Generators> = { transcribe: { props: (schemas) => schemas.TranscriptParams, }, 'list-transcript': { props: (schemas) => schemas.ListTranscriptParams, }, 'lemur-task': { props: (schemas) => schemas.LemurTaskParams, }, }; const merge = mergician({ appendArrays: true, dedupArrays: true }); (async function () { const specLocation = process.env.OPENAPI_SPEC_LOCATION; if (!specLocation) throw new Error('OPENAPI_SPEC_LOCATION env variable is required'); let spec: any = merge( await OpenAPIParser.parse(specLocation), await OpenAPIParser.parse('./scripts/openapi.overrides.yml', { validate: { schema: false, spec: false, }, }) ); spec = await OpenAPIParser.dereference(spec); Object.entries(generateMap).forEach(([paramsName, { props }]) => { const parametersPath = join(generatedPath, paramsName, 'props.ts'); if (props) { let propsJson = createPropsFromSchema(props(spec.components.schemas)); let propsTs = createTs(propsJson); const dir = dirname(parametersPath); mkdirSync(dir, { recursive: true }); writeFileSync(parametersPath, propsTs, 'utf-8'); } else if (existsSync(parametersPath)) { unlinkSync(parametersPath); } }); })(); function createPropsFromSchema(schema: any): Record<string, any> { if (!schema) return {}; schema = structuredClone(schema); if (schema.allOf) { const obj = {}; (schema.allOf as any[]).forEach((schema) => Object.assign(obj, createPropsFromSchema(schema)) ); return obj; } if (!schema.properties) return {}; const properties: Record<string, unknown> = {}; const requiredProperties = schema.required ?? []; for (let [key, value] of Object.entries(schema.properties) as [ key: string, value: any ]) { if (value['x-ap-ignore']) { continue; } let nullable = false; let label: string; if (value['x-label']) { label = titleCase(value['x-label']); } else { label = titleCase(key); console.warn(`No x-label found for property ${key}`); } // grab the value of oneOf with null if (value.oneOf) { if (value.oneOf.findIndex((item: any) => item.type === 'null') > -1) { nullable = true; } const options = value.oneOf.filter((item: any) => item.type !== 'null'); // take first one and hope for the best value.oneOf = undefined; const option = { type: options[0].type, enum: options[0].enum, format: options[0].format, items: options[0].items, 'x-aai-enum': options[0]['x-aai-enum'], anyOf: options[0].anyOf, }; value = { ...value, ...option }; if (options[0].properties) { value.properties = { ...value.properties, ...options[0].properties }; } if (options[0].required) { value.required = (value.required || []).concat(options[0].required); } } if (value.anyOf) { const enumAnyOfIndex = value.anyOf.findIndex((item: any) => item.enum); // if any string or an enum, use the enum if ( value.anyOf.findIndex((item: any) => item.type === 'string') > -1 && enumAnyOfIndex > -1 ) { value.type = value.anyOf[enumAnyOfIndex].type; value.enum = value.anyOf[enumAnyOfIndex].enum; if ('x-aai-enum' in value.anyOf[enumAnyOfIndex]) { value['x-aai-enum'] = value.anyOf[enumAnyOfIndex]['x-aai-enum']; } value.anyOf = undefined; } else { throw new Error(`Unsupported AnyOf found for ${key}`); } } if (Array.isArray(value.type)) { if (value.type.indexOf('null') > -1) { nullable = true; } const types = value.type.filter((type: string) => type !== 'null'); if (types.length === 1) { value.type = types[0]; } else { throw new Error(`Multiple types found for ${key}`); } } const required = requiredProperties.indexOf(key) > -1 && !nullable; // handleArray if (value.type === 'array' && value.items.type === 'object') { properties[key] = { displayName: label, description: value.description, type: 'Array', required, properties: { ...createPropsFromSchema(value.items) }, }; } else if ( value.items && value.items.type === 'string' && value.items.enum ) { properties[key] = { ...createField(key, label, value.items, required), displayName: label, description: value.description, type: 'StaticMultiSelectDropdown', required, }; } else { // default field properties[key] = createField(key, label, value, required); } } return properties; } function createField( key: string, label: string, value: any, required: boolean ) { const field: any = { displayName: label, type: mapType(value), required, description: value.description, }; if (value.type === 'boolean' && 'default' in value) { field.defaultValue = value.default; } if (value.enum) { field.type = 'StaticDropdown'; field.options = { options: (value.enum as string[]).map((item) => { const option = { label: titleCase(item), value: item }; if ( value['x-aai-enum'] && value['x-aai-enum'][item] && value['x-aai-enum'][item]['label'] ) { option.label = titleCase(value['x-aai-enum'][item]['label']); return option; } else { console.warn(`No x-aai-enum value found for property ${key} ${item}`); } return option; }), }; } return field; } const typeMap: Record<string, string> = { date: 'DateTime', 'date-time': 'DateTime', url: 'ShortText', string: 'ShortText', uuid: 'ShortText', object: 'Object', number: 'Number', integer: 'Number', float: 'Number', double: 'Number', boolean: 'Checkbox', array: 'Array', json: 'Json', }; function mapType(schema: any) { if (schema['x-ap-type']) { return schema['x-ap-type']; } if (schema.format && schema.format in typeMap) { return typeMap[schema.format]; } if (schema.type in typeMap) { return typeMap[schema.type]; } throw new Error(`Unsupported type found ${schema.type}`); } function createTs( propsJson: Record<string, { type: string } & object> ): string { let result = `import { Property } from "@activepieces/pieces-framework"; export const props = `; result += createTsProps(propsJson); result += ';\n'; return result; } function createTsProps( propsJson: Record<string, { type: string } & object> ): string { let result = '{\n'; for (const key in propsJson) { const { type, ...prop } = propsJson[key]; let innerProps: null | string = null; if ('properties' in prop) { innerProps = createTsProps( prop['properties'] as Record<string, { type: string } & object> ); prop.properties = '[REPLACE_WITH_PROPS]'; } result += ` ${key}: Property.${type}(\n` + JSON.stringify(prop, null, ' ') + '),\n'; if (innerProps) { result = result.replace('"[REPLACE_WITH_PROPS]"', innerProps); } } result += '\n}'; return result; }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/activepieces/activepieces'

If you have feedback or need assistance with the MCP directory API, please join our Discord server