Skip to main content
Glama
mcpToolsResponse.test.ts8.77 kB
import { describe, expect, it } from "vitest"; import { TOOL_NAMES } from "../../src/core/constants.js"; import type { ILogger } from "../../src/core/logger.js"; import { registerTools } from "../../src/features/tools/register.js"; import type { ExecNodeMethodRequest } from "../../src/gen/endpoints/TouchDesignerAPI"; import type { TouchDesignerClient } from "../../src/tdClient/index.js"; type ToolHandler = (params?: Record<string, unknown>) => Promise<unknown>; class MockMcpServer { public tools = new Map<string, ToolHandler>(); tool(name: string, ...rest: unknown[]): void { const args = [...rest]; const handler = args.pop(); if (typeof handler === "function") { this.tools.set(name, handler as ToolHandler); } } getTool(name: string): ToolHandler { const tool = this.tools.get(name); if (!tool) throw new Error(`Tool ${name} not registered`); return tool; } } const logger: ILogger = { sendLog: () => {}, }; function createMockTdClient(): TouchDesignerClient { const execNodeMethod: TouchDesignerClient["execNodeMethod"] = async < DATA extends NonNullable<{ result: unknown }>, >( _params: ExecNodeMethodRequest, ) => ({ data: { result: [] } as DATA, success: true }); const mock = { createNode: (async (_params: unknown) => ({ data: { result: { id: 1, name: "mock", opType: "textTOP", path: "/project1/mock", properties: {}, }, }, success: true, })) as TouchDesignerClient["createNode"], deleteNode: async (_params: unknown) => ({ data: { deleted: true }, success: true, }), execNodeMethod, execPythonScript: (async (_params: unknown) => ({ data: { result: { value: ["geo1", "text1"] } }, success: true, })) as TouchDesignerClient["execPythonScript"], getAdditionalToolResultContents: () => null, getClassDetails: async (_className: unknown) => ({ data: { description: "Base operator", methods: [{ description: "find", name: "op", signature: "op(path)" }], name: "OP", properties: [{ name: "name", type: "string" }], type: "class", }, success: true, }), getClasses: (async () => ({ data: { classes: [ { description: "Base operator", name: "OP", type: "class" }, { description: "Component", name: "COMP", type: "class" }, ], }, success: true, })) as TouchDesignerClient["getClasses"], getModuleHelp: (async (_params: unknown) => ({ data: { helpText: `Help on module noiseCHOP: NAME noiseCHOP DESCRIPTION Generates procedural noise for CHOP channels. METHODS cook(frame) DATA DESCRIPTORS sampleRate`, moduleName: "noiseCHOP", }, success: true, })) as TouchDesignerClient["getModuleHelp"], getNodeDetail: async (_params: unknown) => ({ data: { id: 10, name: "webserverDAT", opType: "webServerDAT", path: "/project1/webserverDAT", properties: { active: true, port: 9981 }, }, success: true, }), getNodeErrors: async (_params: unknown) => ({ data: { errorCount: 1, errors: [ { message: "Mock error detected", nodeName: "mockNode", nodePath: "/project1/mockNode", opType: "textTOP", }, ], hasErrors: true, nodeName: "mockNode", nodePath: "/project1/mockNode", opType: "textTOP", }, success: true, }), getNodes: async (_params: unknown) => ({ data: { nodes: [ { id: 1, name: "geo1", opType: "geometry", path: "/project1/geo1", properties: {}, }, { id: 2, name: "text1", opType: "textTOP", path: "/project1/text1", properties: {}, }, ], parentPath: "/project1", }, success: true, }), getTdInfo: (async () => ({ data: { osName: "test-os", osVersion: "0.0.0", server: "mock", version: "0.0.0", }, success: true, })) as TouchDesignerClient["getTdInfo"], updateNode: async (_params: unknown) => ({ data: { updated: ["a"] }, success: true, }), } satisfies Partial<TouchDesignerClient>; return mock as unknown as TouchDesignerClient; } describe("MCP tool responses", () => { const server = new MockMcpServer(); registerTools( server as unknown as import("@modelcontextprotocol/sdk/server/mcp.js").McpServer, logger, createMockTdClient(), ); it("returns formatted node list for GET_TD_NODES", async () => { const handler = server.getTool(TOOL_NAMES.GET_TD_NODES); const result = (await handler({ detailLevel: "summary", parentPath: "/project1", responseFormat: "markdown", })) as { content?: Array<{ type: string; text?: string }>; }; const text = result.content?.find((c) => c.type === "text")?.text ?? ""; expect(text).toContain("Nodes in /project1"); expect(text).toContain("geo1"); expect(text).toContain("text1"); }); it("returns formatted node parameters for GET_TD_NODE_PARAMETERS", async () => { const handler = server.getTool(TOOL_NAMES.GET_TD_NODE_PARAMETERS); const result = (await handler({ detailLevel: "summary", nodePath: "/project1/webserverDAT", responseFormat: "markdown", })) as { content?: Array<{ type: string; text?: string }>; }; const text = result.content?.find((c) => c.type === "text")?.text ?? ""; expect(text).toContain("webserverDAT"); expect(text).toContain("Properties shown"); }); it("returns formatted error details for GET_TD_NODE_ERRORS", async () => { const handler = server.getTool(TOOL_NAMES.GET_TD_NODE_ERRORS); const result = (await handler({ detailLevel: "summary", nodePath: "/project1/mockNode", responseFormat: "markdown", })) as { content?: Array<{ type: string; text?: string }>; }; const text = result.content?.find((c) => c.type === "text")?.text ?? ""; expect(text).toContain("mockNode"); expect(text).toContain("Mock error detected"); }); it("returns formatted class list for GET_TD_CLASSES", async () => { const handler = server.getTool(TOOL_NAMES.GET_TD_CLASSES); const result = (await handler({ detailLevel: "summary", responseFormat: "markdown", })) as { content?: Array<{ type: string; text?: string }>; }; const text = result.content?.find((c) => c.type === "text")?.text ?? ""; expect(text).toContain("TouchDesigner Classes"); expect(text).toContain("OP"); }); it("returns formatted script result for EXECUTE_PYTHON_SCRIPT", async () => { const handler = server.getTool(TOOL_NAMES.EXECUTE_PYTHON_SCRIPT); const result = (await handler({ detailLevel: "summary", responseFormat: "markdown", script: "op('/project1').children", })) as { content?: Array<{ type: string; text?: string }>; }; const text = result.content?.find((c) => c.type === "text")?.text ?? ""; expect(text).toContain("Script Result"); expect(text).toContain("Return type"); }); it("returns filesystem manifest for DESCRIBE_TD_TOOLS", async () => { const handler = server.getTool(TOOL_NAMES.DESCRIBE_TD_TOOLS); const result = (await handler({ detailLevel: "summary", filter: "class", responseFormat: "markdown", })) as { content?: Array<{ type: string; text?: string }>; }; const text = result.content?.find((c) => c.type === "text")?.text ?? ""; expect(text).toContain("getTdClasses"); expect(text).toContain("getTdClassDetails"); expect(text).toContain("servers/touchdesigner"); }); it("returns formatted module help preview for GET_TD_MODULE_HELP", async () => { const handler = server.getTool(TOOL_NAMES.GET_TD_MODULE_HELP); const result = (await handler({ detailLevel: "summary", moduleName: "noiseCHOP", responseFormat: "markdown", })) as { content?: Array<{ type: string; text?: string }>; }; const text = result.content?.find((c) => c.type === "text")?.text ?? ""; expect(text).toContain("✓ Help information for noiseCHOP"); expect(text).toContain("Sections:"); expect(text).toContain("METHODS"); }); it("returns an error response when GET_TD_MODULE_HELP fails", async () => { const failingServer = new MockMcpServer(); const failingClient = createMockTdClient(); failingClient.getModuleHelp = (async () => ({ error: new Error("Module missing"), success: false, })) as TouchDesignerClient["getModuleHelp"]; registerTools( failingServer as unknown as import("@modelcontextprotocol/sdk/server/mcp.js").McpServer, logger, failingClient, ); const handler = failingServer.getTool(TOOL_NAMES.GET_TD_MODULE_HELP); const result = (await handler({ moduleName: "missing", })) as { content?: Array<{ type: string; text?: string }>; isError?: boolean; }; expect(result.isError).toBe(true); const text = result.content?.find((c) => c.type === "text")?.text ?? ""; expect(text).toContain("Module missing"); }); });

Latest Blog Posts

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/8beeeaaat/touchdesigner-mcp'

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