diff --git a/.gitignore b/.gitignore
index c73adf2..76c4fed 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,8 +2,6 @@
 __pycache__/
 *.py[cod]
 *.so
-.mcp.json
-claude.md
 
 # ---- Packaging ---------------------------------------------------------
 *.egg-info/
@@ -22,6 +20,7 @@ venv/
 
 # ---- Secrets -----------------------------------------------------------
 client_secret.json
+.mcp.json
 
 # ---- Logs --------------------------------------------------------------
 mcp_server_debug.log
diff --git a/auth/service_decorator.py b/auth/service_decorator.py
index e2718cb..cdfd054 100644
--- a/auth/service_decorator.py
+++ b/auth/service_decorator.py
@@ -431,18 +431,29 @@ def require_multiple_services(service_configs: List[Dict[str, Any]]):
             # Both services are automatically injected
     """
     def decorator(func: Callable) -> Callable:
+        # Inspect the original function signature
+        original_sig = inspect.signature(func)
+        params = list(original_sig.parameters.values())
+        
+        # Create list of service parameter names that will be injected
+        service_param_names = [config["param_name"] for config in service_configs]
+        
+        # Filter out the service parameters from the original signature
+        # to create the wrapper signature that MCP will see
+        wrapper_params = [p for p in params if p.name not in service_param_names]
+        wrapper_sig = original_sig.replace(parameters=wrapper_params)
+        
         @wraps(func)
         async def wrapper(*args, **kwargs):
-            # Extract user_google_email
-            sig = inspect.signature(func)
-            param_names = list(sig.parameters.keys())
-
+            # Extract user_google_email - use robust approach that works with injected services
             user_google_email = None
             if 'user_google_email' in kwargs:
                 user_google_email = kwargs['user_google_email']
             else:
+                # Look for user_google_email in positional args using original function signature
+                original_param_names = list(original_sig.parameters.keys())
                 try:
-                    user_email_index = param_names.index('user_google_email')
+                    user_email_index = original_param_names.index('user_google_email')
                     if user_email_index < len(args):
                         user_google_email = args[user_email_index]
                 except ValueError:
@@ -528,6 +539,9 @@ def require_multiple_services(service_configs: List[Dict[str, Any]]):
                 error_message = _handle_token_refresh_error(e, user_google_email, "Multiple Services")
                 raise Exception(error_message)
 
+        # Set the wrapper's signature to the one without service parameters
+        # This is critical for MCP compatibility
+        wrapper.__signature__ = wrapper_sig
         return wrapper
     return decorator
 
diff --git a/core/utils.py b/core/utils.py
index 36fb154..2418bbd 100644
--- a/core/utils.py
+++ b/core/utils.py
@@ -295,12 +295,16 @@ def handle_http_errors(tool_name: str, is_read_only: bool = False, service_type:
                                 f"The required API is not enabled for your project. "
                                 f"Please check the Google Cloud Console to enable it."
                             )
-                    else:
+                    elif error.resp.status in [401, 403]:
+                        # Authentication/authorization errors
                         message = (
                             f"API error in {tool_name}: {error}. "
                             f"You might need to re-authenticate for user '{user_google_email}'. "
                             f"LLM: Try 'start_google_auth' with the user's email and the appropriate service_name."
                         )
+                    else:
+                        # Other HTTP errors (400 Bad Request, etc.) - don't suggest re-auth
+                        message = f"API error in {tool_name}: {error}"
                     
                     logger.error(f"API error in {tool_name}: {error}", exc_info=True)
                     raise Exception(message) from error
diff --git a/gdocs/docs_helpers.py b/gdocs/docs_helpers.py
index a59d668..1394085 100644
--- a/gdocs/docs_helpers.py
+++ b/gdocs/docs_helpers.py
@@ -72,6 +72,28 @@ def create_insert_text_request(index: int, text: str) -> Dict[str, Any]:
         }
     }
 
+def create_insert_text_segment_request(index: int, text: str, segment_id: str) -> Dict[str, Any]:
+    """
+    Create an insertText request for Google Docs API with segmentId (for headers/footers).
+    
+    Args:
+        index: Position to insert text
+        text: Text to insert
+        segment_id: Segment ID (for targeting headers/footers)
+    
+    Returns:
+        Dictionary representing the insertText request with segmentId
+    """
+    return {
+        'insertText': {
+            'location': {
+                'segmentId': segment_id,
+                'index': index
+            },
+            'text': text
+        }
+    }
+
 def create_delete_range_request(start_index: int, end_index: int) -> Dict[str, Any]:
     """
     Create a deleteContentRange request for Google Docs API.
