HuggingfaceClient.Agent (huggingface_client v0.1.0)

Copy Markdown View Source

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.

Summary

Functions

Discovers tools from all configured MCP servers.

Creates a new agent struct.

Runs the agentic loop for a user input string.

Types

server_config()

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

t()

@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()]
}

Functions

load_tools(agent)

@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(opts)

@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(agent, input)

@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)