diff --git a/mcp-server/.prettierignore b/mcp-server/.prettierignore index dd44972..6be0b8d 100644 --- a/mcp-server/.prettierignore +++ b/mcp-server/.prettierignore @@ -1 +1,3 @@ *.md +*.yml + diff --git a/mcp-server/data/prompts.yml b/mcp-server/data/prompts.yml index e5d4826..643aa16 100644 --- a/mcp-server/data/prompts.yml +++ b/mcp-server/data/prompts.yml @@ -22,3 +22,5 @@ initial_instructions: | generateMarkup(shapes: Shape[], options?: { type?: "html" | "svg"; }): string; + + You have hereby read the 'Penpot High-Level Overview' and need not use a tool to read it again. diff --git a/mcp-server/src/ConfigurationLoader.ts b/mcp-server/src/ConfigurationLoader.ts index 500b873..f8c43c5 100644 --- a/mcp-server/src/ConfigurationLoader.ts +++ b/mcp-server/src/ConfigurationLoader.ts @@ -9,7 +9,7 @@ import { createLogger } from "./logger.js"; */ export interface PromptsConfig { /** Initial instructions displayed when the server starts or connects to a client */ - initial_instructions?: string; + initial_instructions: string; [key: string]: any; // Allow for future extension with additional prompt types } @@ -51,24 +51,16 @@ export class ConfigurationLoader { const promptsPath = join(this.baseDir, "..", "data", "prompts.yml"); if (!existsSync(promptsPath)) { - this.logger.warn(`Prompts configuration file not found at ${promptsPath}, using defaults`); - this.promptsConfig = {}; - return this.promptsConfig; + throw new Error(`Prompts configuration file not found at ${promptsPath}, using defaults`); } - try { - const fileContent = readFileSync(promptsPath, "utf8"); - const parsedConfig = yaml.load(fileContent) as PromptsConfig; + const fileContent = readFileSync(promptsPath, "utf8"); + const parsedConfig = yaml.load(fileContent) as PromptsConfig; - this.promptsConfig = parsedConfig || {}; - this.logger.info(`Loaded prompts configuration from ${promptsPath}`); + this.promptsConfig = parsedConfig || {}; + this.logger.info(`Loaded prompts configuration from ${promptsPath}`); - return this.promptsConfig; - } catch (error) { - this.logger.error(error, `Failed to load prompts configuration from ${promptsPath}`); - this.promptsConfig = {}; - return this.promptsConfig; - } + return this.promptsConfig; } /** @@ -76,7 +68,7 @@ export class ConfigurationLoader { * * @returns The initial instructions string, or undefined if not configured */ - public getInitialInstructions(): string | undefined { + public getInitialInstructions(): string { const config = this.getPromptsConfig(); return config.initial_instructions; } diff --git a/mcp-server/src/PenpotMcpServer.ts b/mcp-server/src/PenpotMcpServer.ts index c1abd96..5654cf8 100644 --- a/mcp-server/src/PenpotMcpServer.ts +++ b/mcp-server/src/PenpotMcpServer.ts @@ -9,6 +9,7 @@ import { PluginBridge } from "./PluginBridge"; import { ConfigurationLoader } from "./ConfigurationLoader"; import { createLogger } from "./logger"; import { Tool } from "./Tool"; +import { HighLevelOverviewTool } from "./tools/HighLevelOverviewTool"; export class PenpotMcpServer { private readonly logger = createLogger("PenpotMcpServer"); @@ -29,7 +30,6 @@ export class PenpotMcpServer { this.port = port; const instructions = this.configLoader.getInitialInstructions(); - this.logger.info("Instructions: %s", instructions ?? ""); this.server = new McpServer( { name: "penpot-mcp-server", @@ -46,11 +46,16 @@ export class PenpotMcpServer { this.registerTools(); } + public getInitialInstructions(): string { + return this.configLoader.getInitialInstructions(); + } + private registerTools(): void { const toolInstances: Tool[] = [ new HelloWorldTool(this), new PrintTextTool(this), new ExecuteCodeTool(this), + new HighLevelOverviewTool(this), ]; for (const tool of toolInstances) { diff --git a/mcp-server/src/Tool.ts b/mcp-server/src/Tool.ts index cf66379..8ffc6a4 100644 --- a/mcp-server/src/Tool.ts +++ b/mcp-server/src/Tool.ts @@ -1,7 +1,4 @@ import { z } from "zod"; -import { Tool as MCPTool } from "@modelcontextprotocol/sdk/types.js"; -import { validate, ValidationError } from "class-validator"; -import { plainToClass } from "class-transformer"; import "reflect-metadata"; import { TextResponse, ToolResponse } from "./ToolResponse"; import type { PenpotMcpServer } from "./PenpotMcpServer"; diff --git a/mcp-server/src/tools/HighLevelOverviewTool.ts b/mcp-server/src/tools/HighLevelOverviewTool.ts new file mode 100644 index 0000000..ada8829 --- /dev/null +++ b/mcp-server/src/tools/HighLevelOverviewTool.ts @@ -0,0 +1,26 @@ +import { EmptyToolArgs, Tool } from "../Tool"; +import "reflect-metadata"; +import type { ToolResponse } from "../ToolResponse"; +import { TextResponse } from "../ToolResponse"; +import { PenpotMcpServer } from "../PenpotMcpServer"; + +export class HighLevelOverviewTool extends Tool { + constructor(mcpServer: PenpotMcpServer) { + super(mcpServer, EmptyToolArgs.schema); + } + + public getToolName(): string { + return "high_level_overview"; + } + + public getToolDescription(): string { + return ( + "Returns basic high-level instructions on the usage of Penpot-related tools and the Penpot API. " + + "If you have already read the 'Penpot High-Level Overview', you must not call this tool." + ); + } + + protected async executeCore(args: EmptyToolArgs): Promise { + return new TextResponse(this.mcpServer.getInitialInstructions()); + } +}