Skip to main content

MCP Tool Search Module

The MCP Server V2 provides a powerful tool search capability as an extension method (tools/search). This is not part of the core MCP spec but provides valuable server-side filtering.

Overview

Tool search supports:
  • Text query: Search in name, description, and tags
  • Category filter: Filter by tool category
  • Tags filter: Filter by one or more tags
  • Annotation filter: Filter by readOnlyHint
  • Pagination: Results are paginated like tools/list

Code Usage

from praisonai.mcp_server.registry import MCPToolRegistry, MCPToolDefinition

# Create registry with tools
registry = MCPToolRegistry()

registry._tools["memory.show"] = MCPToolDefinition(
    name="memory.show",
    description="Show memory contents",
    handler=lambda: None,
    input_schema={"type": "object"},
    category="memory",
    read_only_hint=True,
)

registry._tools["file.delete"] = MCPToolDefinition(
    name="file.delete",
    description="Delete a file",
    handler=lambda: None,
    input_schema={"type": "object"},
    category="file",
    destructive_hint=True,
)

# Search by query
results, next_cursor, total = registry.search(query="memory")
print(f"Found {total} tools matching 'memory'")

Search by Category

# Find all file-related tools
results, _, total = registry.search(category="file")
print(f"Found {total} file tools")

for tool in results:
    print(f"  - {tool['name']}")

Search by Read-Only Hint

# Find all read-only tools (safe to call without side effects)
results, _, total = registry.search(read_only=True)
print(f"Found {total} read-only tools")

# Find all non-read-only tools (may modify state)
results, _, total = registry.search(read_only=False)
print(f"Found {total} tools that may modify state")

Search by Tags

# Register tool with tags
registry._tools["web.search"] = MCPToolDefinition(
    name="web.search",
    description="Search the web",
    handler=lambda: None,
    input_schema={"type": "object"},
    tags=["search", "internet", "query"],
)

# Search by tags (any match)
results, _, total = registry.search(tags=["search"])
print(f"Found {total} tools with 'search' tag")

Combined Filters

# Find read-only tools in memory category
results, _, total = registry.search(
    category="memory",
    read_only=True,
)

# Find tools matching query with specific category
results, _, total = registry.search(
    query="show",
    category="memory",
)
# Search with pagination
results, next_cursor, total = registry.search(
    query="tool",
    page_size=10,
)
print(f"Page 1: {len(results)} of {total} total")

# Get next page
if next_cursor:
    results2, next_cursor2, _ = registry.search(
        query="tool",
        cursor=next_cursor,
        page_size=10,
    )
    print(f"Page 2: {len(results2)} results")

Server Handler

The MCP server exposes search via the tools/search method:
from praisonai.mcp_server.server import MCPServer
import asyncio

server = MCPServer(name="my-server")

async def demo():
    # Simulate client request
    result = await server._handle_tools_search({
        "query": "memory",
        "category": "memory",
        "readOnly": True,
    })
    
    print(f"Found {result['total']} tools")
    for tool in result['tools']:
        print(f"  - {tool['name']}")

asyncio.run(demo())

Request Parameters

ParameterTypeDescription
querystringText to search in name, description, tags
categorystringFilter by category
tagsarrayFilter by tags (any match)
readOnlybooleanFilter by readOnlyHint
cursorstringPagination cursor

Response Format

{
  "tools": [
    {
      "name": "memory.show",
      "description": "Show memory contents",
      "inputSchema": {"type": "object"},
      "annotations": {"readOnlyHint": true}
    }
  ],
  "total": 1,
  "nextCursor": null
}

Search Algorithm

The search implementation:
  1. Query matching: Case-insensitive search in:
    • Tool name
    • Tool description
    • Tool tags (if any)
  2. Filter application: All filters are AND-ed together
  3. Sorting: Results are sorted by name for deterministic ordering
  4. Pagination: Applied after filtering and sorting

Example: Tool Discovery

from praisonai.mcp_server.registry import MCPToolRegistry

def discover_tools(registry: MCPToolRegistry):
    """Discover and categorize available tools."""
    
    # Find safe tools (read-only)
    safe_tools, _, _ = registry.search(read_only=True)
    print(f"Safe tools ({len(safe_tools)}):")
    for t in safe_tools:
        print(f"  ✓ {t['name']}")
    
    # Find potentially dangerous tools
    dangerous, _, _ = registry.search(read_only=False)
    print(f"\nTools that may modify state ({len(dangerous)}):")
    for t in dangerous:
        print(f"  ⚠ {t['name']}")
    
    # Group by category
    categories = set()
    all_tools, _, _ = registry.search()
    for t in all_tools:
        # Extract category from annotations or name
        name_parts = t['name'].split('.')
        if len(name_parts) > 1:
            categories.add(name_parts[-2])
    
    print(f"\nCategories: {', '.join(sorted(categories))}")

See Also