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 anthropicnpm install @composio/core @composio/anthropic @anthropic-ai/sdkConfigure API Keys
Set COMPOSIO_API_KEY with your API key from Settings and ANTHROPIC_API_KEY with your Anthropic API key.
COMPOSIO_API_KEY=xxxxxxxxx
ANTHROPIC_API_KEY=xxxxxxxxxCreate 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: trueto the constructor (new AnthropicProvider({ cacheTools: true })) to attach Anthropic's ephemeralcache_controlto 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. handleToolCallsreturns Messages API content, not raw strings. On the TypeScript side it returnsAnthropic.Messages.MessageParam[](ausermessage oftool_resultblocks) that you append directly to your message list. This is why the loop above does not buildtool_resultblocks by hand.- String-encoded tool inputs are handled for you. Claude occasionally emits a tool's
inputas a JSON string instead of an object. The provider normalizes this before execution, so you do not have to parse it yourself. cacheToolsonly. 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.