# `ConduitMcp.Client.Adapters.Anthropic`
[🔗](https://github.com/nyo16/conduit_mcp/blob/v0.11.0/lib/conduit_mcp/client/adapters/anthropic.ex#L1)

Converts between a `ConduitMcp.Client.ToolSet` and the Anthropic Messages
API tool format. Pure functions over string-keyed maps; no HTTP.

    {toolset, _report} = ConduitMcp.Client.Naming.build([{"files", tools}])

    request = %{"model" => model, "messages" => messages,
                "tools" => Anthropic.tools(toolset, cache_control: :last)}

    # ... the response stops with "tool_use" ...
    {calls, rejected} = Anthropic.tool_calls(response, toolset)
    # run each %ToolCall{} through the client pipeline (or let
    # ConduitMcp.Client.Adapters.execute_tool_calls/4 do all of this), then:
    results = Enum.map(outcomes, fn {call, outcome} -> Anthropic.tool_result(call.call_id, outcome) end)
    # next user turn: %{"role" => "user", "content" => rejected ++ results ++ other_blocks}

## Tool definitions

`tools/2` emits `%{"name", "description", "input_schema"}` per tool in
toolset order. Names are the toolset's exposed names, which satisfy
Anthropic's `^[a-zA-Z0-9_-]{1,128}$`. Descriptions have invisible
characters (bidi, zero-width, tag, other controls) removed and are capped
at 4 KiB (PI-1). Only tools in the toolset are emitted, so tools removed by
a filter, a collision or a down server never reach the model (PO-7).

## No forced `tool_choice`

This module never builds a `tool_choice` of type `any` or `tool`. Current
Claude models (Opus 5.5, Sonnet 5.5, Fable 5.1, Mythos 5.1) reject forced
tool use with HTTP 400; leave `tool_choice` unset (or `auto`/`none`).
Source: https://platform.claude.com/docs/en/agents-and-tools/tool-use/implement-tool-use
(verified 2026-10-17).

## Results

`tool_result/3` maps MCP content into `tool_result.content`:

| MCP content | Anthropic block |
|---|---|
| `text` | `text` |
| `image` (JPEG/PNG/GIF/WebP) | `image` with a base64 source |
| other images (e.g. SVG), `audio` | `text` placeholder `"[image: mime, n bytes]"` |
| `resource_link` | `text` `"[resource] name <uri> (mime)"` |
| embedded text `resource` | `text` with a `[resource: uri]` header |
| embedded blob `resource` | `image` if an accepted image type, else a placeholder |

`is_error` is `true` for a `ToolResult` with `is_error: true` and for any
`ConduitMcp.Client.Error`. `tool_result` blocks must come first in the
next user turn.

# `tool_calls`

```elixir
@spec tool_calls(map() | [map()], ConduitMcp.Client.ToolSet.t()) ::
  {[ConduitMcp.Client.ToolCall.t()], [map()]}
```

Extracts the tool calls from a response.

Accepts a Messages API response or assistant message (anything with a
`"content"` list) or the content block list itself. Every `tool_use` block
whose `name` resolves in `toolset` becomes a `ConduitMcp.Client.ToolCall`
for the tool's server and MCP name, with `call_id` set to the block's `id`.

A `tool_use` naming a tool that is not in the toolset, or whose `input` is
not an object, produces no call (nothing may be sent to a server, HB-2).
Instead it yields an `is_error` `tool_result` block, returned in the
second list, to put in the next user turn. Other block types are ignored.

# `tool_result`

```elixir
@spec tool_result(
  String.t(),
  ConduitMcp.Client.ToolResult.t() | ConduitMcp.Client.Error.t(),
  keyword()
) ::
  map()
```

Builds the `tool_result` block answering `tool_use_id`.

## Options

  * `:structured` - how `structuredContent` is used: `:auto` (default, only
    when `content` is empty), `:prefer`, `:append` or `:ignore`. It is
    rendered as JSON text.

# `tools`

```elixir
@spec tools(ConduitMcp.Client.ToolSet.t(), keyword()) :: [map()]
```

The `tools` request parameter for `toolset`.

## Options

  * `:cache_control` - `:last` sets `"cache_control" => %{"type" => "ephemeral"}`
    on the last tool, caching the whole tool list as a prompt prefix.
    Default `nil` (no cache marker).

---

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