Skip to main content
Glama
client.py8.63 kB
"""Client for connecting to the Penpot MCP server.""" import asyncio from typing import Any, Dict, List, Optional from mcp import ClientSession, StdioServerParameters from mcp.client.stdio import stdio_client class PenpotMCPClient: """Client for interacting with the Penpot MCP server.""" def __init__(self, server_command="python", server_args=None, env=None): """ Initialize the Penpot MCP client. Args: server_command: The command to run the server server_args: Arguments to pass to the server command env: Environment variables for the server process """ self.server_command = server_command self.server_args = server_args or ["-m", "penpot_mcp.server.mcp_server"] self.env = env self.session = None async def connect(self): """ Connect to the MCP server. Returns: The client session """ # Create server parameters for stdio connection server_params = StdioServerParameters( command=self.server_command, args=self.server_args, env=self.env, ) # Connect to the server read, write = await stdio_client(server_params).__aenter__() self.session = await ClientSession(read, write).__aenter__() # Initialize the connection await self.session.initialize() return self.session async def disconnect(self): """Disconnect from the server.""" if self.session: await self.session.__aexit__(None, None, None) self.session = None async def list_resources(self) -> List[Dict[str, Any]]: """ List available resources from the server. Returns: List of resource information """ if not self.session: raise RuntimeError("Not connected to server") return await self.session.list_resources() async def list_tools(self) -> List[Dict[str, Any]]: """ List available tools from the server. Returns: List of tool information """ if not self.session: raise RuntimeError("Not connected to server") return await self.session.list_tools() async def get_server_info(self) -> Dict[str, Any]: """ Get server information. Returns: Server information """ if not self.session: raise RuntimeError("Not connected to server") info, _ = await self.session.read_resource("server://info") return info async def list_projects(self) -> Dict[str, Any]: """ List Penpot projects. Returns: Project information """ if not self.session: raise RuntimeError("Not connected to server") return await self.session.call_tool("list_projects") async def get_project(self, project_id: str) -> Dict[str, Any]: """ Get details for a specific project. Args: project_id: The project ID Returns: Project information """ if not self.session: raise RuntimeError("Not connected to server") return await self.session.call_tool("get_project", {"project_id": project_id}) async def get_project_files(self, project_id: str) -> Dict[str, Any]: """ Get files for a specific project. Args: project_id: The project ID Returns: File information """ if not self.session: raise RuntimeError("Not connected to server") return await self.session.call_tool("get_project_files", {"project_id": project_id}) async def get_file(self, file_id: str, features: Optional[List[str]] = None, project_id: Optional[str] = None) -> Dict[str, Any]: """ Get details for a specific file. Args: file_id: The file ID features: List of features to include project_id: Optional project ID Returns: File information """ if not self.session: raise RuntimeError("Not connected to server") params = {"file_id": file_id} if features: params["features"] = features if project_id: params["project_id"] = project_id return await self.session.call_tool("get_file", params) async def get_components(self) -> Dict[str, Any]: """ Get components from the server. Returns: Component information """ if not self.session: raise RuntimeError("Not connected to server") components, _ = await self.session.read_resource("content://components") return components async def export_object(self, file_id: str, page_id: str, object_id: str, export_type: str = "png", scale: int = 1, save_to_file: Optional[str] = None) -> Dict[str, Any]: """ Export an object from a Penpot file. Args: file_id: The ID of the file containing the object page_id: The ID of the page containing the object object_id: The ID of the object to export export_type: Export format (png, svg, pdf) scale: Scale factor for the export save_to_file: Optional path to save the exported file Returns: If save_to_file is None: Dictionary with the exported image data If save_to_file is provided: Dictionary with the saved file path """ if not self.session: raise RuntimeError("Not connected to server") params = { "file_id": file_id, "page_id": page_id, "object_id": object_id, "export_type": export_type, "scale": scale } result = await self.session.call_tool("export_object", params) # The result is now directly an Image object which has 'data' and 'format' fields # If the client wants to save the file if save_to_file: import os # Create directory if it doesn't exist os.makedirs(os.path.dirname(os.path.abspath(save_to_file)), exist_ok=True) # Save to file with open(save_to_file, "wb") as f: f.write(result["data"]) return {"file_path": save_to_file, "format": result.get("format")} # Otherwise return the result as is return result async def run_client_example(): """Run a simple example using the client.""" # Create and connect the client client = PenpotMCPClient() await client.connect() try: # Get server info print("Getting server info...") server_info = await client.get_server_info() print(f"Server info: {server_info}") # List projects print("\nListing projects...") projects_result = await client.list_projects() if "error" in projects_result: print(f"Error: {projects_result['error']}") else: projects = projects_result.get("projects", []) print(f"Found {len(projects)} projects:") for project in projects[:5]: # Show first 5 projects print(f"- {project.get('name', 'Unknown')} (ID: {project.get('id', 'N/A')})") # Example of exporting an object (uncomment and update with actual IDs to test) """ print("\nExporting object...") # Replace with actual IDs from your Penpot account export_result = await client.export_object( file_id="your-file-id", page_id="your-page-id", object_id="your-object-id", export_type="png", scale=2, save_to_file="exported_object.png" ) print(f"Export saved to: {export_result.get('file_path')}") # Or get the image data directly without saving image_data = await client.export_object( file_id="your-file-id", page_id="your-page-id", object_id="your-object-id" ) print(f"Received image in format: {image_data.get('format')}") print(f"Image size: {len(image_data.get('data'))} bytes") """ finally: # Disconnect from the server await client.disconnect() def main(): """Run the client example.""" asyncio.run(run_client_example()) if __name__ == "__main__": main()

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/montevive/penpot-mcp'

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