Skip to main content
Glama
goonoo
by goonoo

upload_and_attach_file_to_page

Upload a file to Notion and attach it to a specific page, returning a temporary download URL with metadata.

Instructions

Upload a file to Notion and attach it to a specific page, returning download URL.

This function performs the complete workflow:
1. Upload file to Notion
2. Create file block and attach to specified page
3. Extract and return download URL with metadata

Args:
    file_path: Path to the file to upload
    page_id: Notion page ID to attach the file to (with or without dashes)
    notion_token: Notion API token for authentication (optional if NOTION_API_TOKEN env var is set)
    file_name: Optional custom filename (defaults to original filename)
    caption: Optional caption for the file block
    
Returns:
    Dictionary containing:
    - file_upload_id: The uploaded file ID
    - file_block_id: The created file block ID
    - download_url: Temporary download URL (expires in 1 hour)
    - expiry_time: ISO timestamp when download URL expires
    - filename: Name of the file
    - content_type: MIME type of the file
    - file_size: Size of the file in bytes
    
Raises:
    Exception: If file doesn't exist, exceeds size limit, or API call fails
    
Example:
    result = await upload_and_attach_file_to_page(
        "document.pdf", 
        "2785bbc0e5c281f48dfae9a48f53f6a6",
        notion_token="ntn_xxx"
    )
    download_url = result["download_url"]

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
file_pathYes
page_idYes
notion_tokenNo
file_nameNo
captionNo

