Skip to main content
Glama
stevereiner
by stevereiner

search_content

Search for content in Alfresco using AFTS query language to find documents and files based on specific criteria.

Instructions

Search for content in Alfresco using AFTS query language.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
queryYes
max_resultsNo
node_typeNo

Implementation Reference

  • Core handler implementation: Performs Alfresco content search using AFTS query syntax. Validates inputs, ensures connection, executes search via search_client, formats results with node details (name, ID, type, created date). Handles progress reporting and errors.
    async def search_content_impl(
        search_query: str,
        max_results: int = 25,
        node_type: str = "cm:content",
        ctx: Optional[Context] = None
    ) -> str:
        """Search for content in Alfresco repository.
        
        Args:
            search_query: Search query string
            max_results: Maximum number of results to return (default: 25)
            node_type: Type of nodes to search for (default: "cm:content" - searches documents)
            ctx: MCP context for progress reporting
        
        Returns:
            Formatted search results
        """
        # Parameter validation and extraction
        try:
            # Extract parameters with fallback handling
            if hasattr(search_query, 'value'):
                actual_query = str(search_query.value)
            else:
                actual_query = str(search_query)
                
            if hasattr(max_results, 'value'):
                actual_max_results = int(max_results.value)
            else:
                actual_max_results = int(max_results)
                
            if hasattr(node_type, 'value'):
                actual_node_type = str(node_type.value)
            else:
                actual_node_type = str(node_type)
            
            # Default to cm:content if empty
            if not actual_node_type.strip():
                actual_node_type = "cm:content"
            
            # Clean and normalize for display (prevent Unicode encoding issues)
            safe_query_display = safe_format_output(str(actual_query))
            safe_node_type_display = safe_format_output(str(actual_node_type))
            
        except Exception as e:
            logger.error(f"Parameter extraction error: {e}")
            return safe_format_output(f"ERROR: Parameter error: {str(e)}")
        
        if not actual_query.strip():
            return """Content Search Tool
    
    Usage: Provide a search query to search Alfresco repository content.
    
    Example searches:
    - admin (finds items with 'admin' in name or content)
    - name:test* (finds items with names starting with 'test')
    - modified:[2024-01-01 TO 2024-12-31] (finds items modified in 2024)
    - TYPE:"cm:content" (finds all documents)
    - TYPE:"cm:folder" (finds all folders)
    
    Search uses AFTS (Alfresco Full Text Search) syntax for flexible content discovery.
    By default, searches for documents (cm:content) unless a different type is specified.
    """
        
        if ctx:
            await ctx.info(safe_format_output(f"Content search for: '{safe_query_display}'"))
            await ctx.report_progress(0.0)
        
        try:
            # Get all clients that ensure_connection() already created
            master_client = await ensure_connection()
            
            # Import search_utils
            from python_alfresco_api.utils import search_utils
            
            # Access the search client that was already created
            search_client = master_client.search
            
            logger.info(f"Content search for: '{safe_query_display}', type: '{safe_node_type_display}'")
            
            if ctx:
                await ctx.report_progress(0.3)
            
            # Build search query to include node_type filter
            final_query = actual_query
            
            # Add node_type filter if not already in query
            has_type_in_query = "TYPE:" in final_query.upper()
            if not has_type_in_query:
                if final_query == "*":
                    final_query = f'TYPE:"{actual_node_type}"'
                else:
                    final_query = f'({final_query}) AND TYPE:"{actual_node_type}"'
            
            # Use the correct working pattern: search_utils.simple_search with existing search_client
            try:
                search_results = search_utils.simple_search(search_client, final_query, max_items=actual_max_results)
                
                if search_results and hasattr(search_results, 'list_'):
                    entries_list = search_results.list_.entries if search_results.list_  else []
                    logger.info(f"Found {len(entries_list)} content search results")
                    
                    if ctx:
                        await ctx.report_progress(1.0)
                    
                    if not entries_list:
                        return "0"
                    
                    result_text = f"Found {len(entries_list)} item(s) matching the search query:\n\n"
                    
                    for i, entry in enumerate(entries_list, 1):
                        # Debug: Log the entry structure
                        logger.debug(f"Entry {i} type: {type(entry)}, content: {entry}")
                        
                        # Handle different possible entry structures
                        node = None
                        if isinstance(entry, dict):
                            if 'entry' in entry:
                                node = entry['entry']
                            elif 'name' in entry:  # Direct node structure
                                node = entry
                            else:
                                logger.warning(f"Unknown entry structure: {entry}")
                                continue
                        elif hasattr(entry, 'entry'):  # ResultSetRowEntry object
                            node = entry.entry
                        else:
                            logger.warning(f"Entry is not a dict or ResultSetRowEntry: {type(entry)}")
                            continue
                        
                        if node:
                            # Handle both dict and ResultNode objects
                            if isinstance(node, dict):
                                name = str(node.get('name', 'Unknown'))
                                node_id = str(node.get('id', 'Unknown'))
                                node_type_actual = str(node.get('nodeType', 'Unknown'))
                                created_at = str(node.get('createdAt', 'Unknown'))
                            else:
                                # ResultNode object - access attributes directly
                                name = str(getattr(node, 'name', 'Unknown'))
                                node_id = str(getattr(node, 'id', 'Unknown'))
                                node_type_actual = str(getattr(node, 'node_type', 'Unknown'))
                                created_at = str(getattr(node, 'created_at', 'Unknown'))
                            
                            # Clean JSON-friendly formatting (no markdown syntax)
                            # Apply safe formatting to individual fields to prevent emoji encoding issues
                            safe_name = safe_format_output(name)
                            safe_node_id = safe_format_output(node_id)
                            safe_node_type = safe_format_output(node_type_actual)
                            safe_created_at = safe_format_output(created_at)
                            
                            result_text += f"{i}. {safe_name}\n"
                            result_text += f"   - ID: {safe_node_id}\n"
                            result_text += f"   - Type: {safe_node_type}\n"
                            result_text += f"   - Created: {safe_created_at}\n\n"
                    
                    return safe_format_output(result_text)
                else:
                    return safe_format_output(f"ERROR: Content search failed - invalid response from Alfresco")
                    
            except Exception as e:
                logger.error(f"Content search failed: {e}")
                return safe_format_output(f"ERROR: Content search failed: {str(e)}")
            
        except Exception as e:
            # Preserve Unicode characters in error messages
            error_msg = f"ERROR: Content search failed: {str(e)}"
            if ctx:
                await ctx.error(safe_format_output(error_msg))
            return safe_format_output(error_msg)
    
        if ctx:
            await ctx.info(safe_format_output("Content search completed!")) 
  • Tool registration using @mcp.tool decorator in FastMCP server. Defines input schema via type hints and docstring, delegates execution to search_content_impl.
    @mcp.tool
    async def search_content(
        query: str, 
        max_results: int = 25,
        node_type: str = "",
        ctx: Context = None
    ) -> str:
        """Search for content in Alfresco using AFTS query language."""
        return await search_content_impl(query, max_results, node_type, ctx)
  • Input schema defined by function parameters and type annotations for the search_content tool.
    async def search_content(
        query: str, 
        max_results: int = 25,
        node_type: str = "",
        ctx: Context = None
    ) -> str:
        """Search for content in Alfresco using AFTS query language."""
        return await search_content_impl(query, max_results, node_type, ctx)

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