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

# AI agent tool

> Connect an AI agent to Andi search via MCP, or define search and fetch as tools for any agent framework.

There are two ways to give your AI agent web search: connect the MCP server (zero code), or define search and fetch as tool functions in your framework.

## MCP server (recommended)

The fastest path is to point your agent at the install guide. It will configure itself:

```text theme={null}
https://api.andiai.com/install.md
```

Or connect directly — for example, in Claude Code:

```bash theme={null}
claude mcp add --transport http andi https://api.andiai.com/mcp --header "x-api-key: YOUR_API_KEY"
```

The MCP server exposes two tools: `andi_web_search` and `andi_fetch_url`. Your agent can search the web and then fetch full page content from any result. See [Build with AI agents](/resources/ai-agents) for setup snippets for Cursor, VS Code, Codex, and other clients.

## Tool definition (custom frameworks)

If your agent framework uses function/tool definitions instead of MCP, define the search tool with parameters and expected output:

<CodeGroup>
  ```python Python (OpenAI-compatible) theme={null}
  search_tool = {
      "type": "function",
      "function": {
          "name": "web_search",
          "description": "Search the web for current information. Use this when the user asks about recent events, facts you're unsure about, or anything that requires up-to-date information.",
          "parameters": {
              "type": "object",
              "properties": {
                  "query": {
                      "type": "string",
                      "description": "The search query",
                  },
                  "limit": {
                      "type": "integer",
                      "description": "Number of results (1-100, default 10)",
                  },
                  "searchMode": {
                      "type": "string",
                      "enum": ["auto", "low-cost", "fast", "balanced", "deep", "exhaustive"],
                      "description": "Search mode. auto (default) sets the right effort per query; pin a mode (low-cost, fast, balanced, deep, exhaustive) to control it",
                  },
                  "effort": {
                      "type": "string",
                      "enum": ["low", "medium", "high", "max"],
                      "description": "How hard to try — low favors speed, max favors thoroughness. Omit for the adaptive default; searchMode wins if both are set",
                  },
              },
              "required": ["query"],
          },
      },
  }
  ```

  ```javascript JavaScript (OpenAI-compatible) theme={null}
  const searchTool = {
    type: "function",
    function: {
      name: "web_search",
      description:
        "Search the web for current information. Use this when the user asks about recent events, facts you're unsure about, or anything that requires up-to-date information.",
      parameters: {
        type: "object",
        properties: {
          query: {
            type: "string",
            description: "The search query",
          },
          limit: {
            type: "integer",
            description: "Number of results (1-100, default 10)",
          },
          searchMode: {
            type: "string",
            enum: ["auto", "low-cost", "fast", "balanced", "deep", "exhaustive"],
            description: "Search mode. auto (default) sets the right effort per query; pin a mode (low-cost, fast, balanced, deep, exhaustive) to control it",
          },
          effort: {
            type: "string",
            enum: ["low", "medium", "high", "max"],
            description: "How hard to try — low favors speed, max favors thoroughness. Omit for the adaptive default; searchMode wins if both are set",
          },
        },
        required: ["query"],
      },
    },
  };
  ```
</CodeGroup>

## Tool execution

When the agent calls the tool, execute the search and return results:

<CodeGroup>
  ```python Python theme={null}
  import os
  import json
  import requests

  api_key = os.environ["ANDI_API_KEY"]


  def execute_web_search(query: str, limit: int = 5, searchMode: str = "auto", effort: str = None) -> str:
      """Execute a web search and return formatted results."""
      params = {"q": query, "limit": limit, "searchMode": searchMode, "extracts": "true"}
      if effort:
          params["effort"] = effort
      response = requests.get(
          "https://api.andiai.com/api/v1/search",
          params=params,
          headers={"x-api-key": api_key},
      )

      if response.status_code != 200:
          return json.dumps({"error": response.json().get("error", "Search failed")})

      data = response.json()
      results = []
      for r in data["results"]:
          result = {
              "title": r["title"],
              "url": r["link"],
              "description": r["desc"],
          }
          if r.get("extracts"):
              result["content"] = " ".join(r["extracts"])
          results.append(result)

      return json.dumps(results)
  ```

  ```javascript JavaScript theme={null}
  const apiKey = process.env.ANDI_API_KEY;

  async function executeWebSearch(query, limit = 5, searchMode = "auto", effort = null) {
    const url = new URL("https://api.andiai.com/api/v1/search");
    url.searchParams.set("q", query);
    url.searchParams.set("limit", String(limit));
    url.searchParams.set("searchMode", searchMode);
    url.searchParams.set("extracts", "true");
    if (effort) {
      url.searchParams.set("effort", effort);
    }

    const response = await fetch(url, {
      headers: { "x-api-key": apiKey },
    });

    if (!response.ok) {
      const error = await response.json();
      return JSON.stringify({ error: error.error || "Search failed" });
    }

    const data = await response.json();
    return JSON.stringify(
      data.results.map((r) => ({
        title: r.title,
        url: r.link,
        description: r.desc,
        content: r.extracts ? r.extracts.join(" ") : undefined,
      }))
    );
  }
  ```