Implementation Reference

  • The main handler function that executes the tool logic: uploads the file to Notion via API, creates a file block, appends it to the specified page, and returns metadata including the temporary download URL.
    async def upload_and_attach_file_to_page(
        file_path: str,
        page_id: str,
        notion_token: Optional[str] = None,
        file_name: Optional[str] = None,
        caption: Optional[str] = None
    ) -> Dict[str, Any]:
        """
        Upload a file to Notion and attach it to a specific page, returning download URL.
        
        This function performs the complete workflow:
        1. Upload file to Notion
        2. Create file block and attach to specified page
        3. Extract and return download URL with metadata
        
        Args:
            file_path: Path to the file to upload
            page_id: Notion page ID to attach the file to (with or without dashes)
            notion_token: Notion API token for authentication (optional if NOTION_API_TOKEN env var is set)
            file_name: Optional custom filename (defaults to original filename)
            caption: Optional caption for the file block
            
        Returns:
            Dictionary containing:
            - file_upload_id: The uploaded file ID
            - file_block_id: The created file block ID
            - download_url: Temporary download URL (expires in 1 hour)
            - expiry_time: ISO timestamp when download URL expires
            - filename: Name of the file
            - content_type: MIME type of the file
            - file_size: Size of the file in bytes
            
        Raises:
            Exception: If file doesn't exist, exceeds size limit, or API call fails
            
        Example:
            result = await upload_and_attach_file_to_page(
                "document.pdf", 
                "2785bbc0e5c281f48dfae9a48f53f6a6",
                notion_token="ntn_xxx"
            )
            download_url = result["download_url"]
        """
        # Get token from parameter or environment variable
        token = notion_token or os.environ.get('NOTION_API_TOKEN')
        if not token:
            raise Exception("Notion API token is required. Pass it as a parameter or set NOTION_API_TOKEN environment variable")
            
        # Validate file exists
        file_path_obj = Path(file_path)
        if not file_path_obj.exists():
            raise Exception(f"File not found: {file_path}")
        
        # Check file size (max 20MB)
        file_size = file_path_obj.stat().st_size
        max_size = 20 * 1024 * 1024  # 20MB in bytes
        if file_size > max_size:
            raise Exception(f"File size ({file_size / 1024 / 1024:.2f}MB) exceeds 20MB limit")
        
        # Use custom filename if provided, otherwise use original
        upload_filename = file_name or file_path_obj.name
        
        # Notion API headers
        headers = {
            "Authorization": f"Bearer {token}",
            "Notion-Version": "2022-06-28"
        }
        
        async with httpx.AsyncClient() as client:
            try:
                # Step 1: Upload file to Notion
                # Create file upload object
                create_upload_response = await client.post(
                    "https://api.notion.com/v1/file_uploads",
                    headers={**headers, "Content-Type": "application/json"},
                    json={"name": upload_filename}
                )
                create_upload_response.raise_for_status()
                
                upload_data = create_upload_response.json()
                file_upload_id = upload_data.get("id")
                upload_url = upload_data.get("upload_url")
                
                if not file_upload_id or not upload_url:
                    raise Exception("Failed to get upload ID or URL from Notion API")
                
                # Upload file contents to the upload URL
                with open(file_path, 'rb') as f:
                    files = {"file": (upload_filename, f)}
                    upload_response = await client.post(
                        upload_url,
                        headers=headers,
                        files=files
                    )
                    upload_response.raise_for_status()
                
                # Step 2: Create file block and attach to page
                file_block_payload = {
                    "children": [
                        {
                            "object": "block",
                            "type": "file",
                            "file": {
                                "type": "file_upload",
                                "file_upload": {
                                    "id": file_upload_id
                                },
                                "caption": [
                                    {
                                        "type": "text",
                                        "text": {
                                            "content": caption or upload_filename
                                        }
                                    }
                                ] if caption or upload_filename else []
                            }
                        }
                    ]
                }
                
                # Normalize page ID (remove dashes)
                normalized_page_id = page_id.replace("-", "")
                
                attach_response = await client.patch(
                    f"https://api.notion.com/v1/blocks/{normalized_page_id}/children",
                    headers={**headers, "Content-Type": "application/json"},
                    json=file_block_payload
                )
                attach_response.raise_for_status()
                
                # Step 3: Extract file block information and download URL
                attach_data = attach_response.json()
                if not attach_data.get("results"):
                    raise Exception("Failed to create file block")
                    
                file_block = attach_data["results"][0]
                file_block_id = file_block.get("id")
                
                # Extract file information
                file_info = file_block.get("file", {})
                file_data = file_info.get("file", {})
                download_url = file_data.get("url")
                expiry_time = file_data.get("expiry_time")
                content_type = file_data.get("content_type", "application/octet-stream")
                
                if not download_url:
                    raise Exception("File block created but no download URL available")
                
                # Return comprehensive file information
                return {
                    "file_upload_id": file_upload_id,
                    "file_block_id": file_block_id,
                    "download_url": download_url,
                    "expiry_time": expiry_time,
                    "filename": upload_filename,
                    "content_type": content_type,
                    "file_size": file_size,
                    "status": "success",
                    "message": f"File '{upload_filename}' uploaded and attached successfully"
                }
                
            except httpx.HTTPStatusError as e:
                error_detail = ""
                try:
                    error_detail = e.response.json().get("message", e.response.text)
                except:
                    error_detail = e.response.text
                    
                raise Exception(f"Notion API error: {e.response.status_code} - {error_detail}")
            except Exception as e:
                raise Exception(f"Upload and attach failed: {str(e)}")
  • The @mcp.tool() decorator registers the upload_and_attach_file_to_page function with the FastMCP server instance.
    @mcp.tool()
  • Related helper tool 'upload_file_to_notion' that performs the file upload step (replicated in the main tool's logic), returning the file_upload_id. Referenced in the main tool's documentation.
    @mcp.tool()
    async def upload_file_to_notion(
        file_path: str,
        notion_token: Optional[str] = None,
        file_name: Optional[str] = None
    ) -> str:
        """
        Upload a file to Notion using the Notion API.
        
        Args:
            file_path: Path to the file to upload
            notion_token: Notion API token for authentication (optional if NOTION_API_TOKEN env var is set)
            file_name: Optional custom filename (defaults to original filename)
            
        Returns:
            The file upload ID from Notion (use upload_and_attach_file_to_page for URL)
            
        Raises:
            Exception: If file doesn't exist, exceeds size limit, or API call fails
        """
        # Get token from parameter or environment variable
        token = notion_token or os.environ.get('NOTION_API_TOKEN')
        if not token:
            raise Exception("Notion API token is required. Pass it as a parameter or set NOTION_API_TOKEN environment variable")
        # Validate file exists
        file_path_obj = Path(file_path)
        if not file_path_obj.exists():
            raise Exception(f"File not found: {file_path}")
        
        # Check file size (max 20MB)
        file_size = file_path_obj.stat().st_size
        max_size = 20 * 1024 * 1024  # 20MB in bytes
        if file_size > max_size:
            raise Exception(f"File size ({file_size / 1024 / 1024:.2f}MB) exceeds 20MB limit")
        
        # Use custom filename if provided, otherwise use original
        upload_filename = file_name or file_path_obj.name
        
        # Notion API headers
        headers = {
            "Authorization": f"Bearer {token}",
            "Notion-Version": "2022-06-28"
        }
        
        async with httpx.AsyncClient() as client:
            try:
                # Step 1: Create file upload object
                create_upload_response = await client.post(
                    "https://api.notion.com/v1/file_uploads",
                    headers={**headers, "Content-Type": "application/json"},
                    json={"name": upload_filename}
                )
                create_upload_response.raise_for_status()
                
                upload_data = create_upload_response.json()
                file_upload_id = upload_data.get("id")
                upload_url = upload_data.get("upload_url")
                
                if not file_upload_id or not upload_url:
                    raise Exception("Failed to get upload ID or URL from Notion API")
                
                # Step 2: Upload file contents to the upload URL
                with open(file_path, 'rb') as f:
                    files = {"file": (upload_filename, f)}
                    upload_response = await client.post(
                        upload_url,
                        headers=headers,
                        files=files
                    )
                    upload_response.raise_for_status()
                
                # Step 3: Complete the upload and get the final file object
                complete_response = await client.get(
                    f"https://api.notion.com/v1/file_uploads/{file_upload_id}",
                    headers=headers
                )
                complete_response.raise_for_status()
                
                final_data = complete_response.json()
                
                # Return the file_upload_id instead of URL (URL is only available after attachment to page)
                return file_upload_id
                
            except httpx.HTTPStatusError as e:
                raise Exception(f"Notion API error: {e.response.status_code} - {e.response.text}")
            except Exception as e:
                raise Exception(f"Upload failed: {str(e)}")

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/goonoo/mcp_notion_upload'

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