touchdesigner-mcp-server
Version:
MCP server for TouchDesigner
188 lines (187 loc) ⢠6.51 kB
JavaScript
/**
* 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);
}