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

# Anthropic and Claude Agent SDK tool factories

> make_claude_tools() for the Anthropic Messages API and make_claude_agent_sdk_tools() for the Claude Agent SDK, both wiring CodebaseMemory and ImportGraph.

The Claude integration module covers two Anthropic surfaces. `make_claude_tools` targets the `anthropic` Python package and returns `(tool_specs, dispatch)` for a manual `tool_use` loop. `make_claude_agent_sdk_tools` targets the higher-level `claude-agent-sdk` package and returns `@tool`-decorated async functions you pass directly to `query(tools=...)`. Both factories expose the same `CodebaseMemory` and `ImportGraph` capabilities. A `run_messages_agent_loop` helper drives the Messages API tool-use loop end-to-end.

```python theme={null}
from reasonblocks.integrations.claude_tools import (
    make_claude_tools,
    make_claude_agent_sdk_tools,
    run_messages_agent_loop,
)
```

<Note>
  Unlike `make_langchain_tools` and `make_openai_tools`, neither Claude factory exposes an `enable_recall` flag. `recall_findings` is always included whenever `memory` is set; only `store_finding` and `impact_analysis` are toggleable.
</Note>

<Tabs>
  <Tab title="Anthropic Messages API">
    ## make\_claude\_tools

    ```python theme={null}
    make_claude_tools(
        memory: CodebaseMemory,
        graph: ImportGraph | None = None,
        *,
        recall_top_k: int = 5,
        recall_threshold: float = 0.25,
        enable_store: bool = True,
        enable_impact: bool = True,
    ) -> tuple[list[dict], Callable[[str, dict], str]]
    ```

    Returns `(tool_specs, dispatch)` — a list of JSONSchema tool-spec dicts and a callable that executes a named tool.

    ### Parameters

    <ParamField path="memory" type="CodebaseMemory" required>
      The `CodebaseMemory` instance the tools read from and write to. Required — unlike the LangChain factory, `memory` cannot be `None`.
    </ParamField>

    <ParamField path="graph" type="ImportGraph | None" default="None">
      Optional `ImportGraph`. When provided and `enable_impact=True`, an `impact_analysis` spec is added.
    </ParamField>

    <ParamField path="recall_top_k" type="int" default="5">
      Maximum number of findings returned by `recall_findings`.
    </ParamField>

    <ParamField path="recall_threshold" type="float" default="0.25">
      Minimum similarity score for recall results.
    </ParamField>

    <ParamField path="enable_store" type="bool" default="true">
      Include the `store_finding` spec.
    </ParamField>

    <ParamField path="enable_impact" type="bool" default="true">
      Include the `impact_analysis` spec when a `graph` is provided.
    </ParamField>

    ### Returns

    <ResponseField name="tool_specs" type="list[dict]">
      A list of `{"name", "description", "input_schema"}` dicts in the shape `client.messages.create(tools=...)` expects.
    </ResponseField>

    <ResponseField name="dispatch" type="Callable[[str, dict], str]">
      A callable `dispatch(tool_name, tool_input) -> str` that executes the named tool and returns its string result. Raises `KeyError` for unknown tool names. Exceptions inside the handler are caught and returned as a `(tool '<name>' raised: ...)` error string rather than propagated.
    </ResponseField>

    ### Manual tool-use loop

    ```python theme={null}
    import anthropic
    from reasonblocks import CodebaseMemory, ImportGraph
    from reasonblocks.integrations.claude_tools import make_claude_tools

    import pathlib

    client = anthropic.Anthropic()
    memory = CodebaseMemory(codebase_id="my-repo", api_key="rb_live_...")
    graph = ImportGraph().build_from_files(
        {str(p): p.read_text() for p in pathlib.Path("myrepo").rglob("*.py")}
    )

    tool_specs, dispatch = make_claude_tools(memory, graph)

    messages = [{"role": "user", "content": "Find the bug in auth/session.py"}]

    while True:
        resp = client.messages.create(
            model="claude-sonnet-4-6",
            max_tokens=4096,
            tools=tool_specs,
            messages=messages,
        )
        messages.append({"role": "assistant", "content": resp.content})

        if resp.stop_reason != "tool_use":
            break

        tool_results = []
        for block in resp.content:
            if block.type == "tool_use":
                result = dispatch(block.name, block.input)
                tool_results.append({
                    "type": "tool_result",
                    "tool_use_id": block.id,
                    "content": result,
                })
        messages.append({"role": "user", "content": tool_results})
    ```

    ## run\_messages\_agent\_loop

    `run_messages_agent_loop` runs the full Anthropic Messages tool-use loop, handling every `tool_use` / `tool_result` turn for you.

    ```python theme={null}
    run_messages_agent_loop(
        client: Any,
        *,
        model: str,
        messages: list[dict],
        tool_specs: list[dict],
        dispatch: Callable[[str, dict], str],
        system: str = "",
        max_steps: int = 40,
        max_tokens: int = 4096,
    ) -> dict
    ```

    ### Parameters

    <ParamField path="client" type="anthropic.Anthropic" required>
      A synchronous `anthropic.Anthropic` client. The helper calls `client.messages.create` directly.
    </ParamField>

    <ParamField path="model" type="str" required>
      The Anthropic model ID, e.g. `"claude-haiku-4-5-20251001"` or `"claude-sonnet-4-6"`.
    </ParamField>

    <ParamField path="messages" type="list[dict]" required>
      Initial message list in Anthropic format. The helper appends assistant and tool-result turns in place; pass a copy if you want to preserve the original.
    </ParamField>

    <ParamField path="tool_specs" type="list[dict]" required>
      The tool spec list returned by `make_claude_tools`.
    </ParamField>

    <ParamField path="dispatch" type="Callable[[str, dict], str]" required>
      The dispatch callable returned by `make_claude_tools`.
    </ParamField>

    <ParamField path="system" type="str" default="&#x22;&#x22;">
      Optional system prompt. Forwarded to `client.messages.create` only when non-empty.
    </ParamField>

    <ParamField path="max_steps" type="int" default="40">
      Maximum number of `client.messages.create` calls before the loop exits with `stop_reason="max_steps"`.
    </ParamField>

    <ParamField path="max_tokens" type="int" default="4096">
      `max_tokens` value forwarded to every `client.messages.create` call.
    </ParamField>

    ### Return value

    <ResponseField name="final_text" type="str">
      Concatenated text from the last assistant turn that emitted text. Empty string when the loop exits via `max_steps` with no text output.
    </ResponseField>

    <ResponseField name="messages" type="list[dict]">
      Full message history including assistant turns and tool-result turns appended during the loop.
    </ResponseField>

    <ResponseField name="stop_reason" type="str">
      Stop reason from the final API call, or `"max_steps"` if the loop hit the step limit. Common values: `"end_turn"`, `"max_steps"`, `"stop_sequence"`.
    </ResponseField>

    <ResponseField name="tool_calls" type="list[tuple[str, dict, str]]">
      Every tool call as `(tool_name, tool_input, result)` in execution order.
    </ResponseField>

    ### Example

    ```python theme={null}
    from reasonblocks.integrations.claude_tools import (
        make_claude_tools,
        run_messages_agent_loop,
    )

    tool_specs, dispatch = make_claude_tools(memory, graph)

    outcome = run_messages_agent_loop(
        client,
        model="claude-sonnet-4-6",
        messages=[{"role": "user", "content": "Audit the payment module"}],
        tool_specs=tool_specs,
        dispatch=dispatch,
        system="You are a senior Python code reviewer.",
        max_steps=30,
    )

    print(outcome["final_text"])
    for name, inp, result in outcome["tool_calls"]:
        print(f"  {name}({inp}) -> {result[:80]}")
    ```
  </Tab>

  <Tab title="Claude Agent SDK">
    ## make\_claude\_agent\_sdk\_tools

    ```python theme={null}
    make_claude_agent_sdk_tools(
        memory: CodebaseMemory,
        graph: ImportGraph | None = None,
        *,
        recall_top_k: int = 5,
        recall_threshold: float = 0.25,
        enable_store: bool = True,
        enable_impact: bool = True,
    ) -> list
    ```

    Returns `@tool`-decorated async callables for the `claude-agent-sdk` package. The decorator returns SDK-specific descriptors, so the import is deferred — callers without the package never pay for it.

    <Warning>
      This function imports from `claude_agent_sdk` at call time. If the package is not installed, it raises `ImportError` with an install hint. Install with `pip install claude-agent-sdk`.
    </Warning>

    ### Parameters

    <ParamField path="memory" type="CodebaseMemory" required>
      The `CodebaseMemory` instance bound to the current run.
    </ParamField>

    <ParamField path="graph" type="ImportGraph | None" default="None">
      Optional `ImportGraph`. Adds `impact_analysis` when provided and `enable_impact=True`.
    </ParamField>

    <ParamField path="recall_top_k" type="int" default="5">
      Maximum number of findings per `recall_findings` call.
    </ParamField>

    <ParamField path="recall_threshold" type="float" default="0.25">
      Minimum similarity score for recall results.
    </ParamField>

    <ParamField path="enable_store" type="bool" default="true">
      Include the `store_finding` tool.
    </ParamField>

    <ParamField path="enable_impact" type="bool" default="true">
      Include `impact_analysis` when a `graph` is provided.
    </ParamField>

    ### Returns

    <ResponseField name="tools" type="list">
      A list of `@tool`-decorated async callables for `query(tools=...)` or `ClaudeSDKClient`.
    </ResponseField>

    ### Example

    ```python theme={null}
    from claude_agent_sdk import query
    from reasonblocks import CodebaseMemory, ImportGraph
    from reasonblocks.integrations.claude_tools import make_claude_agent_sdk_tools

    import pathlib

    memory = CodebaseMemory(codebase_id="my-repo", api_key="rb_live_...")
    graph = ImportGraph().build_from_files(
        {str(p): p.read_text() for p in pathlib.Path("myrepo").rglob("*.py")}
    )

    rb_tools = make_claude_agent_sdk_tools(memory, graph)

    async for message in query(
        prompt="Find and explain the auth bug",
        tools=[*rb_tools, *your_tools],
    ):
        print(message)
    ```
  </Tab>
</Tabs>

## Tools available in both factories

| Tool                                              | Always included?                             | Description                                           |
| ------------------------------------------------- | -------------------------------------------- | ----------------------------------------------------- |
| `recall_findings(query)`                          | Yes (when `memory` is set)                   | Search `CodebaseMemory` for findings matching a query |
| `store_finding(content, file_path, finding_type)` | When `enable_store=True`                     | Persist a finding                                     |
| `impact_analysis(file_path)`                      | When `graph` is set and `enable_impact=True` | Return dependents and dependencies                    |
