UNPKG

touchdesigner-mcp-server

Version:
188 lines (187 loc) • 6.51 kB
/** * Class List Formatter * * Formats TouchDesigner Python class/module lists with token optimization. * Used by GET_TD_CLASSES and GET_TD_CLASS_DETAILS tools. */ import { DEFAULT_PRESENTER_FORMAT, presentStructuredData, } from "./presenter.js"; import { finalizeFormattedText, limitArray, mergeFormatterOptions, } from "./responseFormatter.js"; /** * Format class/module list */ export function formatClassList(data, options) { const opts = mergeFormatterOptions(options); if (!data) { return "No classes or modules found."; } const classes = data.classes || []; const modules = data.modules || []; const total = classes.length + modules.length; if (total === 0) { return "No classes or modules found."; } if (opts.detailLevel === "detailed") { return formatDetailed(data, opts.responseFormat); } const { text, context } = opts.detailLevel === "minimal" ? formatClassListMinimal(classes, modules, opts.limit) : formatClassListSummary(classes, modules, opts.limit); const ctx = context; return finalizeFormattedText(text, opts, { context: ctx, structured: ctx, template: "classListSummary", }); } /** * Format class details */ export function formatClassDetails(data, options) { const opts = mergeFormatterOptions(options); if (!data?.name) { return "No class details available."; } if (opts.detailLevel === "detailed") { return formatDetailed(data, opts.responseFormat); } const { text, context } = opts.detailLevel === "minimal" ? formatClassDetailsMinimal(data) : formatClassDetailsSummary(data, opts.limit); const ctx = context; return finalizeFormattedText(text, opts, { context: ctx, structured: ctx, template: "classDetailsSummary", }); } function formatClassListMinimal(classes, modules, limit) { const { items: limitedClasses, truncated: classTruncated } = limitArray(classes, limit); let text = `Classes (${classes.length}): ${limitedClasses .map((c) => c.name) .join(", ")}`; if (classTruncated) { text += `\nšŸ’” ${classes.length - limitedClasses.length} more classes omitted.`; } if (modules.length > 0) { text += `\nModules (${modules.length}): ${modules.join(", ")}`; } return { context: buildClassListContext(classes, modules), text, }; } function formatClassListSummary(classes, modules, limit) { const { items: limitedClasses } = limitArray(classes, limit); const text = `Classes (${classes.length}):\n${limitedClasses .map((c) => `- ${c.name} — ${c.description || ""}`) .join("\n")}\n\nModules (${modules.length}):\n${modules .map((m) => `- ${m}`) .join("\n")}`; return { context: buildClassListContext(classes, modules), text, }; } function buildClassListContext(classes, modules) { return { classCount: classes.length, classes: classes.map((cls) => ({ description: cls.description, name: cls.name, })), moduleCount: modules.length, modules, }; } function formatClassDetailsMinimal(data) { const text = `Class: ${data.name}\nType: ${data.type}`; return { context: { description: data.description, methods: [], methodsShown: 0, methodsTotal: data.methods?.length ?? 0, name: data.name, properties: [], propertiesShown: 0, propertiesTotal: data.properties?.length ?? 0, truncated: false, type: data.type, }, text, }; } function formatClassDetailsSummary(data, limit) { const methods = data.methods || []; const properties = data.properties || []; const { items: limitedMethods, truncated: methodsTruncated } = limitArray(methods, limit); const { items: limitedProps, truncated: propsTruncated } = limitArray(properties, limit); let text = `${data.name}`; if (data.type) text += ` (${data.type})`; text += "\n"; if (data.description) { text += `\n${data.description}\n`; } if (limitedMethods.length > 0) { text += `\nMETHODS (${methods.length}):\n`; for (const method of limitedMethods) { const sig = method.signature || `${method.name}()`; const doc = method.description ? ` - ${method.description.split("\n")[0]}` : ""; text += ` • ${sig}${doc}\n`; } if (methodsTruncated) { text += ` šŸ’” ${methods.length - limitedMethods.length} more methods omitted.\n`; } } if (limitedProps.length > 0) { text += `\nPROPERTIES (${properties.length}):\n`; for (const prop of limitedProps) { const typeInfo = prop.type ? `: ${prop.type}` : ""; const valueInfo = prop.value !== undefined ? ` = ${JSON.stringify(prop.value)}` : ""; text += ` • ${prop.name}${typeInfo}${valueInfo}\n`; } if (propsTruncated) { text += ` šŸ’” ${properties.length - limitedProps.length} more properties omitted.\n`; } } return { context: { description: data.description, methods: limitedMethods.map((method) => ({ signature: method.signature || `${method.name}()`, summary: method.description?.split("\n")[0] ?? "", })), methodsShown: limitedMethods.length, methodsTotal: methods.length, name: data.name, properties: limitedProps.map((prop) => ({ name: prop.name, type: prop.type, })), propertiesShown: limitedProps.length, propertiesTotal: properties.length, truncated: methodsTruncated || propsTruncated, type: data.type, }, text, }; } function formatDetailed(data, format) { const title = "name" in data ? `Class ${data.name ?? "details"}` : "TouchDesigner Classes"; const payloadFormat = format ?? DEFAULT_PRESENTER_FORMAT; return presentStructuredData({ context: { payloadFormat, title, }, detailLevel: "detailed", structured: data, template: "detailedPayload", text: title, }, payloadFormat); }