export_object
Export a Penpot design object as an image by specifying file, page, object IDs, format, and scale.
Instructions
Export a Penpot design object as an image.
Args:
file_id: The ID of the Penpot file
page_id: The ID of the page containing the object
object_id: The ID of the object to export
export_type: Image format (png, svg, etc.)
scale: Scale factor for the exported image
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| file_id | Yes | ||
| page_id | Yes | ||
| object_id | Yes | ||
| export_type | No | png | |
| scale | No |
Implementation Reference
- penpot_mcp/server/mcp_server.py:216-269 (handler)The primary handler function for the 'export_object' MCP tool. It orchestrates the export by calling the PenpotAPI.export_and_download method, handles temporary files, creates an MCP Image object, and optionally serves the image via HTTP.@self.mcp.tool() def export_object( file_id: str, page_id: str, object_id: str, export_type: str = "png", scale: int = 1) -> Image: """Export a Penpot design object as an image. Args: file_id: The ID of the Penpot file page_id: The ID of the page containing the object object_id: The ID of the object to export export_type: Image format (png, svg, etc.) scale: Scale factor for the exported image """ temp_filename = None try: import tempfile temp_dir = tempfile.gettempdir() temp_filename = os.path.join(temp_dir, f"{object_id}.{export_type}") output_path = self.api.export_and_download( file_id=file_id, page_id=page_id, object_id=object_id, export_type=export_type, scale=scale, save_to_file=temp_filename ) with open(output_path, "rb") as f: file_content = f.read() image = Image(data=file_content, format=export_type) # If HTTP server is enabled, add the image to the server if self.image_server and self.image_server.is_running: image_id = hashlib.md5(f"{file_id}:{page_id}:{object_id}".encode()).hexdigest() # Use the current image_server_url to ensure the correct port image_url = self.image_server.add_image(image_id, file_content, export_type) # Add HTTP URL to the image metadata image.http_url = image_url return image except Exception as e: if isinstance(e, CloudFlareError): raise Exception(f"CloudFlare Protection: {str(e)}") else: raise Exception(f"Export failed: {str(e)}") finally: if temp_filename and os.path.exists(temp_filename): try: os.remove(temp_filename) except Exception as e: print(f"Warning: Failed to delete temporary file {temp_filename}: {str(e)}")
- penpot_mcp/api/penpot_api.py:771-816 (helper)Core helper method 'export_and_download' in PenpotAPI class that handles the actual Penpot API interactions: creates an export job and downloads the resulting image file.def export_and_download(self, file_id: str, page_id: str, object_id: str, save_to_file: Optional[str] = None, export_type: str = "png", scale: int = 1, name: str = "Board", suffix: str = "", email: Optional[str] = None, password: Optional[str] = None, profile_id: Optional[str] = None) -> Union[bytes, str]: """ Create and download an export in one step. This is a convenience method that combines create_export and get_export_resource. Args: file_id: The file ID page_id: The page ID object_id: The object ID to export save_to_file: Path to save the file (if None, returns the content) export_type: Type of export (png, svg, pdf) scale: Scale factor for the export name: Name for the export suffix: Suffix to add to the export name email: Email for authentication (if different from instance) password: Password for authentication (if different from instance) profile_id: Optional profile ID (if not provided, will be fetched automatically) Returns: Either the file content as bytes, or the path to the saved file """ # Create the export resource_id = self.create_export( file_id=file_id, page_id=page_id, object_id=object_id, export_type=export_type, scale=scale, email=email, password=password, profile_id=profile_id ) # Download the resource return self.get_export_resource( resource_id=resource_id, save_to_file=save_to_file, email=email, password=password )
- penpot_mcp/api/penpot_api.py:584-671 (helper)Supporting helper 'create_export' that sends the Transit+JSON payload to Penpot's /export endpoint to initiate the object export job.def create_export(self, file_id: str, page_id: str, object_id: str, export_type: str = "png", scale: int = 1, email: Optional[str] = None, password: Optional[str] = None, profile_id: Optional[str] = None): """ Create an export job for a Penpot object. Args: file_id: The file ID page_id: The page ID object_id: The object ID to export export_type: Type of export (png, svg, pdf) scale: Scale factor for the export name: Name for the export suffix: Suffix to add to the export name email: Email for authentication (if different from instance) password: Password for authentication (if different from instance) profile_id: Optional profile ID (if not provided, will be fetched automatically) Returns: Export resource ID """ # This uses the cookie auth approach, which requires login token = self.login_for_export(email, password) # If profile_id is not provided, get it from instance variable if not profile_id: profile_id = self.profile_id if not profile_id: raise ValueError("Profile ID not available. It should be automatically extracted during login.") # Build the URL for export creation url = f"{self.base_url}/export" # Set up the data for the export payload = { "~:wait": True, "~:exports": [ {"~:type": f"~:{export_type}", "~:suffix": "", "~:scale": scale, "~:page-id": f"~u{page_id}", "~:file-id": f"~u{file_id}", "~:name": "", "~:object-id": f"~u{object_id}"} ], "~:profile-id": f"~u{profile_id}", "~:cmd": "~:export-shapes" } if self.debug: print("\nCreating export with parameters:") print(json.dumps(payload, indent=2)) # Create a session with the auth token export_session = requests.Session() export_session.cookies.set("auth-token", token) headers = { "Content-Type": "application/transit+json", "Accept": "application/transit+json", "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" } # Make the request response = export_session.post(url, json=payload, headers=headers) if self.debug and response.status_code != 200: print(f"\nError response: {response.status_code}") print(f"Response text: {response.text}") response.raise_for_status() # Parse the response data = response.json() if self.debug: print("\nExport created successfully") print(f"Response: {json.dumps(data, indent=2)}") # Extract and return the resource ID resource_id = data.get("~:id") if not resource_id: raise ValueError("Resource ID not found in response") return resource_id
- penpot_mcp/api/penpot_api.py:672-770 (helper)Supporting helper 'get_export_resource' that polls/retrieves the exported image content from Penpot using the resource ID.def get_export_resource(self, resource_id: str, save_to_file: Optional[str] = None, email: Optional[str] = None, password: Optional[str] = None) -> Union[bytes, str]: """ Download an export resource by ID. Args: resource_id: The resource ID from create_export save_to_file: Path to save the file (if None, returns the content) email: Email for authentication (if different from instance) password: Password for authentication (if different from instance) Returns: Either the file content as bytes, or the path to the saved file """ # This uses the cookie auth approach, which requires login token = self.login_for_export(email, password) # Build the URL for the resource url = f"{self.base_url}/export" payload = { "~:wait": False, "~:cmd": "~:get-resource", "~:id": resource_id } headers = { "Content-Type": "application/transit+json", "Accept": "*/*", "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" } if self.debug: print(f"\nFetching export resource: {url}") # Create a session with the auth token export_session = requests.Session() export_session.cookies.set("auth-token", token) # Make the request response = export_session.post(url, json=payload, headers=headers) if self.debug and response.status_code != 200: print(f"\nError response: {response.status_code}") print(f"Response headers: {response.headers}") response.raise_for_status() # Get the content type content_type = response.headers.get('Content-Type', '') if self.debug: print(f"\nResource fetched successfully") print(f"Content-Type: {content_type}") print(f"Content length: {len(response.content)} bytes") # Determine filename if saving to file if save_to_file: if os.path.isdir(save_to_file): # If save_to_file is a directory, we need to figure out the filename filename = None # Try to get filename from Content-Disposition header content_disp = response.headers.get('Content-Disposition', '') if 'filename=' in content_disp: filename = content_disp.split('filename=')[1].strip('"\'') # If we couldn't get a filename, use the resource_id with an extension if not filename: ext = content_type.split('/')[-1].split(';')[0] if ext in ('jpeg', 'png', 'pdf', 'svg+xml'): if ext == 'svg+xml': ext = 'svg' filename = f"{resource_id}.{ext}" else: filename = f"{resource_id}" save_path = os.path.join(save_to_file, filename) else: # Use the provided path directly save_path = save_to_file # Ensure the directory exists os.makedirs(os.path.dirname(os.path.abspath(save_path)), exist_ok=True) # Save the content to file with open(save_path, 'wb') as f: f.write(response.content) if self.debug: print(f"\nSaved resource to {save_path}") return save_path else: # Return the content return response.content
- penpot_mcp/server/mcp_server.py:216-216 (registration)The @self.mcp.tool() decorator registers the export_object function as an MCP tool named 'export_object'.@self.mcp.tool()