</CodeGroup>

## Search then fetch workflow

An agent can search for results, then fetch full content from the most relevant page. This example pins `searchMode=deep` so every call gets broad coverage before the fetch — omit it to let `auto` decide per query:

```python theme={null}
def search_and_fetch(query: str) -> str:
    """Search, then fetch the top result's full content."""
    # Step 1: Search
    search_resp = requests.get(
        "https://api.andiai.com/api/v1/search",
        params={"q": query, "limit": 3, "searchMode": "deep"},
        headers={"x-api-key": api_key},
    )
    results = search_resp.json()["results"]
    if not results:
        return "No results found."

    # Step 2: Fetch the top result
    fetch_resp = requests.get(
        "https://api.andiai.com/api/v1/fetch",
        params={"url": results[0]["link"], "format": "context"},
        headers={"x-api-key": api_key},
    )

    if fetch_resp.status_code == 200:
        return fetch_resp.text
    elif fetch_resp.status_code == 503:
        return "Page is still loading — retry shortly."
    else:
        return f"Could not fetch page (status {fetch_resp.status_code})."
```

## Putting it together

Here's how the tool fits into an agent loop:

```python theme={null}
import openai

client = openai.OpenAI()

messages = [
    {"role": "system", "content": "You are a helpful assistant with web search access. Cite sources with URLs."},
    {"role": "user", "content": "What are the latest developments in quantum computing?"},
]

# Step 1: LLM decides to call the tool
response = client.chat.completions.create(
    model="gpt-4o",
    messages=messages,
    tools=[search_tool],
)

message = response.choices[0].message

# Step 2: Execute tool calls
if message.tool_calls:
    messages.append(message)
    for tool_call in message.tool_calls:
        args = json.loads(tool_call.function.arguments)
        result = execute_web_search(**args)

        messages.append({
            "role": "tool",
            "tool_call_id": tool_call.id,
            "content": result,
        })

    # Step 3: LLM generates final answer with search context
    final = client.chat.completions.create(
        model="gpt-4o",
        messages=messages,
    )
    print(final.choices[0].message.content)
```

## Using `format=context` for agents

For agents that pass search results directly into conversation context, `format=context` returns pre-formatted markdown:

```python theme={null}
def execute_web_search_context(query: str) -> str:
    """Return search results as markdown text."""
    response = requests.get(
        "https://api.andiai.com/api/v1/search",
        params={"q": query, "format": "context", "limit": 5},
        headers={"x-api-key": api_key},
    )
    return response.text
```

<Tip>
  `format=context` reduces the code in your tool executor — no JSON parsing or formatting needed. The tradeoff is less control over result structure.
</Tip>

## Next steps

<CardGroup cols={2}>
  <Card title="Build with AI agents" icon="robot" href="/resources/ai-agents">
    MCP server setup for Claude Code, Cursor, and more.
  </Card>

  <Card title="Content retrieval" icon="file-lines" href="/search/content-retrieval">
    Full fetch endpoint reference.
  </Card>

  <Card title="Search modes" icon="gauge-high" href="/search/search-modes">
    Automatic effort by default, manual control when you want it.
  </Card>

  <Card title="Error handling" icon="triangle-exclamation" href="/resources/error-handling">
    Error codes and retry strategies.
  </Card>
</CardGroup>
