> ## Documentation Index
> Fetch the complete documentation index at: https://praison.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# MCP Tool Search Module

> Search and filter MCP tools by query, category, tags, and annotations

# 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

### Basic Search

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
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

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# 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

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# 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

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# 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

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# 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",
)
```

### Paginated Search

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# 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:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
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

| Parameter  | Type    | Description                               |
| ---------- | ------- | ----------------------------------------- |
| `query`    | string  | Text to search in name, description, tags |
| `category` | string  | Filter by category                        |
| `tags`     | array   | Filter by tags (any match)                |
| `readOnly` | boolean | Filter by readOnlyHint                    |
| `cursor`   | string  | Pagination cursor                         |

### Response Format

```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
{
  "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

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
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

* [MCP Tool Search CLI](/docs/cli/mcp-tool-search) - CLI commands for tool search
* [MCP Pagination](/docs/mcp/mcp-pagination) - Pagination details
* [MCP Tool Annotations](/docs/mcp/mcp-tool-annotations) - Annotation hints
