January 1, 2024
Anthropic’s Claude and MCP: A Deep Dive into Content-Based Tool Integration
Anthropic’s Claude and MCP: A Deep Dive into Content-Based Tool Integration
When integrating AI models with external tools, the details matter most. While OpenAI uses function calling and LiteLLM provides a universal interface, Anthropic’s Claude takes a distinctly different approach with its content-based message structure. This article explores how to integrate Claude with the Model Context Protocol (MCP). We reveal the unique patterns that make Anthropic’s implementation powerful and elegant.
We’ll dive deep into the code and compare Anthropic’s approach with OpenAI and LiteLLM. This helps you understand when and why to choose each integration method. By the end, you’ll have a thorough understanding of how Claude’s content-based architecture shapes its MCP integration.
About Our MCP Server: The Customer Service Assistant
Before exploring how Claude connects to MCP, let’s understand what we’re connecting to. In our comprehensive MCP guide, we built a complete customer service MCP server using FastMCP. This server serves as our foundation for demonstrating different client integrations.
Our MCP server exposes three powerful tools that any AI system can use:
Available Tools:
- get_recent_customers: Retrieves a list of recently active customers with their current status
- create_support_ticket: Creates new support tickets with customizable priority levels
- calculate_account_value: Analyzes purchase history to calculate total account value
The beauty of MCP is that these same tools work identically with Claude, GPT-4, or any other AI model. Only the integration pattern differs.
Understanding Anthropic’s Content-Based Architecture
Before diving into the code, let’s understand what makes Anthropic’s approach unique. While OpenAI separates message content from function calls, Claude treats everything as content items within a message. This design philosophy creates several key differences:
- Unified Content Model: Text and tool uses are both content items in the same array
- Message Role Semantics: Tool results are user messages, not a separate role
- Inline Processing: Tool calls flow naturally within the conversation
- Type-Based Routing: Content type determines processing logic
Let’s see how these principles manifest in actual code.
Building the Anthropic MCP Integration
Step 1: Setting Up the Connection
The initial setup looks similar to OpenAI, but watch for the subtle differences:
class AnthropicMCPChatBot:
def __init__(self, api_key: str):
self.anthropic = Anthropic(api_key=api_key)
self.sessions = []
self.exit_stack = AsyncExitStack()
self.available_tools = []
self.tool_to_session = {}
The structure mirrors OpenAI’s implementation. This shows that MCP provides a consistent foundation for any AI provider. The real differences emerge when we start handling tools and messages.
Step 2: Tool Format Mapping
Here’s where Anthropic’s approach first diverges. Let’s examine how tools are converted from MCP format to Anthropic’s expected structure:
async def connect_to_server(self, server_name: str, server_config: dict) -> None:
"""Connect to a single MCP server."""
# ... connection setup code ...
# List available tools for this session
response = await session.list_tools()
tools = response.tools
for tool in tools:
self.tool_to_session[tool.name] = session
# Anthropic's tool format is simpler than OpenAI's
self.available_tools.append({
"name": tool.name,
"description": tool.description,
"input_schema": tool.inputSchema, # Note: not 'parameters'
})
```**Key Difference**: Compare this with OpenAI's format:
```python
# OpenAI format
openai_tool = {
"type": "function",
"function": {
"name": tool.name,
"description": tool.description,
"parameters": tool.inputSchema, # Different field name
}
}
# Anthropic format - flatter structure
anthropic_tool = {
"name": tool.name,
"description": tool.description,
"input_schema": tool.inputSchema,
}
Anthropic’s flatter structure shows its philosophy of simplicity and directness. There’s no wrapper object or type specification. A tool is just a tool.
Step 3: The Content-Based Message Processing
This is where Anthropic’s unique architecture truly shines. Instead of checking for separate tool_calls
, we iterate through content items:
async def process_query(self, query: str):
"""Process a query using Claude with MCP tools."""
messages = [{"role": "user", "content": query}]
response = self.anthropic.messages.create(
max_tokens=2024,
model=Config.ANTHROPIC_MODEL,
tools=self.available_tools,
messages=messages,
)
process_query = True
while process_query:
assistant_content = []
for content in response.content:
if content.type == "text":
print(content.text)
assistant_content.append(content)
# If only text, we're done
if len(response.content) == 1:
process_query = False
elif content.type == "tool_use":
assistant_content.append(content)
# More processing needed...
This approach treats the response as a stream of content items. Each has its own type. Let’s break down what happens with each type:
Text Content:
- Displayed immediately to the user
- Added to assistant_content for conversation history
- If it’s the only content, the interaction is complete
Tool Use Content:
- Contains the tool to call and its arguments
- Triggers tool execution
- Requires sending results back to Claude
Step 4: Tool Execution and Result Handling
Here’s where Anthropic’s approach differs most significantly from OpenAI:
elif content.type == "tool_use":
assistant_content.append(content)
# First, we need to acknowledge the assistant's tool use
messages.append({"role": "assistant", "content": assistant_content})
# Extract tool information
tool_id = content.id # Unique ID for this tool use
tool_args = content.input # Arguments as a dict
tool_name = content.name
print(f"Calling tool {tool_name} with args {tool_args}")
# Execute the tool through MCP
session = self.tool_to_session[tool_name]
result = await session.call_tool(tool_name, arguments=tool_args)
# KEY DIFFERENCE: Tool results are user messages!
messages.append({
"role": "user", # Not "tool" like OpenAI
"content": [{
"type": "tool_result",
"tool_use_id": tool_id, # Links to the tool_use
"content": result.content,
}]
})
# Continue the conversation with the tool result
response = self.anthropic.messages.create(
max_tokens=2024,
model=Config.ANTHROPIC_MODEL,
tools=self.available_tools,
messages=messages,
)
```**Critical Insight**: Anthropic treats tool results as user messages with special content. This maintains a clean conversation flow where only users and assistants exist. Tools are just a type of content, not a separate entity.
## Comparing the Three Approaches
Let's visualize how each integration handles the same tool interaction:
```mermaid
sequenceDiagram
participant User
participant AI
participant Tool
Note over User,Tool: Anthropic Flow
User->>AI: "Calculate account value"
AI->>AI: Returns content array:<br/>[{type: "tool_use", name: "calculate"}]
AI->>Tool: Execute calculate_account_value
Tool-->>AI: Result: $552
User->>AI: {role: "user", content: [{type: "tool_result", content: "$552"}]}
AI->>User: "The total account value is $552"
Note over User,Tool: OpenAI Flow
User->>AI: "Calculate account value"
AI->>AI: Returns with tool_calls array
AI->>Tool: Execute function
Tool-->>AI: Result: $552
AI->>AI: {role: "tool", content: "$552"}
AI->>User: "The total account value is $552"
Note over User,Tool: LiteLLM Flow
User->>AI: "Calculate account value"
AI->>AI: Translates to provider format
AI->>Tool: Universal execution
Tool-->>AI: Result: $552
AI->>AI: Translates back to unified format
AI->>User: "The total account value is $552"
Key Architectural Differences
Feature | Anthropic | OpenAI | LiteLLM |
---|---|---|---|
Message Structure | Content array with types | Separate content and tool_calls | Provider-dependent |
Tool Format | Flat dictionary | Nested under “function” | Adapts to provider |
Tool Results | User message with tool_result | Tool role message | Provider-dependent |
Processing Flow | Iterate content items | Check for tool_calls | Unified interface |
Philosophy | Everything is content | Separation of concerns | Universal compatibility |
Deep Dive: Understanding Anthropic’s Design Philosophy
Anthropic’s content-based approach reflects several design principles:
1. Conversational Naturalness
By treating tool results as user messages, Anthropic maintains a natural conversation flow:
# The conversation looks like:
# User: "What's the account value?"
# Assistant: [Decides to use tool]
# User: [Provides tool result]
# Assistant: "The value is $X"
This mirrors how a human assistant might ask for information and receive it.
2. Unified Content Model
Everything being content simplifies the mental model:
# All responses follow the same pattern
for content in response.content:
match content.type:
case "text": handle_text(content)
case "tool_use": handle_tool(content)
case "image": handle_image(content) # Future capability
3. Explicit State Management
The conversation state is always clear:
# You always know exactly what was said and by whom
messages = [
{"role": "user", "content": "Help me"},
{"role": "assistant", "content": [
{"type": "text", "text": "I'll check that"},
{"type": "tool_use", "name": "check_status"}
]},
{"role": "user", "content": [
{"type": "tool_result", "content": "Status: OK"}
]},
{"role": "assistant", "content": "Everything looks good!"}
]
Practical Considerations
When to Use Anthropic’s Direct Integration
Choose Anthropic’s native integration when:
- You need fine-grained control over the conversation flow
- You’re building Claude-specific features that use its unique capabilities
- You want to minimize abstraction layers for performance
- Your application deeply integrates with Claude’s content model
When to Consider Alternatives
Consider OpenAI or LiteLLM when:
- You need provider flexibility (might switch between models)
- You’re building provider-agnostic tools
- You want to use existing OpenAI-compatible tooling
- You need to support multiple providers simultaneously
Advanced Patterns with Anthropic
Pattern 1: Multi-Step Tool Chains
Anthropic’s content model makes complex tool chains intuitive:
# Claude can naturally chain tools in a single response
response.content = [
{"type": "text", "text": "I'll help you with that. First, let me look up the customer."},
{"type": "tool_use", "name": "get_customer_info", "id": "1"},
{"type": "text", "text": "Now I'll check their recent orders."},
{"type": "tool_use", "name": "get_orders", "id": "2"},
]
Pattern 2: Conditional Tool Usage
The content array allows for sophisticated decision-making:
# Process each content item and decide next steps
for content in response.content:
if content.type == "tool_use" and content.name == "check_permission":
result = await execute_tool(content)
if not result.allowed:
# Don't execute subsequent tools
break
Pattern 3: Parallel Tool Execution
When Claude requests multiple tools, you can execute them in parallel:
# Collect all tool uses
tool_uses = [c for c in response.content if c.type == "tool_use"]
# Execute in parallel
results = await asyncio.gather(*[
session.call_tool(tool.name, tool.input)
for tool in tool_uses
])
# Send all results back at once
messages.append({
"role": "user",
"content": [
{"type": "tool_result", "tool_use_id": tool.id, "content": result}
for tool, result in zip(tool_uses, results)
]
})
Getting Started
1.Clone the example repository:
```bash
git clone https://github.com/RichardHightower/mcp_article1
cd mcp_article1
```
2.Install dependencies (follow README.md for accurate details):
```bash
poetry add anthropic python-mcp-sdk
```
3.Set up your API key:
```bash
export ANTHROPIC_API_KEY=your-key-here
```
4.Run the Anthropic integration:
```bash
poetry run python src/anthropic_integration.py
```
Key Takeaways
Anthropic’s MCP integration reveals important lessons about AI tool integration:
- Design Philosophy Matters: Anthropic’s content-based approach creates cleaner conversation flows
- Simpler Can Be Better: The flat tool structure reduces complexity
- Role Semantics Are Important: Treating tool results as user messages maintains conversational coherence
- One Size Doesn’t Fit All: Different providers optimize for different use cases
Your choice between Anthropic’s elegant content model, OpenAI’s structured approach, or LiteLLM’s universal compatibility depends on your specific needs. The beauty of MCP is that your tools remain constant. Only the integration pattern changes.
References
- GitHub Repository: MCP Article Examples - Complete working code for all integrations
- Comprehensive MCP Guide: MCP: From Chaos to Harmony - Deep dive into MCP architecture
- Anthropic Documentation: Claude API Reference - Official documentation
- Related Articles:
Next Steps
Now that you understand Anthropic’s unique approach to tool integration:
- Experiment with the content-based model to build more natural conversations
- Try implementing multi-step tool chains that leverage Claude’s reasoning
- Compare performance and user experience across different providers
- Build provider-specific optimizations where they add value
The future of AI tool integration isn’t about choosing one approach. It’s about understanding each provider’s strengths and using them appropriately. With MCP as your foundation, you have the flexibility to choose the best tool for each job.
Ready to explore more integration patterns? Check out our guides on OpenAI’s function calling, DSPy’s self-optimizing approach, and LangChain’s workflow orchestration.
If you like this article, follow Rick on LinkedIn or on Medium.
About the AuthorRick Hightower brings deep enterprise experience as a former executive and distinguished engineer at a Fortune 100 company. He specialized in delivering Machine Learning and AI solutions to create intelligent customer experiences. His expertise spans both theoretical foundations and practical applications of AI technologies.
As a TensorFlow certified professional and graduate of Stanford University’s comprehensive Machine Learning Specialization, Rick combines academic rigor with real-world implementation experience. His training includes mastery of supervised learning techniques, neural networks, and advanced AI concepts. He has successfully applied these to enterprise-scale solutions.
With a deep understanding of both business and technical aspects of AI implementation, Rick bridges the gap between theoretical machine learning concepts and practical business applications. He helps organizations use AI to create tangible value.
If you like this article, follow Rick on LinkedIn or on Medium.
TweetApache Spark Training
Kafka Tutorial
Akka Consulting
Cassandra Training
AWS Cassandra Database Support
Kafka Support Pricing
Cassandra Database Support Pricing
Non-stop Cassandra
Watchdog
Advantages of using Cloudurable™
Cassandra Consulting
Cloudurable™| Guide to AWS Cassandra Deploy
Cloudurable™| AWS Cassandra Guidelines and Notes
Free guide to deploying Cassandra on AWS
Kafka Training
Kafka Consulting
DynamoDB Training
DynamoDB Consulting
Kinesis Training
Kinesis Consulting
Kafka Tutorial PDF
Kubernetes Security Training
Redis Consulting
Redis Training
ElasticSearch / ELK Consulting
ElasticSearch Training
InfluxDB/TICK Training TICK Consulting