Skip to main content
Glama
resources.py13.3 kB
""" Resource-based operations for Google Docs MCP Server. Handles uploading files and images using resource identifiers from the mcp_mapped_resource_lib library for sharing blobs across MCP servers via mapped Docker volumes. """ import os from typing import Any from fastmcp.exceptions import ToolError from googleapiclient.http import MediaFileUpload from mcp_mapped_resource_lib import BlobStorage from google_docs_mcp.auth import get_drive_client from google_docs_mcp.utils import log def _get_blob_storage() -> BlobStorage: """ Get or create the BlobStorage instance. Configuration is controlled via environment variables: - BLOB_STORAGE_ROOT: Required. Path to blob storage directory - BLOB_STORAGE_MAX_SIZE_MB: Optional. Max file size in MB (default: 100) - BLOB_STORAGE_TTL_HOURS: Optional. Time-to-live for blobs in hours (default: 24) Returns: BlobStorage instance configured for this MCP server Raises: ToolError: If BLOB_STORAGE_ROOT environment variable is not set """ storage_root = os.environ.get("BLOB_STORAGE_ROOT") if not storage_root: raise ToolError( "BLOB_STORAGE_ROOT environment variable not set. " "Please configure the blob storage directory." ) # Get configurable settings from environment variables with defaults max_size_mb = int(os.environ.get("BLOB_STORAGE_MAX_SIZE_MB", "100")) ttl_hours = int(os.environ.get("BLOB_STORAGE_TTL_HOURS", "24")) log(f"Initializing blob storage: root={storage_root}, max_size={max_size_mb}MB, ttl={ttl_hours}h") # Create storage with configurable settings # Allow all common image and document types return BlobStorage( storage_root=storage_root, max_size_mb=max_size_mb, default_ttl_hours=ttl_hours, allowed_mime_types=[ "image/*", "application/pdf", "text/*", "application/vnd.openxmlformats-officedocument.*", "application/msword", "application/vnd.ms-excel", "application/vnd.ms-powerpoint", ], enable_deduplication=True, ) def upload_image_to_drive_from_resource( resource_id: str, name: str | None = None, parent_folder_id: str | None = None, ) -> str: """ Upload an image to Google Drive from a resource identifier. The resource identifier references a blob in the mapped blob storage volume that is shared across MCP servers. Args: resource_id: Resource identifier (e.g., "blob://1733437200-a3f9d8c2b1e4f6a7.png") name: Name for the file in Drive (if not provided, uses resource filename) parent_folder_id: Optional parent folder ID (None for root) Returns: Success message with file ID and link Raises: ToolError: For permission, upload, or resource not found errors """ drive = get_drive_client() storage = _get_blob_storage() log(f'Uploading image from resource "{resource_id}" to Drive') try: # Extract blob ID from resource identifier if resource_id.startswith("blob://"): blob_id = resource_id[7:] # Remove "blob://" prefix else: blob_id = resource_id # Get metadata to verify it exists and is an image metadata = storage.get_metadata(blob_id) if not metadata: raise ToolError(f"Resource not found: {resource_id}") mime_type = metadata.get("mime_type", "application/octet-stream") if not mime_type.startswith("image/"): raise ToolError( f"Resource is not an image (MIME type: {mime_type}). " f"Use upload_file_to_drive_from_resource instead." ) # Get the file path for upload file_path = storage.get_file_path(blob_id) if not file_path or not os.path.exists(file_path): raise ToolError(f"Resource file not found: {resource_id}") # Use resource filename if name not provided if not name: name = metadata.get("filename", blob_id) # Prepare metadata file_metadata: dict[str, Any] = {"name": name} if parent_folder_id: file_metadata["parents"] = [parent_folder_id] # Create media upload from file media = MediaFileUpload( file_path, mimetype=mime_type, resumable=True ) # Upload file response = ( drive.files() .create( body=file_metadata, media_body=media, fields="id,name,webViewLink,mimeType,size" ) .execute() ) size_kb = int(response.get("size", 0)) / 1024 return ( f"Successfully uploaded image \"{response.get('name')}\" from resource {resource_id} " f"({size_kb:.1f} KB)\n" f"ID: {response.get('id')}\n" f"Type: {response.get('mimeType')}\n" f"Link: {response.get('webViewLink')}" ) except ToolError: raise except Exception as e: error_message = str(e) log(f"Error uploading image from resource: {error_message}") if "404" in error_message: raise ToolError("Parent folder not found. Check the parent folder ID.") if "403" in error_message: raise ToolError("Permission denied. Make sure you have write access to Drive.") raise ToolError(f"Failed to upload image from resource: {error_message}") def upload_file_to_drive_from_resource( resource_id: str, name: str | None = None, parent_folder_id: str | None = None, ) -> str: """ Upload a file to Google Drive from a resource identifier. The resource identifier references a blob in the mapped blob storage volume that is shared across MCP servers. Args: resource_id: Resource identifier (e.g., "blob://1733437200-a3f9d8c2b1e4f6a7.pdf") name: Name for the file in Drive (if not provided, uses resource filename) parent_folder_id: Optional parent folder ID (None for root) Returns: Success message with file ID and link Raises: ToolError: For permission, upload, or resource not found errors """ drive = get_drive_client() storage = _get_blob_storage() log(f'Uploading file from resource "{resource_id}" to Drive') try: # Extract blob ID from resource identifier if resource_id.startswith("blob://"): blob_id = resource_id[7:] # Remove "blob://" prefix else: blob_id = resource_id # Get metadata to verify it exists metadata = storage.get_metadata(blob_id) if not metadata: raise ToolError(f"Resource not found: {resource_id}") mime_type = metadata.get("mime_type", "application/octet-stream") # Get the file path for upload file_path = storage.get_file_path(blob_id) if not file_path or not os.path.exists(file_path): raise ToolError(f"Resource file not found: {resource_id}") # Use resource filename if name not provided if not name: name = metadata.get("filename", blob_id) # Prepare metadata file_metadata: dict[str, Any] = {"name": name} if parent_folder_id: file_metadata["parents"] = [parent_folder_id] # Create media upload from file media = MediaFileUpload( file_path, mimetype=mime_type, resumable=True ) # Upload file response = ( drive.files() .create( body=file_metadata, media_body=media, fields="id,name,webViewLink,mimeType,size" ) .execute() ) size_kb = int(response.get("size", 0)) / 1024 return ( f"Successfully uploaded file \"{response.get('name')}\" from resource {resource_id} " f"({size_kb:.1f} KB)\n" f"ID: {response.get('id')}\n" f"Type: {response.get('mimeType')}\n" f"Link: {response.get('webViewLink')}" ) except ToolError: raise except Exception as e: error_message = str(e) log(f"Error uploading file from resource: {error_message}") if "404" in error_message: raise ToolError("Parent folder not found. Check the parent folder ID.") if "403" in error_message: raise ToolError("Permission denied. Make sure you have write access to Drive.") raise ToolError(f"Failed to upload file from resource: {error_message}") def insert_image_from_resource( document_id: str, resource_id: str, index: int, width: float | None = None, height: float | None = None, ) -> str: """ Insert an image into a Google Doc from a resource identifier. The resource must first be uploaded to Google Drive, then inserted into the document. Args: document_id: The ID of the Google Document resource_id: Resource identifier (e.g., "blob://1733437200-a3f9d8c2b1e4f6a7.png") index: The index (1-based) where the image should be inserted width: Width of the image in points height: Height of the image in points Returns: Success message with details about the inserted image Raises: ToolError: For permission, upload, or resource not found errors """ from google_docs_mcp.auth import get_docs_client docs = get_docs_client() drive = get_drive_client() storage = _get_blob_storage() log(f'Inserting image from resource "{resource_id}" into document {document_id}') try: # Extract blob ID from resource identifier if resource_id.startswith("blob://"): blob_id = resource_id[7:] # Remove "blob://" prefix else: blob_id = resource_id # Get metadata to verify it exists and is an image metadata = storage.get_metadata(blob_id) if not metadata: raise ToolError(f"Resource not found: {resource_id}") mime_type = metadata.get("mime_type", "application/octet-stream") if not mime_type.startswith("image/"): raise ToolError( f"Resource is not an image (MIME type: {mime_type}). " f"Only images can be inserted into documents." ) # Get the file path for upload file_path = storage.get_file_path(blob_id) if not file_path or not os.path.exists(file_path): raise ToolError(f"Resource file not found: {resource_id}") # Upload image to Drive temporarily filename = metadata.get("filename", blob_id) file_metadata: dict[str, Any] = {"name": f"temp-{filename}"} media = MediaFileUpload( file_path, mimetype=mime_type, resumable=True ) upload_response = ( drive.files() .create( body=file_metadata, media_body=media, fields="id,webContentLink" ) .execute() ) file_id = upload_response.get("id") # Make the file publicly readable so Google Docs can access it permission = { "type": "anyone", "role": "reader" } drive.permissions().create( fileId=file_id, body=permission ).execute() # Use the direct content URL from Drive # This provides a direct download link that Google Docs can access image_url = f"https://drive.google.com/uc?export=download&id={file_id}" # Insert image into document request = { "insertInlineImage": { "uri": image_url, "location": {"index": index} } } if width is not None or height is not None: object_size = {} if width is not None: object_size["width"] = {"magnitude": width, "unit": "PT"} if height is not None: object_size["height"] = {"magnitude": height, "unit": "PT"} request["insertInlineImage"]["objectSize"] = object_size docs.documents().batchUpdate( documentId=document_id, body={"requests": [request]} ).execute() # Note: We're leaving the temp file in Drive for now # It could be cleaned up later if needed size_info = f" ({width}x{height} points)" if width or height else "" return ( f"Successfully inserted image from resource {resource_id} " f"at index {index}{size_info}\n" f"Temporary Drive file ID: {file_id}" ) except ToolError: raise except Exception as e: error_message = str(e) log(f"Error inserting image from resource: {error_message}") if "404" in error_message: raise ToolError(f"Document not found (ID: {document_id}).") if "403" in error_message: raise ToolError("Permission denied. Make sure you have access to this document.") raise ToolError(f"Failed to insert image from resource: {error_message}")

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/nickweedon/google-docs-mcp-docker'

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