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

> ## Agent Instructions
> H Platform has four products: the Agents API (managed computer-use agents, base URL https://agp.eu.hcompany.ai/api/v2 or https://agp.hcompany.ai/api/v2 for the US), the Models API (OpenAI-compatible Holo vision-language models at https://api.hcompany.ai/v1), HoloDesktop CLI (Holo on the user's own desktop), and HoloTab (a free no-code Chrome extension that runs Holo in the user's browser, with recordable routines and schedules).
> Authenticate with a bearer API key from the HAI_API_KEY environment variable. SDKs: `pip install hai-agents` (Python, `from hai_agents import Client`) and `npm install hai-agents` (TypeScript, `import { HaiAgentsClient } from "hai-agents"`). CLI: `hai`.
> Agents do work in a browser or on a desktop; describe the task as an imperative instruction. To run a task quickly, prefer the pre-built agent `h/web-surfer-flash`. Read results from the session's `latest_answer` after it reaches a terminal status.
> Sessions are the unit of work; wait for a terminal status (completed, failed, timed_out, interrupted) before reading the answer. Use webhooks or the `changes` long-poll endpoint to follow progress.

# MCP agent

> Connect Holo to any MCP server: list its tools, pass them as OpenAI functions, and route each tool call back through the MCP session.

MCP servers describe their tools with JSON schemas, which is what the `tools` field expects. So connecting Holo to one takes a single conversion function: list the server's tools, pass them in, and send each call back to the server. This example uses the reference [fetch server](https://github.com/modelcontextprotocol/servers/tree/main/src/fetch), which reads web pages as Markdown. Any other stdio server works the same way.

<Steps titleSize="h3">
  <Step title="Install">
    The fetch server runs through `uvx`, which ships with [uv](https://docs.astral.sh/uv/).

    ```bash theme={"system"}
    pip install openai "mcp>=2"
    export HAI_API_KEY="your-api-key-here"
    ```
  </Step>

  <Step title="Save the agent">
    ```python mcp_agent.py expandable theme={"system"}
    import asyncio
    import json
    import os

    from mcp import ClientSession, StdioServerParameters
    from mcp.client.stdio import stdio_client
    from openai import AsyncOpenAI

    client = AsyncOpenAI(base_url="https://api.hcompany.ai/v1/", api_key=os.environ["HAI_API_KEY"])
    MODEL = "holo4-35b-a3b"

    ANSWER_TOOL = {
        "type": "function",
        "function": {
            "name": "answer",
            "description": "Provide a final answer",
            "parameters": {
                "type": "object",
                "properties": {"content": {"type": "string", "description": "The answer content"}},
                "required": ["content"],
            },
        },
    }


    def to_openai_tool(tool) -> dict:
        return {
            "type": "function",
            "function": {"name": tool.name, "description": tool.description or "", "parameters": tool.input_schema},
        }


    async def run(task: str, max_steps: int = 10) -> str:
        server = StdioServerParameters(command="uvx", args=["mcp-server-fetch"])
        async with stdio_client(server) as (read, write), ClientSession(read, write) as session:
            await session.initialize()
            mcp_tools = (await session.list_tools()).tools
            tools = [to_openai_tool(t) for t in mcp_tools] + [ANSWER_TOOL]

            messages = [
                {"role": "system", "content": "You are a research agent. Use your tools to gather facts, then answer."},
                {"role": "user", "content": task},
            ]
            for _ in range(max_steps):
                resp = await client.chat.completions.create(
                    model=MODEL,
                    messages=messages,
                    tools=tools,
                    tool_choice="required",
                    temperature=0.8,
                    extra_body={"chat_template_kwargs": {"enable_thinking": True}},
                )
                msg = resp.choices[0].message
                messages.append({"role": "assistant", "content": msg.content, "tool_calls": msg.tool_calls})
                if not msg.tool_calls:
                    continue
                call = msg.tool_calls[0]
                args = json.loads(call.function.arguments)
                if call.function.name == "answer":
                    return args["content"]
                print(f"--- {call.function.name}({args})")
                result = await session.call_tool(call.function.name, args)
                text = "\n".join(c.text for c in result.content if c.type == "text")
                messages.append({"role": "tool", "tool_call_id": call.id, "content": text[:8000]})
        return "Step budget exhausted"


    if __name__ == "__main__":
        print(asyncio.run(run("Fetch https://docs.python.org/3/whatsnew/3.13.html and list the three headline features of Python 3.13.")))
    ```
  </Step>

  <Step title="Run it">
    ```bash theme={"system"}
    python mcp_agent.py
    ```

    ```text Output theme={"system"}
    --- fetch({'url': 'https://docs.python.org/3/whatsnew/3.13.html'})
    --- fetch({'url': 'https://docs.python.org/3/whatsnew/3.13.html'})
    --- fetch({'url': 'https://docs.python.org/3/whatsnew/3.13.html', 'raw': True})
    --- fetch({'url': 'https://docs.python.org/3/whatsnew/3.13.html', 'raw': True, 'start_index': 15000})
    --- fetch({'url': 'https://docs.python.org/3/whatsnew/3.13.html', 'raw': True, 'start_index': 20000})
    The three headline features of Python 3.13 are:

    1. **A new interactive interpreter** - A greatly improved REPL with enhanced features and color support
    2. **Experimental free-threaded mode** - Support for running CPython without the Global Interpreter Lock (GIL), enabling true parallel execution of threads (PEP 703)
    3. **A Just-In-Time (JIT) compiler** - A basic JIT compiler was added to improve performance (PEP 744), though it is disabled by default
    ```

    Holo pages through the document on its own, using the `raw` and `start_index` arguments it found in the server's tool schema.
  </Step>
</Steps>

## How it works

* **Tool names come from the server.** Holo sees `fetch` exactly as the server declares it, so the call can go straight back through `session.call_tool`. Add `answer` yourself: MCP servers do not provide it.
* **Results are text.** MCP results are a list of content blocks. The loop keeps the text blocks and caps them at 8,000 characters.
* **Several servers.** Open one `ClientSession` per server, merge their tool lists, and keep a map from tool name to session to route each call.

For remote servers, swap `stdio_client` for `streamable_http_client` from `mcp.client.streamable_http`. The rest of the loop stays as is.

## Next steps

<CardGroup cols={2}>
  <Card title="Hybrid agent" icon="shuffle" href="/models-api/cookbooks/hybrid-agent">
    Mix screen and code tools in one loop.
  </Card>

  <Card title="Function calling" icon="wrench" href="/models-api/build-an-agent/function-calling">
    The format these tools use.
  </Card>
</CardGroup>
