search_models
Search and filter AI models by provider, input price, context window, and required features like tool calling or vision.
Instructions
Search and filter OpenRouter models.
Args: query: Free-text search in model name/id/description provider: Filter by provider (anthropic, google, openai, etc.) max_input_price: Max input price per 1M tokens, 0 = no limit min_context: Minimum context window size requires_tools: Only models supporting tool calling requires_vision: Only models with vision/image input free_only: Only free models
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| query | No | ||
| provider | No | ||
| max_input_price | No | ||
| min_context | No | ||
| requires_tools | No | ||
| requires_vision | No | ||
| free_only | No |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| result | Yes |
Implementation Reference
- src/openrouter_mcp/server.py:139-195 (handler)The `search_models` function (lines 139-195) is the core handler for the 'search_models' tool. It is decorated with @mcp.tool() on line 139, registering it as an MCP tool. It accepts parameters: query, provider, max_input_price, min_context, requires_tools, requires_vision, free_only. It fetches models via fetch_models(), applies all filters in a loop, and returns formatted results (up to 50 models).
@mcp.tool() def search_models( query: str = "", provider: str = "", max_input_price: float = 0, min_context: int = 0, requires_tools: bool = False, requires_vision: bool = False, free_only: bool = False, ) -> str: """Search and filter OpenRouter models. Args: query: Free-text search in model name/id/description provider: Filter by provider (anthropic, google, openai, etc.) max_input_price: Max input price per 1M tokens, 0 = no limit min_context: Minimum context window size requires_tools: Only models supporting tool calling requires_vision: Only models with vision/image input free_only: Only free models """ models = fetch_models() results = [] for m in models: if query: searchable = f"{m.get('id','')} {m.get('name','')} {m.get('description','')}".lower() if not all(w in searchable for w in query.lower().split()): continue if provider and provider.lower() not in m.get("id", "").lower(): continue if max_input_price > 0: if float(m.get("pricing", {}).get("prompt", 0)) * 1_000_000 > max_input_price: continue if min_context > 0 and m.get("context_length", 0) < min_context: continue if requires_tools and "tools" not in m.get("supported_parameters", []): continue if requires_vision: mods = m.get("arch_modality", []) if "image" not in mods and "image-to-text" not in mods: continue if free_only: p = m.get("pricing", {}) if float(p.get("prompt", 0)) > 0 or float(p.get("completion", 0)) > 0: continue results.append(m) if not results: return "No models match your criteria." lines = [f"# Search Results — {len(results)} models\n"] for m in results[:50]: lines.append(_format_model(m)) if len(results) > 50: lines.append(f"\n... and {len(results) - 50} more") return "\n\n".join(lines) - src/openrouter_mcp/server.py:139-148 (registration)The @mcp.tool() decorator on line 139 registers `search_models` as an MCP tool with the FastMCP server instance. The registration occurs at import time when the decorator is applied.
@mcp.tool() def search_models( query: str = "", provider: str = "", max_input_price: float = 0, min_context: int = 0, requires_tools: bool = False, requires_vision: bool = False, free_only: bool = False, ) -> str: - src/openrouter_mcp/server.py:34-50 (helper)The `fetch_models` helper function fetches the model list from the OpenRouter API (https://openrouter.ai/api/v1/models) with in-memory caching (TTL: 300s). It is called by `search_models` to retrieve the full model catalog.
def fetch_models(force=False) -> list[dict]: """Fetch model list from OpenRouter with caching.""" now = time.time() if _cache["data"] is not None and (now - _cache["ts"]) < CACHE_TTL and not force: return _cache["data"] headers = {"Accept": "application/json"} if OR_API_KEY: headers["Authorization"] = f"Bearer {OR_API_KEY}" req = Request(OR_MODELS_URL, headers=headers) try: with urlopen(req, timeout=30) as resp: body = json.loads(resp.read()) _cache["data"] = body.get("data", []) _cache["ts"] = now return _cache["data"] - src/openrouter_mcp/server.py:61-83 (helper)The `_format_model` helper function formats a model dict into a human-readable Markdown string. It is used by `search_models` to render each result entry with provider, context length, pricing, and features.
def _format_model(m: dict, detail=False) -> str: pricing = m.get("pricing", {}) input_p = float(pricing.get("prompt", 0)) output_p = float(pricing.get("completion", 0)) ctx = m.get("context_length", "?") name = m.get("name", m["id"]) provider = m["id"].split("/")[0] supported = m.get("supported_parameters", []) lines = [ f"**{m['id']}**", f" Provider: {provider} | Context: {ctx:,}" if isinstance(ctx, (int, float)) else f" Provider: {provider} | Context: {ctx}", f" Pricing — Input: {_price_str(input_p)} | Output: {_price_str(output_p)}", ] if supported: lines.append(f" Features: {', '.join(supported)}") if detail and m.get("description"): lines.append(f" {m['description'][:150]}") if detail and m.get("architecture", {}).get("modality"): lines.append(f" Modality: {m['architecture']['modality']}") if detail and m.get("top_provider"): lines.append(f" Top provider: {m['top_provider']}") return "\n".join(lines) - src/openrouter_mcp/__init__.py:5-7 (registration)The `search_models` function is re-exported in the package's __init__.py, making it publicly accessible as part of the package's API.
from .server import main, fetch_models, list_models, get_model, search_models, compare_models, refresh_cache __all__ = ["main", "fetch_models", "list_models", "get_model", "search_models", "compare_models", "refresh_cache"]