Skip to main content
Glama

checkin_document

Check in edited documents to Alfresco with version control, comments, and optional renaming using the Alfresco REST API.

Instructions

Check in a document after editing using Alfresco REST API.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
node_idYes
commentNo
major_versionNo
file_pathNo
new_nameNo

Implementation Reference

  • Core handler function executing the checkin_document tool: uploads edited file content, creates new version (major/minor), unlocks the document, cleans up local checkout tracking and files, with progress reporting.
    async def checkin_document_impl( node_id: str, comment: str = "", major_version: bool = False, file_path: str = "", new_name: str = "", ctx: Context = None ) -> str: """Check in a document after editing using Alfresco REST API. Args: node_id: Original node ID to check in (not working copy) comment: Check-in comment (default: empty) major_version: Whether to create a major version (default: False = minor version) file_path: Specific file path to upload (if empty, auto-detects from checkout folder) new_name: Optional new name for the file during checkin (default: keep original name) ctx: MCP context for progress reporting Returns: Check-in confirmation with version details and cleanup status """ if ctx: await ctx.info(f"Checking in document: {node_id}") await ctx.info("Validating parameters...") await ctx.report_progress(0.1) if not node_id.strip(): return safe_format_output("❌ Error: node_id is required") try: logger.info(f"Starting checkin: node {node_id}") core_client = await get_core_client() # Clean the node ID clean_node_id = node_id.strip() if clean_node_id.startswith('alfresco://'): clean_node_id = clean_node_id.split('/')[-1] if ctx: await ctx.info("Finding checkout file...") await ctx.report_progress(0.2) # Find the file to upload checkout_file_path = None checkout_data = {} working_copy_id = None if file_path: # Use specific file path provided - handle quotes and path expansion 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) checkout_file_path = pathlib.Path(cleaned_file_path) if not checkout_file_path.exists(): return safe_format_output(f"❌ Specified file not found: {cleaned_file_path} (cleaned from: {file_path})") # Linux-specific: Check file permissions if not os.access(checkout_file_path, os.R_OK): return safe_format_output(f"❌ File not readable (permission denied): {cleaned_file_path}") else: # Auto-detect from checkout folder downloads_dir = pathlib.Path.home() / "Downloads" checkout_dir = downloads_dir / "checkout" checkout_manifest_path = checkout_dir / ".checkout_manifest.json" if checkout_manifest_path.exists(): try: with open(checkout_manifest_path, 'r') as f: checkout_data = json.load(f) except: checkout_data = {} if 'checkouts' in checkout_data and clean_node_id in checkout_data['checkouts']: checkout_info = checkout_data['checkouts'][clean_node_id] checkout_filename = checkout_info['local_file'] locked_node_id = checkout_info.get('locked_node_id', clean_node_id) # Updated for lock API checkout_file_path = checkout_dir / checkout_filename if not checkout_file_path.exists(): return safe_format_output(f"❌ Checkout file not found: {checkout_file_path}. File may have been moved or deleted.") else: return safe_format_output(f"❌ No locked document found for node {clean_node_id}. Use checkout_document first, or specify file_path manually.") if ctx: await ctx.info(f"Uploading file: {checkout_file_path.name}") await ctx.report_progress(0.4) # Read the file content with open(checkout_file_path, 'rb') as f: file_content = f.read() logger.info(f"Checkin file: {checkout_file_path.name} ({len(file_content)} bytes)") # Get original node info using high-level core client node_response = core_client.nodes.get(node_id=clean_node_id) if not hasattr(node_response, 'entry'): return safe_format_output(f"❌ Failed to get node information for: {clean_node_id}") node_info = node_response.entry original_filename = getattr(node_info, 'name', f"document_{clean_node_id}") if ctx: await ctx.info("Uploading new content with versioning using high-level API...") await ctx.report_progress(0.7) # **USE HIGH-LEVEL API: update_node_content.sync()** # Use new name if provided, otherwise keep original filename final_filename = new_name.strip() if new_name.strip() else original_filename # Create File object with content file_obj = File( payload=BytesIO(file_content), file_name=final_filename, mime_type="application/octet-stream" ) # Use high-level update_node_content API instead of manual httpx try: version_type = "major" if major_version else "minor" logger.info(f"Updating content for {clean_node_id} ({version_type} version)") # Ensure raw client is initialized before using it if not core_client.is_initialized: return safe_format_output("❌ Error: Alfresco server unavailable") # Use high-level update_node_content API content_response = update_node_content_sync( node_id=clean_node_id, client=core_client.raw_client, body=file_obj, major_version=major_version, comment=comment if comment else None, name=new_name.strip() if new_name.strip() else None ) if not content_response: return safe_format_output(f"❌ Failed to update document content using high-level API") logger.info(f"Content updated successfully for {clean_node_id}") # CRITICAL: Unlock the document after successful content update to complete checkin try: logger.info(f"Unlocking document after successful checkin: {clean_node_id}") unlock_response = core_client.versions.cancel_checkout(node_id=clean_node_id) logger.info(f"Document unlocked successfully after checkin: {clean_node_id}") except Exception as unlock_error: error_str = str(unlock_error) if "404" in error_str: logger.info(f"Document was not locked (already unlocked): {clean_node_id}") elif "405" in error_str: logger.warning(f"Server doesn't support unlock APIs: {clean_node_id}") else: logger.error(f"Failed to unlock document after checkin: {clean_node_id} - {error_str}") # Don't fail the entire checkin if unlock fails - content was updated successfully except Exception as api_error: return safe_format_output(f"❌ Failed to update document content: {str(api_error)}") # Get updated node info to show version details using high-level core client updated_node_response = core_client.nodes.get(node_id=clean_node_id) updated_node = updated_node_response.entry if hasattr(updated_node_response, 'entry') else {} # Extract version using multiple access methods (same as get_node_properties) new_version = 'Unknown' if hasattr(updated_node, 'properties') and updated_node.properties: try: # Try to_dict() method first if hasattr(updated_node.properties, 'to_dict'): props_dict = updated_node.properties.to_dict() new_version = props_dict.get('cm:versionLabel', 'Unknown') logger.info(f"Version found via to_dict(): {new_version}") # Try direct attribute access elif hasattr(updated_node.properties, 'cm_version_label') or hasattr(updated_node.properties, 'cm:versionLabel'): new_version = getattr(updated_node.properties, 'cm_version_label', getattr(updated_node.properties, 'cm:versionLabel', 'Unknown')) logger.info(f"Version found via attributes: {new_version}") # Try dict-like access elif hasattr(updated_node.properties, '__getitem__'): new_version = updated_node.properties.get('cm:versionLabel', 'Unknown') if hasattr(updated_node.properties, 'get') else updated_node.properties['cm:versionLabel'] if 'cm:versionLabel' in updated_node.properties else 'Unknown' logger.info(f"Version found via dict access: {new_version}") else: logger.warning(f"Version properties - type: {type(updated_node.properties)}, methods: {dir(updated_node.properties)}") except Exception as version_error: logger.error(f"Error extracting version: {version_error}") new_version = 'Unknown' else: logger.warning("No properties found for version extraction") if ctx: await ctx.info("Cleaning up checkout tracking...") await ctx.report_progress(0.9) # Clean up checkout tracking cleanup_status = "ℹ️ No checkout tracking to clean up" if checkout_data and 'checkouts' in checkout_data and clean_node_id in checkout_data['checkouts']: del checkout_data['checkouts'][clean_node_id] checkout_manifest_path = pathlib.Path.home() / "Downloads" / "checkout" / ".checkout_manifest.json" with open(checkout_manifest_path, 'w') as f: json.dump(checkout_data, f, indent=2) # Optionally remove the checkout file try: checkout_file_path.unlink() cleanup_status = "🗑️ Local checkout file cleaned up" except: cleanup_status = "WARNING: Local checkout file cleanup failed" if ctx: await ctx.info("Checkin completed: Content updated + Document unlocked + Version created!") await ctx.report_progress(1.0) # Format file size file_size = len(file_content) if file_size < 1024: size_str = f"{file_size} bytes" elif file_size < 1024 * 1024: size_str = f"{file_size / 1024:.1f} KB" else: size_str = f"{file_size / (1024 * 1024):.1f} MB" # Clean JSON-friendly formatting (no markdown syntax) return safe_format_output(f"""✅ Document checked in successfully! 📄 Document: {final_filename} 🔢 New Version: {new_version} ({version_type}) 📝 Comment: {comment if comment else '(no comment)'} 📊 File Size: {size_str} 🔗 Node ID: {clean_node_id} {f"📝 Renamed: {original_filename} → {final_filename}" if new_name.strip() else ""} {cleanup_status} Next Steps: 🔓 Document is now UNLOCKED and available for others to edit ✅ New version has been created with your changes ✅ You can continue editing by using checkout_document again Status: Content updated → Document unlocked → Checkin complete!""") except Exception as e: logger.error(f"Checkin failed: {str(e)}") return safe_format_output(f"❌ Checkin failed: {str(e)}")
  • Registers the checkin_document tool with FastMCP (@mcp.tool), defines input schema via parameters and docstring, acts as thin wrapper calling the core impl.
    @mcp.tool async def checkin_document( node_id: str, comment: str = "", major_version: bool = False, file_path: str = "", new_name: str = "", ctx: Context = None ) -> str: """Check in a document after editing using Alfresco REST API.""" return await checkin_document_impl(node_id, comment, major_version, file_path, new_name, ctx)
  • Detailed schema documentation in the impl function docstring, describing parameters, usage, and return value for input/output validation.
    """Check in a document after editing using Alfresco REST API. Args: node_id: Original node ID to check in (not working copy) comment: Check-in comment (default: empty) major_version: Whether to create a major version (default: False = minor version) file_path: Specific file path to upload (if empty, auto-detects from checkout folder) new_name: Optional new name for the file during checkin (default: keep original name) ctx: MCP context for progress reporting Returns: Check-in confirmation with version details and cleanup status """

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