Refactoring: Rename Tool -> ToolInterface, TypeSafeTool -> Tool

This commit is contained in:
Dominik Jain 2025-09-11 16:13:13 +02:00
parent e714caaef2
commit 472dcd7890
4 changed files with 22 additions and 25 deletions

View File

@ -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<string, Tool>;
private readonly tools: Map<string, ToolInterface>;
private readonly wsServer: WebSocketServer;
private readonly connectedClients: Set<WebSocket> = new Set();
private app: any; // Express app
@ -65,7 +65,7 @@ class PenpotMcpServer {
}
);
this.tools = new Map<string, Tool>();
this.tools = new Map<string, ToolInterface>();
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);

View File

@ -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<TArgs extends object> implements Tool {
export abstract class Tool<TArgs extends object> implements ToolInterface {
private _definition: MCPTool | undefined;
constructor(private ArgsClass: new () => TArgs) {}
@ -81,7 +81,7 @@ export abstract class TypeSafeTool<TArgs extends object> 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<TArgs extends object> 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<ToolResponse>;
protected abstract executeCore(args: TArgs): Promise<ToolResponse>;
}

View File

@ -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<HelloWorldArgs> {
export class HelloWorldTool extends Tool<HelloWorldArgs> {
constructor() {
super(HelloWorldArgs);
}
@ -43,7 +43,7 @@ export class HelloWorldTool extends TypeSafeTool<HelloWorldArgs> {
*
* @param args - The validated HelloWorldArgs instance
*/
protected async executeTypeSafe(args: HelloWorldArgs): Promise<ToolResponse> {
protected async executeCore(args: HelloWorldArgs): Promise<ToolResponse> {
return new TextResponse(
`Hello, ${args.name}! This greeting was generated with full type safety and automatic validation.`
);

View File

@ -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<PrintTextArgs> {
export class PrintTextTool extends Tool<PrintTextArgs> {
private connectedClients: Set<any>; // WebSocket clients
/**
@ -52,7 +52,7 @@ export class ToolPrintText extends TypeSafeTool<PrintTextArgs> {
*
* @param args - The validated PrintTextArgs instance
*/
protected async executeTypeSafe(args: PrintTextArgs): Promise<ToolResponse> {
protected async executeCore(args: PrintTextArgs): Promise<ToolResponse> {
try {
// Create the plugin task
const taskParams = new PluginTaskPrintTextParams(args.text);