diff --git a/gdocs/docs_tools.py b/gdocs/docs_tools.py
index 4e9aef1..a0e6093 100644
--- a/gdocs/docs_tools.py
+++ b/gdocs/docs_tools.py
@@ -17,7 +17,9 @@ from core.comments import create_comment_tools
 
 # Import helper functions for document operations
 from gdocs.docs_helpers import (
+    build_text_style,
     create_insert_text_request,
+    create_insert_text_segment_request,
     create_delete_range_request,
     create_format_text_request,
     create_find_replace_request,
@@ -25,7 +27,7 @@ from gdocs.docs_helpers import (
     create_insert_page_break_request,
     create_insert_image_request,
     create_bullet_list_request,
-    validate_operation
+    validate_operation,
 )
 
 # Import document structure and table utilities
@@ -302,7 +304,7 @@ async def create_doc(
     doc = await asyncio.to_thread(service.documents().create(body={'title': title}).execute)
     doc_id = doc.get('documentId')
     if content:
-        requests = [{'insertText': {'location': {'index': 1}, 'text': content}}]
+        requests = [create_insert_text_request(1, content)]
         await asyncio.to_thread(service.documents().batchUpdate(documentId=doc_id, body={'requests': requests}).execute)
     link = f"https://docs.google.com/document/d/{doc_id}/edit"
     msg = f"Created Google Doc '{title}' (ID: {doc_id}) for {user_google_email}. Link: {link}"
@@ -628,10 +630,8 @@ async def insert_doc_image(
     else:
         image_uri = image_source
         source_description = "URL image"
-
     # Use helper to create image request
     requests = [create_insert_image_request(index, image_uri, width, height)]
-
     await asyncio.to_thread(
         docs_service.documents().batchUpdate(
             documentId=document_id,
@@ -646,6 +646,160 @@ async def insert_doc_image(
     link = f"https://docs.google.com/document/d/{document_id}/edit"
     return f"Inserted {source_description}{size_info} at index {index} in document {document_id}. Link: {link}"
 
+@server.tool()
+@handle_http_errors("insert_doc_image_from_drive", service_type="docs")
+@require_multiple_services([
+    {"service_type": "drive", "scopes": "drive_read", "param_name": "drive_service"},
+    {"service_type": "docs", "scopes": "docs_write", "param_name": "docs_service"}
+])
+async def insert_doc_image_from_drive(
+    drive_service,
+    docs_service,
+    user_google_email: str,
+    document_id: str,
+    drive_file_name: str,
+    index: int,
+    width: int = None,
+    height: int = None,
+) -> str:
+    """
+    Searches for an image in Google Drive by name and inserts it into a Google Doc.
+    Checks permissions first and provides helpful error messages if the image isn't publicly shared.
+    
+    Args:
+        user_google_email: User's Google email address
+        document_id: ID of the document to update
+        drive_file_name: Name of the image file in Google Drive (e.g., "product_roadmap_2025.png")
+        index: Position to insert image (0-based)
+        width: Image width in points (optional)
+        height: Image height in points (optional)
+    
+    Returns:
+        str: Confirmation message with insertion details or error with instructions
+    """
+    logger.info(f"[insert_doc_image_from_drive] Doc={document_id}, file={drive_file_name}, index={index}")
+    
+    # Build search query for the specific file name
+    escaped_name = drive_file_name.replace("'", "\\'")
+    search_query = f"name = '{escaped_name}'"
+    
+    # Search for the file in Drive with permission information
+    list_params = {
+        "q": search_query,
+        "pageSize": 5,
+        "fields": "files(id, name, mimeType, webViewLink, permissions, shared)",
+        "supportsAllDrives": True,
+        "includeItemsFromAllDrives": True,
+    }
+    
+    search_results = await asyncio.to_thread(
+        drive_service.files().list(**list_params).execute
+    )
+    
+    files = search_results.get('files', [])
+    if not files:
+        return f"❌ Error: File '{drive_file_name}' not found in Google Drive"
+    
+    # Use the first matching file
+    file_info = files[0]
+    file_id = file_info.get('id')
+    file_name = file_info.get('name')
+    mime_type = file_info.get('mimeType', '')
+    
+    # Check if it's an image file
+    if not mime_type.startswith('image/'):
+        logger.warning(f"File '{drive_file_name}' has MIME type '{mime_type}' which may not be an image")
+    
+    # Check permissions to see if file has "anyone with link" permission
+    from gdrive.drive_helpers import check_public_link_permission
+    permissions = file_info.get('permissions', [])
+    has_public_link = check_public_link_permission(permissions)
+    
+    if not has_public_link:
+        from gdrive.drive_helpers import format_public_sharing_error
+        return format_public_sharing_error(file_name, file_id)
+    
+    # File has public access - proceed with insertion
+    from gdrive.drive_helpers import get_drive_image_url
+    image_uri = get_drive_image_url(file_id)
+    
+    # Use helper function to create request
+    request = create_insert_image_request(index, image_uri, width, height)
+    requests = [request]
+    
+    try:
+        await asyncio.to_thread(
+            docs_service.documents().batchUpdate(
+                documentId=document_id,
+                body={'requests': requests}
+            ).execute
+        )
+        
+        size_info = ""
+        if width or height:
+            size_info = f" (size: {width or 'auto'}x{height or 'auto'} points)"
+        
+        link = f"https://docs.google.com/document/d/{document_id}/edit"
+        return f"✅ Successfully inserted Drive image '{file_name}' (ID: {file_id}){size_info} at index {index} in document {document_id}. Link: {link}"
+        
+    except Exception as e:
+        error_str = str(e)
+        if "publicly accessible" in error_str or "forbidden" in error_str.lower():
+            return f"❌ API Error: Drive image '{file_name}' access denied despite public sharing. May need propagation time or use insert_doc_image_url with: {get_drive_image_url(file_id)}"
+        else:
+            # Some other error occurred
+            return f"❌ Error inserting image '{file_name}': {e}"
+
+@server.tool()
+@handle_http_errors("insert_doc_image_url", service_type="docs")
+@require_google_service("docs", "docs_write")
+async def insert_doc_image_url(
+    service,
+    user_google_email: str,
+    document_id: str,
+    image_url: str,
+    index: int,
+    width: int = None,
+    height: int = None,
+) -> str:
+    """
+    Inserts an image from a URL into a Google Doc.
+    Simplified version that only works with URLs, not Drive files.
+    
+    Args:
+        user_google_email: User's Google email address
+        document_id: ID of the document to update
+        image_url: Public image URL (must start with http:// or https://)
+        index: Position to insert image (0-based)
+        width: Image width in points (optional)
+        height: Image height in points (optional)
+    
+    Returns:
+        str: Confirmation message with insertion details
+    """
+    logger.info(f"[insert_doc_image_url] Doc={document_id}, url={image_url}, index={index}")
+    
+    if not (image_url.startswith('http://') or image_url.startswith('https://')):
+        return "Error: image_url must be a valid HTTP/HTTPS URL"
+    
+    # Use helper function to create request
+    request = create_insert_image_request(index, image_url, width, height)
+    requests = [request]
+    
+    await asyncio.to_thread(
+        service.documents().batchUpdate(
+            documentId=document_id,
+            body={'requests': requests}
+        ).execute
+    )
+    
+    size_info = ""
+    if width or height:
+        size_info = f" (size: {width or 'auto'}x{height or 'auto'} points)"
+    
+    link = f"https://docs.google.com/document/d/{document_id}/edit"
+    return f"Inserted image from URL{size_info} at index {index} in document {document_id}. Link: {link}"
+
 @server.tool()
 @handle_http_errors("update_doc_headers_footers", service_type="docs")
 @require_google_service("docs", "docs_write")
diff --git a/gdrive/drive_file_permissions.py b/gdrive/drive_file_permissions.py
new file mode 100644
index 0000000..1a19b45
--- /dev/null
+++ b/gdrive/drive_file_permissions.py
@@ -0,0 +1,209 @@
+"""
+Google Drive File Permissions Tools
+
+This module provides tools for checking and managing file permissions in Google Drive.
+"""
+import logging
+import asyncio
+from typing import Optional, Dict, Any
+
+from auth.service_decorator import require_google_service
+from core.utils import handle_http_errors
+from core.server import server
+
+logger = logging.getLogger(__name__)
+
+@server.tool()
+@handle_http_errors("get_drive_file_permissions", is_read_only=True, service_type="drive")
+@require_google_service("drive", "drive_read")
+async def get_drive_file_permissions(
+    service,
+    user_google_email: str,
+    file_id: str,
+) -> str:
+    """
+    Gets detailed metadata about a Google Drive file including sharing permissions.
+    
+    Args:
+        user_google_email (str): The user's Google email address. Required.
+        file_id (str): The ID of the file to check permissions for.
+    
+    Returns:
+        str: Detailed file metadata including sharing status and URLs.
+    """
+    logger.info(f"[get_drive_file_permissions] Checking file {file_id} for {user_google_email}")
+    
+    try:
+        # Get comprehensive file metadata including permissions
+        file_metadata = await asyncio.to_thread(
+            service.files().get(
+                fileId=file_id,
+                fields="id, name, mimeType, size, modifiedTime, owners, permissions, "
+                       "webViewLink, webContentLink, shared, sharingUser, viewersCanCopyContent",
+                supportsAllDrives=True
+            ).execute
+        )
+        
+        # Format the response
+        output_parts = [
+            f"File: {file_metadata.get('name', 'Unknown')}",
+            f"ID: {file_id}",
+            f"Type: {file_metadata.get('mimeType', 'Unknown')}",
+            f"Size: {file_metadata.get('size', 'N/A')} bytes",
+            f"Modified: {file_metadata.get('modifiedTime', 'N/A')}",
+            f"",
+            "Sharing Status:",
+            f"  Shared: {file_metadata.get('shared', False)}",
+        ]
+        
+        # Add sharing user if available
+        sharing_user = file_metadata.get('sharingUser')
+        if sharing_user:
+            output_parts.append(f"  Shared by: {sharing_user.get('displayName', 'Unknown')} ({sharing_user.get('emailAddress', 'Unknown')})")
+        
+        # Process permissions
+        permissions = file_metadata.get('permissions', [])
+        if permissions:
+            output_parts.append(f"  Number of permissions: {len(permissions)}")
+            output_parts.append("  Permissions:")
+            for perm in permissions:
+                perm_type = perm.get('type', 'unknown')
+                role = perm.get('role', 'unknown')
+                
+                if perm_type == 'anyone':
+                    output_parts.append(f"    - Anyone with the link ({role})")
+                elif perm_type == 'user':
+                    email = perm.get('emailAddress', 'unknown')
+                    output_parts.append(f"    - User: {email} ({role})")
+                elif perm_type == 'domain':
+                    domain = perm.get('domain', 'unknown')
+                    output_parts.append(f"    - Domain: {domain} ({role})")
+                elif perm_type == 'group':
+                    email = perm.get('emailAddress', 'unknown')
+                    output_parts.append(f"    - Group: {email} ({role})")
+                else:
+                    output_parts.append(f"    - {perm_type} ({role})")
+        else:
+            output_parts.append("  No additional permissions (private file)")
+        
+        # Add URLs
+        output_parts.extend([
+            "",
+            "URLs:",
+            f"  View Link: {file_metadata.get('webViewLink', 'N/A')}",
+        ])
+        
+        # webContentLink is only available for files that can be downloaded
+        web_content_link = file_metadata.get('webContentLink')
+        if web_content_link:
+            output_parts.append(f"  Direct Download Link: {web_content_link}")
+        
+        # Check if file has "anyone with link" permission
+        from gdrive.drive_helpers import check_public_link_permission
+        has_public_link = check_public_link_permission(permissions)
+        
+        if has_public_link:
+            output_parts.extend([
+                "",
+                "✅ This file is shared with 'Anyone with the link' - it can be inserted into Google Docs"
+            ])
+        else:
+            output_parts.extend([
+                "",
+                "❌ This file is NOT shared with 'Anyone with the link' - it cannot be inserted into Google Docs",
+                "   To fix: Right-click the file in Google Drive → Share → Anyone with the link → Viewer"
+            ])
+        
+        return "\n".join(output_parts)
+        
+    except Exception as e:
+        logger.error(f"Error getting file permissions: {e}")
+        return f"Error getting file permissions: {e}"
+
+
+@server.tool()
+@handle_http_errors("check_drive_file_public_access", is_read_only=True, service_type="drive")
+@require_google_service("drive", "drive_read")
+async def check_drive_file_public_access(
+    service,
+    user_google_email: str,
+    file_name: str,
+) -> str:
+    """
+    Searches for a file by name and checks if it has public link sharing enabled.
+    
+    Args:
+        user_google_email (str): The user's Google email address. Required.
+        file_name (str): The name of the file to check.
+    
+    Returns:
+        str: Information about the file's sharing status and whether it can be used in Google Docs.
+    """
+    logger.info(f"[check_drive_file_public_access] Searching for {file_name}")
+    
+    # Search for the file
+    escaped_name = file_name.replace("'", "\\'")
+    query = f"name = '{escaped_name}'"
+    
+    list_params = {
+        "q": query,
+        "pageSize": 10,
+        "fields": "files(id, name, mimeType, webViewLink)",
+        "supportsAllDrives": True,
+        "includeItemsFromAllDrives": True,
+    }
+    
+    results = await asyncio.to_thread(
+        service.files().list(**list_params).execute
+    )
+    
+    files = results.get('files', [])
+    if not files:
+        return f"No file found with name '{file_name}'"
+    
+    if len(files) > 1:
+        output_parts = [f"Found {len(files)} files with name '{file_name}':"]
+        for f in files:
+            output_parts.append(f"  - {f['name']} (ID: {f['id']})")
+        output_parts.append("\nChecking the first file...")
+        output_parts.append("")
+    else:
+        output_parts = []
+    
+    # Check permissions for the first file
+    file_id = files[0]['id']
+    
+    # Get detailed permissions
+    file_metadata = await asyncio.to_thread(
+        service.files().get(
+            fileId=file_id,
+            fields="id, name, mimeType, permissions, webViewLink, webContentLink, shared",
+            supportsAllDrives=True
+        ).execute
+    )
+    
+    permissions = file_metadata.get('permissions', [])
+    from gdrive.drive_helpers import check_public_link_permission
+    has_public_link = check_public_link_permission(permissions)
+    
+    output_parts.extend([
+        f"File: {file_metadata['name']}",
+        f"ID: {file_id}",
+        f"Type: {file_metadata['mimeType']}",
+        f"Shared: {file_metadata.get('shared', False)}",
+        ""
+    ])
+    
+    if has_public_link:
+        from gdrive.drive_helpers import get_drive_image_url
+        output_parts.extend([
+            "✅ PUBLIC ACCESS ENABLED - This file can be inserted into Google Docs",
+            f"Use with insert_doc_image_url: {get_drive_image_url(file_id)}"
+        ])
+    else:
+        output_parts.extend([
+            "❌ NO PUBLIC ACCESS - Cannot insert into Google Docs",
+            "Fix: Drive → Share → 'Anyone with the link' → 'Viewer'"
+        ])
+    
+    return "\n".join(output_parts)
\ No newline at end of file
diff --git a/gdrive/drive_helpers.py b/gdrive/drive_helpers.py
new file mode 100644
index 0000000..45aed32
--- /dev/null
+++ b/gdrive/drive_helpers.py
@@ -0,0 +1,53 @@
+"""
+Google Drive Helper Functions
+
+Shared utilities for Google Drive operations including permission checking.
+"""
+from typing import List, Dict, Any
+
+
+def check_public_link_permission(permissions: List[Dict[str, Any]]) -> bool:
+    """
+    Check if file has 'anyone with the link' permission.
+    
+    Args:
+        permissions: List of permission objects from Google Drive API
+        
+    Returns:
+        bool: True if file has public link sharing enabled
+    """
+    return any(
+        p.get('type') == 'anyone' and p.get('role') in ['reader', 'writer', 'commenter']
+        for p in permissions
+    )
+
+
+def format_public_sharing_error(file_name: str, file_id: str) -> str:
+    """
+    Format error message for files without public sharing.
+    
+    Args:
+        file_name: Name of the file
+        file_id: Google Drive file ID
+        
+    Returns:
+        str: Formatted error message
+    """
+    return (
+        f"❌ Permission Error: '{file_name}' not shared publicly. "
+        f"Set 'Anyone with the link' → 'Viewer' in Google Drive sharing. "
+        f"File: https://drive.google.com/file/d/{file_id}/view"
+    )
+
+
+def get_drive_image_url(file_id: str) -> str:
+    """
+    Get the correct Drive URL format for publicly shared images.
+    
+    Args:
+        file_id: Google Drive file ID
+        
+    Returns:
+        str: URL for embedding Drive images
+    """
+    return f"https://drive.google.com/uc?export=view&id={file_id}"
\ No newline at end of file
diff --git a/google_workspace_mcp.dxt b/google_workspace_mcp.dxt
index 20a8199..0150ed9 100644
Binary files a/google_workspace_mcp.dxt and b/google_workspace_mcp.dxt differ
diff --git a/main.py b/main.py
index 022aed3..8e9aa8a 100644
--- a/main.py
+++ b/main.py
@@ -117,7 +117,10 @@ def main():
     # Import tool modules to register them with the MCP server via decorators
     tool_imports = {
         'gmail': lambda: __import__('gmail.gmail_tools'),
-        'drive': lambda: __import__('gdrive.drive_tools'),
+        'drive': lambda: (
+            __import__('gdrive.drive_tools'),
+            __import__('gdrive.drive_file_permissions')
+        ),
         'calendar': lambda: __import__('gcalendar.calendar_tools'),
         'docs': lambda: __import__('gdocs.docs_tools'),
         'sheets': lambda: __import__('gsheets.sheets_tools'),
Dockerfile:FROM python:3.11-slim
gdocs/docs_tools.py:                    'preview': table_data[:3] if table_data else []  # First 3 rows
gdocs/managers/batch_operation_manager.py:        summary_items = operation_descriptions[:3]  # Show first 3 operations
Binary file google_workspace_mcp.dxt matches
