Anthropic

The Anthropic provider formats Composio tools for Claude and executes the tool calls Claude returns. It works two ways:

  • The Claude Messages API, where you run the tool-call loop yourself.
  • The Claude Agent SDK, where the SDK runs the loop and Composio tools are exposed as an in-process MCP server.

Pick the tab that matches your integration.

The AnthropicProvider transforms Composio tools into the format the Claude Messages API expects, then executes the tool calls Claude requests and shapes the results back into Messages API content blocks.

Install

pip install composio composio_anthropic anthropic
npm install @composio/core @composio/anthropic @anthropic-ai/sdk

Configure API Keys

Set COMPOSIO_API_KEY with your API key from Settings and ANTHROPIC_API_KEY with your Anthropic API key.

.env
COMPOSIO_API_KEY=xxxxxxxxx
ANTHROPIC_API_KEY=xxxxxxxxx

Create session and run

import json
import anthropic
from composio import Composio
from composio_anthropic import AnthropicProvider

composio = Composio(provider=AnthropicProvider())
client = anthropic.Anthropic()

# Create a session for your user
session = composio.create(user_id="user_123")
tools = session.tools()

messages = [
    {"role": "user", "content": "Send an email to john@example.com with the subject 'Hello' and body 'Hello from Composio!'"}
]

response = client.messages.create(
    model="claude-opus-4-6",
    max_tokens=4096,
    tools=tools,
    messages=messages,
)

# Agentic loop: keep executing tool calls until the model responds with text
while response.stop_reason == "tool_use":
    tool_use_blocks = [block for block in response.content if block.type == "tool_use"]
    results = composio.provider.handle_tool_calls(user_id="user_123", response=response)
    messages.append({"role": "assistant", "content": response.content})
    messages.append({
        "role": "user",
        "content": [
            {"type": "tool_result", "tool_use_id": tool_use_blocks[i].id, "content": json.dumps(result)}
            for i, result in enumerate(results)
        ]
    })
    response = client.messages.create(
        model="claude-opus-4-6",
        max_tokens=4096,
        tools=tools,
        messages=messages,
    )

# Print final response
for block in response.content:
    if block.type == "text":
        print(block.text)
import Anthropic from '@anthropic-ai/sdk';
import { Composio } from '@composio/core';
import { AnthropicProvider } from '@composio/anthropic';

const composio = new Composio({
    provider: new AnthropicProvider(),
});
const client = new Anthropic();

// Create a session for your user
const session = await composio.create("user_123");
const tools = await session.tools();

const messages: Anthropic.MessageParam[] = [
    {
        role: "user",
        content: "Send an email to john@example.com with the subject 'Hello' and body 'Hello from Composio!'"
    },
];

let response = await client.messages.create({
    model: "claude-opus-4-6",
    max_tokens: 4096,
    tools: tools,
    messages: messages,
});

// Agentic loop: keep executing tool calls until the model responds with text
while (response.stop_reason === "tool_use") {
    const toolResults = await composio.provider.handleToolCalls("user_123", response);
    messages.push({ role: "assistant", content: response.content });
    messages.push(...toolResults);
    response = await client.messages.create({
        model: "claude-opus-4-6",
        max_tokens: 4096,
        tools: tools,
        messages: messages,
    });
}

// Print final response
for (const block of response.content) {
    if (block.type === "text") {
        console.log(block.text);
    }
}

handleToolCalls does the loop body for you. It extracts every tool_use block from the response, executes the matching Composio tools, and returns a ready-to-append array of Messages API content (a user message holding tool_result blocks). That is why the TypeScript example spreads the result straight into messages with messages.push(...toolResults). In Python it returns the raw result list, which the example wraps into tool_result blocks by hand.

Provider specifics

A few things are specific to the Anthropic provider:

  • Tool caching. Pass cacheTools: true to the constructor (new AnthropicProvider({ cacheTools: true })) to attach Anthropic's ephemeral cache_control to every tool definition and tool-result block. This lets Claude reuse cached tool schemas across requests and can cut prompt cost when you send the same large tool set on every turn.
  • handleToolCalls returns Messages API content, not raw strings. On the TypeScript side it returns Anthropic.Messages.MessageParam[] (a user message of tool_result blocks) that you append directly to your message list. This is why the loop above does not build tool_result blocks by hand.
  • String-encoded tool inputs are handled for you. Claude occasionally emits a tool's input as a JSON string instead of an object. The provider normalizes this before execution, so you do not have to parse it yourself.
  • cacheTools only. The constructor takes no other options. There is no agentic execution in this provider; you run the loop (Messages API) or hand the loop to the Claude Agent SDK (Agent SDK tab).

Next

What is a session?

How sessions scope users, tools, and auth, and how to reuse them across requests.