From 472dcd789019660fee0cec9dacee5c90db2ff9b2 Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Thu, 11 Sep 2025 16:13:13 +0200 Subject: [PATCH] Refactoring: Rename Tool -> ToolInterface, TypeSafeTool -> Tool --- mcp-server/src/index.ts | 10 ++++---- mcp-server/src/interfaces/Tool.ts | 25 ++++++++----------- mcp-server/src/tools/HelloWorldTool.ts | 6 ++--- .../{ToolPrintText.ts => PrintTextTool.ts} | 6 ++--- 4 files changed, 22 insertions(+), 25 deletions(-) rename mcp-server/src/tools/{ToolPrintText.ts => PrintTextTool.ts} (94%) diff --git a/mcp-server/src/index.ts b/mcp-server/src/index.ts index 4f999c3..b103288 100644 --- a/mcp-server/src/index.ts +++ b/mcp-server/src/index.ts @@ -22,9 +22,9 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { CallToolRequestSchema, CallToolResult, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js"; import { WebSocketServer, WebSocket } from "ws"; -import { Tool } from "./interfaces/Tool.js"; +import { ToolInterface } from "./interfaces/Tool.js"; import { HelloWorldTool } from "./tools/HelloWorldTool.js"; -import { ToolPrintText } from "./tools/ToolPrintText.js"; +import { PrintTextTool } from "./tools/PrintTextTool"; /** * Main MCP server implementation for Penpot integration. @@ -34,7 +34,7 @@ import { ToolPrintText } from "./tools/ToolPrintText.js"; */ class PenpotMcpServer { private readonly server: Server; - private readonly tools: Map; + private readonly tools: Map; private readonly wsServer: WebSocketServer; private readonly connectedClients: Set = new Set(); private app: any; // Express app @@ -65,7 +65,7 @@ class PenpotMcpServer { } ); - this.tools = new Map(); + this.tools = new Map(); this.wsServer = new WebSocketServer({ port: 8080 }); this.setupMcpHandlers(); @@ -80,7 +80,7 @@ class PenpotMcpServer { * the internal registry for later execution. */ private registerTools(): void { - const toolInstances: Tool[] = [new HelloWorldTool(), new ToolPrintText(this.connectedClients)]; + const toolInstances: ToolInterface[] = [new HelloWorldTool(), new PrintTextTool(this.connectedClients)]; for (const tool of toolInstances) { this.tools.set(tool.definition.name, tool); diff --git a/mcp-server/src/interfaces/Tool.ts b/mcp-server/src/interfaces/Tool.ts index 9a8f07c..5f867c3 100644 --- a/mcp-server/src/interfaces/Tool.ts +++ b/mcp-server/src/interfaces/Tool.ts @@ -5,12 +5,12 @@ import "reflect-metadata"; import { ToolResponse } from "./ToolResponse"; /** - * Defines the contract for MCP tool implementations. + * Base interface for MCP tool implementations. * - * This interface maintains compatibility with the MCP protocol while - * supporting both type-safe and traditional implementations. + * This interface maintains compatibility with the MCP protocol. + * Most implementations should extend the Tool abstract class instead. */ -export interface Tool { +export interface ToolInterface { /** * The tool's unique identifier and metadata definition. */ @@ -37,12 +37,12 @@ interface PropertyMetadata { /** * Base class for type-safe tools with automatic schema generation and validation. * - * This class directly implements the Tool interface while providing type safety - * through automatic validation and strongly-typed protected methods. + * This class provides type safety through automatic validation and strongly-typed + * protected methods. All tools should extend this class. * * @template TArgs - The strongly-typed arguments class for this tool */ -export abstract class TypeSafeTool implements Tool { +export abstract class Tool implements ToolInterface { private _definition: MCPTool | undefined; constructor(private ArgsClass: new () => TArgs) {} @@ -81,7 +81,7 @@ export abstract class TypeSafeTool implements Tool { } // Call the type-safe implementation - return await this.executeTypeSafe(argsInstance); + return await this.executeCore(argsInstance); } catch (error) { if (error instanceof Error) { throw error; @@ -201,12 +201,9 @@ export abstract class TypeSafeTool implements Tool { protected abstract getToolDescription(): string; /** - * Executes the tool with strongly-typed, pre-validated arguments. + * Executes the tool's core logic. * - * This method receives fully validated and typed arguments, providing - * complete type safety without any casting or manual validation. - * - * @param args - The validated, strongly-typed arguments + * @param args - The (typed) tool arguments */ - protected abstract executeTypeSafe(args: TArgs): Promise; + protected abstract executeCore(args: TArgs): Promise; } diff --git a/mcp-server/src/tools/HelloWorldTool.ts b/mcp-server/src/tools/HelloWorldTool.ts index b86f5d5..59e0d95 100644 --- a/mcp-server/src/tools/HelloWorldTool.ts +++ b/mcp-server/src/tools/HelloWorldTool.ts @@ -1,5 +1,5 @@ import { IsNotEmpty, IsString } from "class-validator"; -import { TypeSafeTool } from "../interfaces/Tool.js"; +import { Tool } from "../interfaces/Tool.js"; import "reflect-metadata"; import type { ToolResponse } from "../interfaces/ToolResponse.js"; import { TextResponse } from "../interfaces/ToolResponse.js"; @@ -22,7 +22,7 @@ export class HelloWorldArgs { * This tool directly implements the Tool interface while maintaining full * type safety through the protected executeTypeSafe method. */ -export class HelloWorldTool extends TypeSafeTool { +export class HelloWorldTool extends Tool { constructor() { super(HelloWorldArgs); } @@ -43,7 +43,7 @@ export class HelloWorldTool extends TypeSafeTool { * * @param args - The validated HelloWorldArgs instance */ - protected async executeTypeSafe(args: HelloWorldArgs): Promise { + protected async executeCore(args: HelloWorldArgs): Promise { return new TextResponse( `Hello, ${args.name}! This greeting was generated with full type safety and automatic validation.` ); diff --git a/mcp-server/src/tools/ToolPrintText.ts b/mcp-server/src/tools/PrintTextTool.ts similarity index 94% rename from mcp-server/src/tools/ToolPrintText.ts rename to mcp-server/src/tools/PrintTextTool.ts index ce85607..2f10fab 100644 --- a/mcp-server/src/tools/ToolPrintText.ts +++ b/mcp-server/src/tools/PrintTextTool.ts @@ -1,5 +1,5 @@ import { IsNotEmpty, IsString } from "class-validator"; -import { TypeSafeTool } from "../interfaces/Tool.js"; +import { Tool } from "../interfaces/Tool.js"; import { PluginTaskPrintText, PluginTaskPrintTextParams } from "../interfaces/PluginTask.js"; import type { ToolResponse } from "../interfaces/ToolResponse.js"; import { TextResponse } from "../interfaces/ToolResponse.js"; @@ -23,7 +23,7 @@ export class PrintTextArgs { * This tool sends a PluginTaskPrintText to connected plugin instances, * instructing them to create and position text elements in the canvas. */ -export class ToolPrintText extends TypeSafeTool { +export class PrintTextTool extends Tool { private connectedClients: Set; // WebSocket clients /** @@ -52,7 +52,7 @@ export class ToolPrintText extends TypeSafeTool { * * @param args - The validated PrintTextArgs instance */ - protected async executeTypeSafe(args: PrintTextArgs): Promise { + protected async executeCore(args: PrintTextArgs): Promise { try { // Create the plugin task const taskParams = new PluginTaskPrintTextParams(args.text);