Skip to main content
Glama

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
NameRequiredDescriptionDefault
file_idYes
page_idYes
object_idYes
export_typeNopng
scaleNo

Implementation Reference

  • 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)}")
  • 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
        )
  • 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
  • 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
  • The @self.mcp.tool() decorator registers the export_object function as an MCP tool named 'export_object'.
    @self.mcp.tool()

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