Skip to main content
Glama
stevereiner
by stevereiner

advanced_search

Search Alfresco content with customizable sorting and filtering options to find specific documents efficiently.

Instructions

Advanced search with sorting and filtering capabilities.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
queryYes
sort_fieldNocm:modified
sort_ascendingNo
max_resultsNo

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

Implementation Reference

  • Core handler implementation of the advanced_search tool. Executes the search logic using Alfresco search client, handles sorting, processes results into formatted output, includes fallbacks and comprehensive error handling.
    async def advanced_search_impl(
        query: str, 
        sort_field: str = "cm:modified",
        sort_ascending: bool = False,
        max_results: int = 25,
        ctx: Optional[Context] = None
    ) -> str:
        """Advanced search with sorting and filtering capabilities.
        
        Args:
            query: Search query string (supports Alfresco Full Text Search syntax)
            sort_field: Field to sort by (default: cm:modified)
            sort_ascending: Sort order (default: False for descending)
            max_results: Maximum number of results to return (default: 25)
            ctx: MCP context for progress reporting
        
        Returns:
            Formatted search results with metadata, sorted as requested
        """
        # Parameter validation and extraction
        try:
            # Extract parameters with fallback handling
            if hasattr(query, 'value'):
                actual_query = str(query.value)
            else:
                actual_query = str(query)
                
            if hasattr(sort_field, 'value'):
                actual_sort_field = str(sort_field.value)
            else:
                actual_sort_field = str(sort_field)
                
            if hasattr(sort_ascending, 'value'):
                actual_sort_ascending = bool(sort_ascending.value)
            else:
                actual_sort_ascending = bool(sort_ascending)
                
            if hasattr(max_results, 'value'):
                actual_max_results = int(max_results.value)
            else:
                actual_max_results = int(max_results)
            
            # Clean and normalize for display (preserve Unicode characters)
            safe_query_display = str(actual_query)
            safe_sort_field_display = str(actual_sort_field)
            
        except Exception as e:
            logger.error(f"Parameter extraction error: {e}")
            return f"ERROR: Parameter error: {str(e)}"
        
        if ctx:
            await ctx.info(safe_format_output(f"Advanced search for '{safe_query_display}' with sorting..."))
            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.debug(f"Advanced search for: '{safe_query_display}', sort: {safe_sort_field_display} ({'asc' if actual_sort_ascending else 'desc'})")
            
            if ctx:
                await ctx.report_progress(0.3)
            
            # Use search_utils.advanced_search() utility with existing search_client
            if ctx:
                await ctx.report_progress(0.5)
            
            try:
                # Use search_utils.advanced_search() with existing search_client that has working authentication
                search_results = search_utils.advanced_search(
                    search_client,
                    actual_query,
                    max_items=actual_max_results,
                    sort_by=actual_sort_field,
                    sort_ascending=actual_sort_ascending
                )
                
                if not search_results:
                    logger.debug("Advanced search returned None, attempting fallback to simple search")
                    search_results = search_utils.simple_search(search_client, actual_query, max_items=actual_max_results)
                    
                # Check for different possible SearchResult structures
                if not search_results:
                    logger.error(f"No search results returned")
                    return safe_format_output(f"ERROR: Advanced search failed - no results returned")
                    
                # Try to get entries from different possible structures
                entries = []
                
                if hasattr(search_results, 'list') and search_results.list and hasattr(search_results.list, 'entries'):
                    entries = search_results.list.entries if search_results.list else []
                    logger.debug(f"Found entries using list attribute: {len(entries)}")
                elif hasattr(search_results, 'list_') and search_results.list_ and hasattr(search_results.list_, 'entries'):
                    entries = search_results.list_.entries if search_results.list_ else []
                    logger.debug(f"Found entries using list_ attribute: {len(entries)}")
                elif hasattr(search_results, 'entries'):
                    entries = search_results.entries
                    logger.debug(f"Found entries using direct entries attribute: {len(entries)}")
                elif hasattr(search_results, 'results'):
                    entries = search_results.results
                    logger.debug(f"Found entries using results attribute: {len(entries)}")
                else:
                    logger.error(f"SearchResult structure not recognized")
                    return safe_format_output(f"ERROR: Advanced search failed - unknown SearchResult structure")
                    
            except Exception as e:
                logger.error(f"Advanced search failed: {e}")
                # Try fallback to simple search
                try:
                    logger.debug("Attempting fallback to simple search after advanced search error")
                    search_results = search_utils.simple_search(search_client, actual_query, max_items=actual_max_results)
                    if not search_results:
                        return safe_format_output(f"ERROR: Both advanced and simple search failed: {str(e)}")
                    # Extract entries from simple search result
                    entries = []
                    if hasattr(search_results, 'list_') and search_results.list_ and hasattr(search_results.list_, 'entries'):
                        entries = search_results.list_.entries if search_results.list_ else []
                        logger.debug(f"Fallback simple search found {len(entries)} results")
                    else:
                        return safe_format_output(f"ERROR: Both advanced and simple search failed: {str(e)}")
                except Exception as fallback_error:
                    logger.error(f"Fallback simple search also failed: {fallback_error}")
                    return safe_format_output(f"ERROR: Advanced search failed: {str(e)}")
            
            if ctx:
                await ctx.report_progress(1.0)
            
            # Process final results
            if entries:
                logger.info(f"Found {len(entries)} search results")
                result_text = f"Found {len(entries)} item(s) matching '{safe_query_display}':\n\n"
                
                for i, entry in enumerate(entries, 1):
                    # 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.debug(f"Unknown entry structure: {entry}")
                            continue
                    elif hasattr(entry, 'entry'):  # ResultSetRowEntry object
                        node = entry.entry
                    else:
                        logger.debug(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'))
                        
                        # 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:
                # Simple "0" for zero results as requested
                return "0"
            
        except Exception as e:
            error_msg = f"ERROR: Advanced search failed: {str(e)}"
            if ctx:
                await ctx.error(safe_format_output(error_msg))
            return safe_format_output(error_msg) 
  • FastMCP tool registration for 'advanced_search' via @mcp.tool decorator. Defines the tool schema through type hints and defaults, and delegates to the core impl.
    @mcp.tool
    async def advanced_search(
        query: str, 
        sort_field: str = "cm:modified",
        sort_ascending: bool = False,
        max_results: int = 25,
        ctx: Context = None
    ) -> str:
        """Advanced search with sorting and filtering capabilities."""
        return await advanced_search_impl(query, sort_field, sort_ascending, max_results, ctx)
  • Input schema defined by the tool handler function signature: query (str), sort_field (str, default 'cm:modified'), sort_ascending (bool, default False), max_results (int, default 25), ctx (Context). Output str.
    async def advanced_search(
        query: str, 
        sort_field: str = "cm:modified",
        sort_ascending: bool = False,
        max_results: int = 25,
        ctx: Context = None
    ) -> str:
        """Advanced search with sorting and filtering capabilities."""
        return await advanced_search_impl(query, sort_field, sort_ascending, max_results, ctx)
Behavior2/5

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

With no annotations provided, the description carries the full burden of behavioral disclosure. It mentions 'sorting and filtering capabilities' but doesn't explain what the tool returns (though an output schema exists), how results are limited, or any constraints like rate limits or permissions. It's minimal and lacks crucial operational details.

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

Conciseness4/5

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

The description is a single, efficient sentence with no wasted words, making it appropriately concise. However, it's front-loaded with basic information but lacks depth, which slightly limits its effectiveness despite the brevity.

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 the tool's moderate complexity (4 parameters, 1 required) and the presence of an output schema, the description is incomplete. It doesn't clarify what's being searched (e.g., documents, nodes) or how it differs from siblings, leaving gaps in context. The output schema mitigates some issues, but the description should do more to guide usage.

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 for undocumented parameters. It only vaguely references 'sorting and filtering' without explaining the four parameters (query, sort_field, sort_ascending, max_results) or their semantics. This adds little value beyond the schema's titles and defaults.

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

Purpose3/5

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

The description 'Advanced search with sorting and filtering capabilities' states the tool's purpose (searching) and mentions additional features (sorting/filtering), but it's vague about what resources are being searched and doesn't distinguish it from sibling tools like 'search_by_metadata' or 'search_content'. It provides a basic verb+scope but lacks specificity.

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 offers no guidance on when to use this tool versus alternatives like 'search_by_metadata' or 'search_content', nor does it mention prerequisites or exclusions. It implies usage for advanced search needs but lacks explicit context or comparisons with siblings.

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