mirror of
https://github.com/penpot/penpot-mcp.git
synced 2026-04-25 11:18:37 +00:00
Add PenpotApiInfoTool
This commit is contained in:
parent
d972e1ed71
commit
74ff6a12df
@ -39,8 +39,9 @@ penpot-mcp/
|
|||||||
|
|
||||||
### Adding a new Tool
|
### Adding a new Tool
|
||||||
|
|
||||||
* Implement the tool class in `mcp-server/src/tools/` following the `Tool` interface.
|
1. Implement the tool class in `mcp-server/src/tools/` following the `Tool` interface.
|
||||||
* IMPORTANT: Do not catch any exceptions in the `executeCore` method. Let them propagate to be handled centrally.
|
IMPORTANT: Do not catch any exceptions in the `executeCore` method. Let them propagate to be handled centrally.
|
||||||
|
2. Register the tool in `PenpotMcpServer`.
|
||||||
|
|
||||||
Look at `PrintTextTool` as an example.
|
Look at `PrintTextTool` as an example.
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,7 @@ initial_instructions: |
|
|||||||
One of the key tools is the execute_code tool, which allows you to run JavaScript code using the Penpot Plugin API
|
One of the key tools is the execute_code tool, which allows you to run JavaScript code using the Penpot Plugin API
|
||||||
directly in the connected project.
|
directly in the connected project.
|
||||||
|
|
||||||
When writing code, a key object is the `penpot` object which provides key functionality:
|
When writing code, a key object is the `penpot` object (which is of type `Penpot`). It provides useful functionality:
|
||||||
* `penpot.selection` provides the list of elements the user has selected in the Penpot UI.
|
* `penpot.selection` provides the list of elements the user has selected in the Penpot UI.
|
||||||
If it is unclear which elements to work on, you can ask the user to select them for you.
|
If it is unclear which elements to work on, you can ask the user to select them for you.
|
||||||
* Generation of CSS content for elements:
|
* Generation of CSS content for elements:
|
||||||
@ -24,7 +24,7 @@ initial_instructions: |
|
|||||||
type?: "html" | "svg";
|
type?: "html" | "svg";
|
||||||
}): string;
|
}): string;
|
||||||
|
|
||||||
For example, to generate CSS for the selected elements, you can execute this:
|
For example, to generate CSS for the currently selected elements, you can execute this:
|
||||||
return penpot.generateStyle(penpot.selection, { type: "css", withChildren: true });
|
return penpot.generateStyle(penpot.selection, { type: "css", withChildren: true });
|
||||||
|
|
||||||
You have hereby read the 'Penpot High-Level Overview' and need not use a tool to read it again.
|
You have hereby read the 'Penpot High-Level Overview' and need not use a tool to read it again.
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import * as fs from "fs";
|
|||||||
import * as path from "path";
|
import * as path from "path";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a single API type with its documentation.
|
* Represents a single type/interface defined in the Penpot API
|
||||||
*/
|
*/
|
||||||
export class ApiType {
|
export class ApiType {
|
||||||
private readonly name: string;
|
private readonly name: string;
|
||||||
@ -24,6 +24,13 @@ export class ApiType {
|
|||||||
return this.name;
|
return this.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the overview text of this API type (which all signature/type declarations)
|
||||||
|
*/
|
||||||
|
getOverviewText() {
|
||||||
|
return this.overview;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a single markdown text document from all parts of this API type.
|
* Creates a single markdown text document from all parts of this API type.
|
||||||
*
|
*
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import { ConfigurationLoader } from "./ConfigurationLoader";
|
|||||||
import { createLogger } from "./logger";
|
import { createLogger } from "./logger";
|
||||||
import { Tool } from "./Tool";
|
import { Tool } from "./Tool";
|
||||||
import { HighLevelOverviewTool } from "./tools/HighLevelOverviewTool";
|
import { HighLevelOverviewTool } from "./tools/HighLevelOverviewTool";
|
||||||
|
import { PenpotApiInfoTool } from "./tools/PenpotApiInfoTool";
|
||||||
|
|
||||||
export class PenpotMcpServer {
|
export class PenpotMcpServer {
|
||||||
private readonly logger = createLogger("PenpotMcpServer");
|
private readonly logger = createLogger("PenpotMcpServer");
|
||||||
@ -56,6 +57,7 @@ export class PenpotMcpServer {
|
|||||||
new PrintTextTool(this),
|
new PrintTextTool(this),
|
||||||
new ExecuteCodeTool(this),
|
new ExecuteCodeTool(this),
|
||||||
new HighLevelOverviewTool(this),
|
new HighLevelOverviewTool(this),
|
||||||
|
new PenpotApiInfoTool(this),
|
||||||
];
|
];
|
||||||
|
|
||||||
for (const tool of toolInstances) {
|
for (const tool of toolInstances) {
|
||||||
|
|||||||
@ -40,16 +40,19 @@ export class ExecuteCodeTool extends Tool<ExecuteCodeArgs> {
|
|||||||
|
|
||||||
public getToolDescription(): string {
|
public getToolDescription(): string {
|
||||||
return (
|
return (
|
||||||
"Executes JavaScript code in the Penpot plugin context. " +
|
"Executes JavaScript code in the Penpot plugin context.\n" +
|
||||||
"Two objects are available: `penpot` (the Penpot API) and `storage` (an object in which arbitrary " +
|
"IMPORTANT: Before using this tool, make sure you have read the 'Penpot High-Level Overview' and know " +
|
||||||
|
"which Penpot API functionality is necessary and how to use it.\n" +
|
||||||
|
"You have access two main objects: `penpot` (the Penpot API, of type `Penpot`) and `storage` (an object in which arbitrary " +
|
||||||
"data can be stored, simply by adding a new attribute; stored attributes can be referenced in future calls " +
|
"data can be stored, simply by adding a new attribute; stored attributes can be referenced in future calls " +
|
||||||
"to this tool, so any intermediate results that could come in handy later should be stored in `storage` " +
|
"to this tool, so any intermediate results that could come in handy later should be stored in `storage` " +
|
||||||
"instead of just a fleeting variable).\n" +
|
"instead of just a fleeting variable).\n" +
|
||||||
"The tool call returns the value of the concluding `return` statement, if any.\n" +
|
"The tool call returns the value of the concluding `return` statement, if any.\n" +
|
||||||
"Any output that you generate via the `console` object will be returned to you; so you may use this" +
|
"Any output that you generate via the `console` object will be returned to you; so you may use this" +
|
||||||
"to track what your code is doing, but you should only do so only if there is an actual need!.\n" +
|
"to track what your code is doing, but you should only do so only if there is an actual need for this! " +
|
||||||
"In general, try a simple approach first, and only if it fails, try more complex code that involves " +
|
"IMPORTANT: Don't use logging prematurely!\n" +
|
||||||
"handling different cases (in particular error cases)."
|
"VERY IMPORTANT: In general, try a simple approach first, and only if it fails, try more complex code that involves " +
|
||||||
|
"handling different cases (in particular error cases) and that applies logging."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
85
mcp-server/src/tools/PenpotApiInfoTool.ts
Normal file
85
mcp-server/src/tools/PenpotApiInfoTool.ts
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
import { Tool } from "../Tool";
|
||||||
|
import "reflect-metadata";
|
||||||
|
import type { ToolResponse } from "../ToolResponse";
|
||||||
|
import { TextResponse } from "../ToolResponse";
|
||||||
|
import { PenpotMcpServer } from "../PenpotMcpServer";
|
||||||
|
import { ApiDocs } from "../ApiDocs";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Arguments class for the PenpotApiInfo tool with validation decorators.
|
||||||
|
*/
|
||||||
|
export class PenpotApiInfoArgs {
|
||||||
|
static schema = {
|
||||||
|
type: z.string().min(1, "Type name cannot be empty"),
|
||||||
|
member: z.string().optional(),
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The API type name to retrieve information for.
|
||||||
|
*/
|
||||||
|
type!: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The specific member name to retrieve (optional).
|
||||||
|
*/
|
||||||
|
member?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tool for retrieving Penpot API documentation information.
|
||||||
|
*
|
||||||
|
* This tool provides access to API type documentation loaded from YAML files,
|
||||||
|
* allowing retrieval of either full type documentation or specific member details.
|
||||||
|
*/
|
||||||
|
export class PenpotApiInfoTool extends Tool<PenpotApiInfoArgs> {
|
||||||
|
private static readonly MAX_FULL_TEXT_CHARS = 2000;
|
||||||
|
private readonly apiDocs: ApiDocs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new PenpotApiInfo tool instance.
|
||||||
|
*
|
||||||
|
* @param mcpServer - The MCP server instance
|
||||||
|
*/
|
||||||
|
constructor(mcpServer: PenpotMcpServer) {
|
||||||
|
super(mcpServer, PenpotApiInfoArgs.schema);
|
||||||
|
this.apiDocs = new ApiDocs();
|
||||||
|
}
|
||||||
|
|
||||||
|
public getToolName(): string {
|
||||||
|
return "penpot_api_info";
|
||||||
|
}
|
||||||
|
|
||||||
|
public getToolDescription(): string {
|
||||||
|
return "Retrieves Penpot API documentation for types and their members";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async executeCore(args: PenpotApiInfoArgs): Promise<ToolResponse> {
|
||||||
|
const apiType = this.apiDocs.getType(args.type);
|
||||||
|
|
||||||
|
if (!apiType) {
|
||||||
|
throw new Error(`API type "${args.type}" not found`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.member) {
|
||||||
|
// return specific member documentation
|
||||||
|
const memberDoc = apiType.getMember(args.member);
|
||||||
|
if (!memberDoc) {
|
||||||
|
throw new Error(`Member "${args.member}" not found in type "${args.type}"`);
|
||||||
|
}
|
||||||
|
return new TextResponse(memberDoc);
|
||||||
|
} else {
|
||||||
|
// return full text or overview based on length
|
||||||
|
const fullText = apiType.getFullText();
|
||||||
|
if (fullText.length <= PenpotApiInfoTool.MAX_FULL_TEXT_CHARS) {
|
||||||
|
return new TextResponse(fullText);
|
||||||
|
} else {
|
||||||
|
return new TextResponse(
|
||||||
|
apiType.getOverviewText() +
|
||||||
|
"\n\nMember details not provided (too long). " +
|
||||||
|
"Call this tool with a member name for more information."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user