Skip to main content
Glama
stevereiner
by stevereiner

upload_document

Upload documents to Alfresco content management system using file paths or base64 content, organizing them with parent IDs and descriptions for structured storage.

Instructions

Upload a document to Alfresco.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
file_pathNo
base64_contentNo
parent_idNo-shared-
descriptionNo

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

Implementation Reference

  • Registers the 'upload_document' tool with FastMCP using @mcp.tool decorator. Defines input schema via type hints and delegates to implementation.
    @mcp.tool
    async def upload_document(
        file_path: str = "",
        base64_content: str = "",
        parent_id: str = "-shared-",
        description: str = "",
        ctx: Context = None
    ) -> str:
        """Upload a document to Alfresco."""
        return await upload_document_impl(file_path, base64_content, parent_id, description, ctx)
  • Core handler implementation for the upload_document tool. Handles file path or base64 content upload to Alfresco, including cross-platform path handling, temp file creation, connection management, and detailed progress reporting.
    async def upload_document_impl(
        file_path: str = "",
        base64_content: str = "",
        parent_id: str = "-shared-",
        description: str = "",
        ctx: Optional[Context] = None
    ) -> str:
        """Upload a document to Alfresco using Share-style behavior.
        
        Args:
            file_path: Path to the file to upload (alternative to base64_content)
            base64_content: Base64 encoded file content (alternative to file_path)
            parent_id: Parent folder ID (default: shared folder)
            description: Document description (optional)
            ctx: MCP context for progress reporting
        
        Note:
            - Uploads as version 1.0 (matching Alfresco Share behavior)
            - Uses full file path as title (matching Alfresco Share behavior)
            - Original filename is preserved automatically
            - For base64 uploads: content type detection and auto-naming
            - Cross-platform support: Windows paths with quotes, macOS ~/path expansion, Linux XDG directories
            - File extension detection works on all platforms (including macOS hidden extensions)
            - Linux filesystem case-sensitivity and permission handling
        
        Returns:
            Upload confirmation with document details
        """
        if ctx:
            if file_path:
                await ctx.info(f">> Uploading document from '{file_path}' to {parent_id}")
            else:
                await ctx.info(f">> Uploading base64 content to {parent_id}")
            await ctx.info("Validating file and parameters...")
            await ctx.report_progress(0.1)
        
        # Determine upload mode and validate
        use_base64 = bool(base64_content.strip())
        use_file_path = bool(file_path.strip())
        
        if not use_base64 and not use_file_path:
            return "ERROR: Must provide either file_path or base64_content"
        
        if use_base64 and use_file_path:
            return "ERROR: Cannot use both file_path and base64_content - choose one"
        
        # Variables for upload
        actual_file_path = None
        temp_file_path = None
        final_filename = None
        
        try:
            if use_file_path:
                # Handle file path upload - cross-platform path handling
                cleaned_file_path = file_path.strip().strip('"').strip("'")
                
                # Handle macOS/Unix path expansion (~/Documents, etc.)
                if cleaned_file_path.startswith('~'):
                    cleaned_file_path = os.path.expanduser(cleaned_file_path)
                
                abs_file_path = os.path.abspath(cleaned_file_path)
                
                if not os.path.exists(abs_file_path):
                    if os.path.exists(cleaned_file_path):
                        abs_file_path = cleaned_file_path
                    else:
                        return f"ERROR: File not found: {cleaned_file_path} (cleaned from: {file_path})"
                
                if not os.path.isfile(abs_file_path):
                    return f"ERROR: Path is not a file: {abs_file_path}"
                
                # Linux-specific: Check file permissions
                if not os.access(abs_file_path, os.R_OK):
                    return f"ERROR: File not readable (permission denied): {abs_file_path}"
                    
                actual_file_path = abs_file_path
                final_filename = os.path.basename(abs_file_path)
                
            else:
                # Handle base64 content upload - create temporary file
                try:
                    file_content = base64.b64decode(base64_content)
                    
                    # Detect content type and create appropriate filename
                    detected_extension = detect_file_extension_from_content(file_content)
                    final_filename = f"uploaded_document{detected_extension or ''}"
                    
                    # Create temporary file with decoded content
                    temp_fd, temp_file_path = tempfile.mkstemp(suffix=f"_{final_filename}")
                    try:
                        with os.fdopen(temp_fd, 'wb') as temp_file:
                            temp_file.write(file_content)
                        actual_file_path = temp_file_path
                    except Exception:
                        os.close(temp_fd)  # Close if writing failed
                        raise
                    
                except Exception as decode_error:
                    return f"ERROR: Invalid base64 content or file creation failed: {str(decode_error)}"
            
            if not actual_file_path:
                return "ERROR: No valid file path available for upload"
                
        except Exception as validation_error:
            return f"ERROR: Validation failed: {str(validation_error)}"
        
        try:
            # Ensure connection and get core client
            await ensure_connection()
            core_client = await get_core_client()
            
            if not core_client.is_initialized:
                return safe_format_output("❌ Error: Alfresco server unavailable")
            
            if ctx:
                await ctx.info("Creating and uploading document using Share-style approach...")
                await ctx.report_progress(0.5)
            
            logger.debug(f"Uploading '{final_filename}' to parent {parent_id} using Share-style function")
            
            # Determine title based on upload type
            custom_title = None
            if use_base64:
                # For base64 uploads, use just the filename as title (not temp file path)
                custom_title = final_filename
            # For file path uploads, let Share-style function use full path as title
            
            # Use Share-style upload function
            result = create_and_upload_file_share_style_temp(
                core_client=core_client,
                file_path=actual_file_path,
                parent_id=parent_id,
                filename=final_filename,
                description=description or None,
                custom_title=custom_title
            )
            
            # Extract essential info
            if hasattr(result, 'entry') and result.entry:
                node_id = getattr(result.entry, 'id', 'Unknown')
                node_name = getattr(result.entry, 'name', final_filename)
                logger.info(f"Upload completed: {node_name} -> {node_id}")
            else:
                logger.info(f"Upload completed successfully")
            
            if ctx:
                await ctx.info("Upload completed successfully!")
                await ctx.report_progress(1.0)
    
            # Format result for MCP with clean JSON-friendly output
            title_info = custom_title if custom_title else "Full file path"
            upload_type = "Base64 content" if use_base64 else "File path"
            
            success_result = f"""SUCCESS: Document Uploaded Successfully!
    
    Name: {final_filename}
    Parent: {parent_id}
    Title: {title_info}
    Description: {description or 'N/A'}
    Upload Type: {upload_type}
    
    Details: {result}
    
    ✨ Share-Style Upload: Version 1.0, proper title handling for upload type
    ✨ File path uploads: Full path as title (matching Alfresco Share)
    ✨ Base64 uploads: Clean filename as title (no temp file paths)"""
            
            return success_result
            
        except Exception as e:
            error_msg = f"ERROR: Document upload failed: {str(e)}"
            if ctx:
                await ctx.error(error_msg)
            logger.error(f"Document upload failed: {e}")
            return error_msg
  • Helper function for Share-style document upload to Alfresco, handling properties like title based on upload type.
    def create_and_upload_file_share_style_temp(
        core_client,
        file_path,
        parent_id="-my-",
        filename=None,
        description=None,
        custom_title=None
    ):
        """
        TEMPORARY: Share-style upload until next python-alfresco-api release.
        Creates version 1.0 with full path as title (for real files) or custom title (for base64).
        """
        from pathlib import Path
        from python_alfresco_api.utils import content_utils
        
        file_path_obj = Path(file_path)
        upload_filename = filename or file_path_obj.name
        
        # Build properties with appropriate title
        if custom_title:
            # Use custom title for base64 uploads (just filename, not temp path)
            properties = {"cm:title": custom_title}
        else:
            # Use full path for real file uploads (Share-style behavior)
            properties = {"cm:title": str(file_path_obj)}
        
        if description:
            properties["cm:description"] = description
        
        # Use existing content_utils.upload_file (already in current package)
        return content_utils.upload_file(
            core_client=core_client,
            file_path=file_path_obj,
            parent_id=parent_id,
            filename=upload_filename,
            description=description,
            properties=properties,
            auto_rename=True
        )
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden for behavioral disclosure. It states this is an upload operation (implying a write/mutation) but doesn't mention authentication requirements, file size limits, supported formats, error conditions, or what happens on success. This leaves significant gaps for a mutation tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence with no wasted words. It's appropriately sized for a basic tool description and gets straight to the point without unnecessary elaboration.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given this is a mutation tool with 4 parameters (0% schema coverage) and no annotations, but with an output schema present, the description is minimally adequate. The output schema will handle return values, but the description should do more to explain parameters and behavioral context for a write operation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate but provides zero parameter information. It doesn't explain what 'parent_id' means, the relationship between 'file_path' and 'base64_content', or what 'description' applies to. With 4 undocumented parameters, this is inadequate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('Upload') and target ('a document to Alfresco'), providing a specific verb+resource combination. However, it doesn't differentiate from sibling tools like 'checkin_document' or 'download_document', which prevents a perfect score.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives like 'create_folder' for folders or 'checkin_document' for version control scenarios. There's no mention of prerequisites, constraints, or typical use cases.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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/stevereiner/python-alfresco-mcp-server'

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