Tool Integration & Function Calling
MCP Integration & Tool Orchestration
Model Context Protocol (MCP) represents the future of AI tool integration. Understanding how production systems orchestrate multiple tools through MCP reveals patterns for building extensible, composable AI assistants.
What is MCP?
MCP is an open protocol for connecting AI models to external tools and data sources:
MCP Architecture:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ AI Model │◄───►│ MCP Host │◄───►│ MCP Servers │
│ (Claude) │ │ (Claude Code)│ │ (Tools) │
└─────────────┘ └─────────────┘ └─────────────┘
│
┌─────┴─────┐
│ │
┌─────┴───┐ ┌─────┴───┐
│Filesystem│ │Database │
└─────────┘ └─────────┘
MCP Server Configuration
Claude Code MCP Setup
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/home/user"],
"env": {}
},
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_TOKEN": "ghp_..."
}
},
"postgres": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres"],
"env": {
"DATABASE_URL": "postgresql://..."
}
}
}
}
Server Types
Official MCP Servers (January 2026):
- filesystem: File operations
- github: Repository management
- postgres: Database queries
- sqlite: Local database
- slack: Messaging integration
- google-drive: Document access
- brave-search: Web search
- puppeteer: Browser automation
Tool Discovery Protocol
How hosts discover available tools:
// Request
{
"jsonrpc": "2.0",
"method": "tools/list",
"id": 1
}
// Response
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"tools": [
{
"name": "read_file",
"description": "Read contents of a file",
"inputSchema": {
"type": "object",
"properties": {
"path": {"type": "string"}
},
"required": ["path"]
}
},
{
"name": "write_file",
"description": "Write contents to a file",
"inputSchema": {
"type": "object",
"properties": {
"path": {"type": "string"},
"content": {"type": "string"}
},
"required": ["path", "content"]
}
}
]
}
}
Tool Orchestration Patterns
Sequential Orchestration
Tools executed in order:
Sequential Pattern:
Task: "Read config, update value, write back"
Step 1: read_file(path="/config.json")
↓ Result: {"debug": false, "port": 3000}
Step 2: Process result
↓ Modified: {"debug": true, "port": 3000}
Step 3: write_file(path="/config.json", content=modified)
↓ Result: success
Parallel Orchestration
Independent tools run simultaneously:
Parallel Pattern:
Task: "Get weather for multiple cities"
┌─────────────────────────────────────────────┐
│ Parallel Execution │
│ │
│ get_weather("NYC") ─────┐ │
│ get_weather("LA") ─────┼──► Aggregate │
│ get_weather("CHI") ─────┘ Results │
│ │
└─────────────────────────────────────────────┘
Conditional Orchestration
Tool selection based on results:
Conditional Pattern:
Task: "Find and process data"
search_database(query)
│
├── Found in DB ──► process_db_result()
│
└── Not Found ──► search_api(query)
│
└──► process_api_result()
Claude Code's Task Agent System
Claude Code orchestrates tools through specialized agents:
Agent Dispatch Pattern:
┌─────────────────┐
│ Main Agent │
│ (Orchestrator)│
└────────┬────────┘
│
┌────┴────┐
▼ ▼
┌───────┐ ┌───────┐
│Explore│ │ Plan │
│ Agent │ │ Agent │
└───────┘ └───────┘
│ │
└────┬────┘
▼
┌─────────┐
│ Task │
│ Result │
└─────────┘
Agent Type Selection
Agent Selection from Claude Code:
- general-purpose: Complex multi-step tasks
- Explore: Codebase exploration (quick/medium/thorough)
- Plan: Implementation planning
- claude-code-guide: Documentation queries
Selection criteria:
- Task complexity
- Required tools
- Autonomy level needed
Tool Chaining Patterns
Data Pipeline
Pipeline Pattern:
fetch_data()
│
▼
transform_data()
│
▼
validate_data()
│
▼
store_data()
Each step receives output from previous step
Fan-Out/Fan-In
Fan-Out/Fan-In Pattern:
┌── process_chunk_1() ──┐
│ │
split_data() ───────┼── process_chunk_2() ──┼──► merge_results()
│ │
└── process_chunk_3() ──┘
Retry with Fallback
Retry Pattern:
primary_tool()
│
├── Success ──► Continue
│
└── Failure ──► retry(max=3)
│
└── All failed ──► fallback_tool()
Error Handling in Orchestration
Tool-Level Errors
// Error Response
{
"jsonrpc": "2.0",
"id": 2,
"error": {
"code": -32602,
"message": "Invalid params: path is required"
}
}
Orchestration-Level Recovery
Error Recovery Strategy:
On tool failure:
1. Check error type
- Transient → Retry with backoff
- Permanent → Log and skip or use fallback
2. Check dependency impact
- Independent → Continue other tools
- Blocking → Halt dependent chain
3. Aggregate errors for reporting
- Partial success possible?
- User notification needed?
Permission and Security
Tool Approval System
From Claude Code's prompt:
Permission Levels:
ALLOWED (auto-approve):
- Read operations
- Search operations
- Safe bash commands
REQUIRES_APPROVAL:
- Write operations
- Delete operations
- Network requests to unknown hosts
- Bash commands with side effects
NEVER_ALLOWED:
- Credential access
- System modification
- Destructive operations
Sandboxing Patterns
Sandbox Configuration:
{
"permissions": {
"filesystem": {
"read": ["/project/**"],
"write": ["/project/**"],
"deny": ["/project/.env", "/project/secrets/**"]
},
"network": {
"allow": ["api.github.com", "registry.npmjs.org"],
"deny": ["*"]
},
"exec": {
"allow": ["npm", "node", "git"],
"deny": ["rm", "sudo"]
}
}
}
Building Custom MCP Servers
Server Structure
import { Server } from "@modelcontextprotocol/sdk/server";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio";
const server = new Server({
name: "custom-server",
version: "1.0.0"
});
// Register tool
server.setRequestHandler("tools/list", async () => ({
tools: [{
name: "custom_tool",
description: "Does something useful",
inputSchema: {
type: "object",
properties: {
input: { type: "string" }
},
required: ["input"]
}
}]
}));
// Handle tool calls
server.setRequestHandler("tools/call", async (request) => {
if (request.params.name === "custom_tool") {
const result = await doSomething(request.params.arguments);
return { content: [{ type: "text", text: result }] };
}
});
// Start server
const transport = new StdioServerTransport();
await server.connect(transport);
Best Practices
1. Minimize Tool Calls
Batching Pattern:
// Instead of:
read_file("a.ts")
read_file("b.ts")
read_file("c.ts")
// Use:
read_files(["a.ts", "b.ts", "c.ts"])
2. Provide Rich Context
{
"name": "search_codebase",
"description": "Search for code patterns. Use for finding definitions, usages, or patterns. Returns file paths with line numbers. Use Grep for content search, Glob for file names.",
"inputSchema": {
"properties": {
"pattern": {
"type": "string",
"description": "Regex pattern. Examples: 'function\\s+\\w+', 'TODO:', 'import.*from'"
}
}
}
}
3. Validate Before Execute
Validation Pattern:
Before tool execution:
1. Check required parameters present
2. Validate parameter types
3. Check permissions
4. Estimate resource usage
5. Confirm with user if high-impact
Key Insight: MCP transforms AI assistants from isolated models to connected systems. Effective tool orchestration requires understanding sequential vs parallel execution, proper error handling, and security boundaries. The best orchestration is invisible—tools combine seamlessly to solve complex problems.
Next module, we'll explore safety, guardrails, and constraints in production prompts. :::