# `HuggingfaceClient.Agent`
[🔗](https://github.com/huggingface/huggingface_client/blob/v0.1.0/lib/huggingface_client/agent/agent.ex#L1)

Agentic chat loop with tool use — an Elixir port of `@huggingface/mcp-client` Agent.

Connects to one or more MCP (Model Context Protocol) servers, discovers their tools,
and runs a multi-turn agentic loop where the LLM can call tools as needed to answer
the user's query.

## Quick start

    agent = HuggingfaceClient.Agent.new(
      model: "meta-llama/Llama-3.1-8B-Instruct",
      provider: "groq",
      access_token: "hf_...",
      servers: [
        %{type: "http", url: "https://my-mcp-server.example.com/mcp"}
      ]
    )

    # Stream the response
    {:ok, stream} = HuggingfaceClient.Agent.run(agent, "What files are in the current directory?")
    Enum.each(stream, fn
      {:content, text}           -> IO.write(text)
      {:tool_call, name, result} -> IO.puts("\n[#{name}]: #{result}\n")
      :done                      -> IO.puts("\n--- done ---")
    end)

## MCP server types

- `%{type: "http", url: "https://..."}` — HTTP/SSE MCP server
- `%{type: "sse", url: "https://..."}` — SSE-only MCP server

## Agent loop

The agent runs up to `max_turns` (default: 10) rounds of:
1. Send messages + available tools to the LLM
2. Stream the response, collecting tool calls
3. Execute each tool call against the appropriate MCP server
4. Add tool results to the conversation
5. Repeat until no more tool calls, or `task_complete` / `ask_question` tools are called

## System prompt

A default prompt instructs the LLM to use tools proactively and keep going until
the task is fully resolved. Override with the `:prompt` option.

# `server_config`

```elixir
@type server_config() ::
  %{type: String.t(), url: String.t()}
  | %{type: String.t(), url: String.t(), headers: map()}
```

# `t`

```elixir
@type t() :: %HuggingfaceClient.Agent{
  access_token: String.t() | nil,
  endpoint_url: String.t() | nil,
  max_turns: non_neg_integer(),
  model: String.t(),
  prompt: String.t(),
  provider: String.t() | nil,
  servers: [server_config()],
  tools: [map()]
}
```

# `load_tools`

```elixir
@spec load_tools(t()) :: {:ok, t()} | {:error, term()}
```

Discovers tools from all configured MCP servers.

Makes HTTP requests to each server's `/tools/list` endpoint and merges
the results into the agent's tool list. Returns an updated agent struct.

## Example

    agent = HuggingfaceClient.Agent.new(model: "...", servers: [...])
    {:ok, agent} = HuggingfaceClient.Agent.load_tools(agent)
    IO.puts("Available tools: #{length(agent.tools)}")

# `new`

```elixir
@spec new(keyword()) :: t()
```

Creates a new agent struct.

## Options

- `:model` — model ID (required)
- `:provider` — inference provider (e.g. `"groq"`, `"together"`)
- `:endpoint_url` — custom endpoint URL (alternative to provider)
- `:access_token` — HF or provider API token
- `:servers` — list of MCP server configs
- `:prompt` — system prompt override
- `:max_turns` — max agentic loop iterations (default: 10)

# `run`

```elixir
@spec run(t(), String.t() | [map()]) ::
  {:ok, Enumerable.t()} | {:error, Exception.t()}
```

Runs the agentic loop for a user input string.

Returns `{:ok, stream}` where each stream element is one of:
- `{:delta, text}` — streamed text token from the LLM
- `{:tool_start, tool_id, tool_name, args_json}` — tool call started
- `{:tool_result, tool_id, tool_name, result}` — tool call result received
- `:done` — agent turn complete

## Example

    {:ok, agent} = HuggingfaceClient.Agent.load_tools(agent)

    {:ok, stream} = HuggingfaceClient.Agent.run(agent, "List all Python files in the repo")
    Enum.each(stream, fn
      {:delta, text}                   -> IO.write(text)
      {:tool_result, _, name, result}  -> IO.puts("\n[#{name}]: #{result}")
      :done                            -> :ok
    end)

---

*Consult [api-reference.md](api-reference.md) for complete listing*
