Function Calling & Model Context Protocol

You have been chatting with ChatGPT about the weather. It tells you it is sunny. But when you look outside, it is pouring rain.
Here is the problem: LLMs do not know anything about the real world in real-time. They are trained on data from the past. They cannot check your calendar, query your database, or call an API to get live information.
Until now.
Function calling and the Model Context Protocol (MCP) change everything. They let AI systems interact with the real world, access live data, and actually do things instead of just talking about them.
This is the difference between an AI that can tell you about weather APIs and an AI that can actually check the weather for your location right now.
What You Will Learn
- What function calling is and why it matters
- How to give AI access to external tools and APIs
- The Model Context Protocol (MCP) architecture
- Building your first MCP server
- Real-world patterns for tool integration
- When to use function calling vs. RAG
- Security and best practices
The Problem: AI Lives in a Bubble
Think of a traditional LLM like a brilliant professor who has been locked in a library since 2023. They have read everything up to that point, but they cannot:
- Check today's stock prices
- Query your database
- Send an email
- Book a flight
- Get real-time weather
- Access your company's internal docs
They can only work with what they remember from training.
Example of the limitation:
plaintextUser: What's the weather like in San Francisco right now? AI (without tools): Based on historical patterns, San Francisco typically has mild weather in December, with temperatures around 50-60°F and possible rain... AI (with tools): Let me check... [calls weather API] It's currently 58°F and partly cloudy in San Francisco.
The second response is actually useful. That is what function calling enables.
What is Function Calling?
Function calling is a way for LLMs to request that your application execute specific functions and return the results. Think of it like this:
Without function calling:
- You ask AI a question
- AI generates text based on training data
- That is it
With function calling:
- You ask AI a question
- AI realizes it needs external data
- AI says "Hey, can you run this function for me?"
- Your code executes the function
- AI uses the result to give you a real answer
It is like giving the AI a phone to call for help when it needs information it does not have.
How It Works: The Flow
Here is what happens under the hood:
-
You define available functions
javascriptconst tools = [{ name: "get_weather", description: "Get current weather for a location", parameters: { location: { type: "string", description: "City name" }, units: { type: "string", enum: ["celsius", "fahrenheit"] } } }]; -
User asks a question
plaintext"What's the weather in Tokyo?" -
AI decides it needs to call a function
json{ "function": "get_weather", "arguments": { "location": "Tokyo", "units": "celsius" } } -
Your code executes the function
javascriptconst result = await getWeather("Tokyo", "celsius"); // Returns: { temp: 15, condition: "cloudy" } -
AI uses the result to respond
plaintext"It's currently 15°C and cloudy in Tokyo."
The AI acts as the "brain" that decides when and how to use tools. Your code provides the actual tools.
Real-World Example: Building a Smart Assistant
Let's build a simple assistant that can check weather and send emails.
The Setup:
- Define available functions - Tell the AI what tools it has access to (get_weather, send_email)
- Implement the functions - Write the actual code that calls weather APIs and email services
- Create the AI loop - Let the AI decide when to call functions and handle the results
Example interaction:
User: "What's the weather in Paris and email the forecast to john@example.com"
Behind the scenes:
- AI calls
get_weather("Paris") - Gets result:
{ temp: 18, condition: "sunny" } - AI calls
send_email(to: "john@example.com", ...) - Returns: "I've checked the weather in Paris (18°C and sunny) and sent the forecast to john@example.com"
The Model Context Protocol (MCP)
Function calling is powerful, but it has a problem: every AI provider implements it differently. OpenAI has one format, Anthropic has another, Google has yet another.
Enter the Model Context Protocol (MCP) - a standardized way for AI systems to connect to external tools and data sources.
Think of MCP Like USB
Remember when every phone had a different charging cable? Then USB-C came along and standardized everything.
MCP does the same for AI tools:
Before MCP:
- Build custom integration for OpenAI
- Build custom integration for Claude
- Build custom integration for Gemini
- Repeat for every AI model
With MCP:
- Build one MCP server
- Any MCP-compatible AI can use it
- Switch AI providers without rewriting integrations
MCP Architecture
MCP has three components:
- MCP Hosts - AI applications (like Claude Desktop, IDEs)
- MCP Clients - Code that connects hosts to servers
- MCP Servers - Your tools and data sources
plaintext┌─────────────┐ │ AI Host │ (Claude, ChatGPT, etc.) │ (MCP Host) │ └──────┬──────┘ │ │ MCP Protocol │ ┌──────┴──────┐ │ MCP Client │ (Handles communication) └──────┬──────┘ │ ├─────────┬─────────┬─────────┐ │ │ │ │ ┌───┴───┐ ┌──┴───┐ ┌───┴───┐ ┌───┴────┐ │Weather│ │ Email│ │Database│ │Calendar│ │Server │ │Server│ │ Server │ │ Server │ └───────┘ └──────┘ └───────┘ └────────┘
Building Your First MCP Server
Let's build a simple MCP server that provides calculator functions.
Step 1: Install MCP SDK
bashnpm install @modelcontextprotocol/sdk
Step 2: Create the Server
typescriptimport { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; const server = new Server( { name: "calculator-server", version: "1.0.0", }, { capabilities: { tools: {}, }, } ); // Define tools server.setRequestHandler("tools/list", async () => { return { tools: [ { name: "add", description: "Add two numbers", inputSchema: { type: "object", properties: { a: { type: "number" }, b: { type: "number" } }, required: ["a", "b"] } }, { name: "multiply", description: "Multiply two numbers", inputSchema: { type: "object", properties: { a: { type: "number" }, b: { type: "number" } }, required: ["a", "b"] } } ] }; }); // Handle tool calls server.setRequestHandler("tools/call", async (request) => { const { name, arguments: args } = request.params; switch (name) { case "add": return { content: [ { type: "text", text: String(args.a + args.b) } ] }; case "multiply": return { content: [ { type: "text", text: String(args.a * args.b) } ] }; default: throw new Error(`Unknown tool: ${name}`); } }); // Start server const transport = new StdioServerTransport(); await server.connect(transport);
Step 3: Configure MCP Host
Add to your MCP client configuration (e.g., Claude Desktop config):
json{ "mcpServers": { "calculator": { "command": "node", "args": ["/path/to/calculator-server.js"] } } }
Now any MCP-compatible AI can use your calculator!
Real-World MCP Use Cases
1. Database Access
typescript// MCP server that lets AI query your database server.setRequestHandler("tools/call", async (request) => { if (request.params.name === "query_users") { const { email } = request.params.arguments; const user = await db.users.findOne({ email }); return { content: [{ type: "text", text: JSON.stringify(user) }] }; } });
AI can now:
- "Find all users who signed up last week"
- "What's the email for user ID 12345?"
- "Show me users with premium subscriptions"
2. File System Access
typescriptserver.setRequestHandler("tools/call", async (request) => { if (request.params.name === "read_file") { const { path } = request.params.arguments; const content = await fs.readFile(path, 'utf-8'); return { content: [{ type: "text", text: content }] }; } });
AI can now:
- "Read the README.md file"
- "What's in the config.json?"
- "Show me the latest log file"
3. API Integration
typescriptserver.setRequestHandler("tools/call", async (request) => { if (request.params.name === "github_issues") { const { repo } = request.params.arguments; const response = await fetch( `https://api.github.com/repos/${repo}/issues` ); const issues = await response.json(); return { content: [{ type: "text", text: JSON.stringify(issues) }] }; } });
AI can now:
- "What are the open issues in my repo?"
- "Create a new issue for the bug I just described"
- "Close issue #42"
Function Calling vs. RAG: When to Use What?
Both function calling and Retrieval-Augmented Generation (RAG) help AI access external information, but they serve different purposes.
Use Function Calling When:
You need real-time data
- Current stock prices, weather, user account info
You need to perform actions
- Send emails, book appointments, update databases
Data is structured and queryable
- SQL databases, REST APIs, system commands
You need precise, deterministic results
- Math calculations, data lookups, transactions
Use RAG When:
You have large knowledge bases
- Documentation, research papers, company wikis
Information is mostly static
- Historical data, archived content, reference material
You need semantic search
- "Find documents similar to this concept"
Context is unstructured text
- Articles, PDFs, chat logs, support tickets
Use Both When:
Building comprehensive AI assistants
- RAG for knowledge, function calling for actions
- Example: "What's our refund policy (RAG) and process this refund (function calling)"
Security and Best Practices
1. Validate Everything
Never trust AI-generated function arguments blindly:
- Validate function exists in allowed list
- Validate argument types and formats (e.g., email addresses)
- Check argument lengths and ranges
- Sanitize inputs before execution
2. Implement Rate Limiting
Prevent abuse by limiting function calls per user:
- Track calls per user per function
- Set reasonable limits (e.g., 10 calls per minute)
- Return clear error messages when limits exceeded
3. Log Everything
Comprehensive logging is essential for debugging and security:
- Log every function call with user ID, function name, arguments, timestamp
- Log successes and failures separately
- Monitor logs for suspicious patterns
4. Use Confirmation for Dangerous Actions
For destructive or sensitive operations:
- Require explicit user confirmation before executing
- Show clear description of what will happen
- Examples: delete_user, send_email, charge_card
Common Patterns and Recipes
Pattern 1: Function Chaining
AI can chain multiple function calls automatically:
User: "Find my last order and send me the tracking info"
AI orchestrates:
get_last_order(user_id)get_tracking_info(order_id)send_email(to: user.email, body: tracking_info)
Pattern 2: Conditional Logic
User: "If it's going to rain tomorrow, move my meeting indoors"
AI decides:
get_weather_forecast(tomorrow)- If rain predicted:
update_meeting(location: "indoors")
Pattern 3: Data Aggregation
User: "Summarize my team's productivity this week"
AI orchestrates:
get_team_members()- For each member:
get_tasks_completed(member, this_week) - Aggregate and summarize results
Debugging Function Calls
Enable Verbose Logging
Enable detailed logging to see:
- AI's decision-making process
- Which functions it chooses to call
- Arguments it generates
- Results returned from functions
- Final response composition
Common Issues
Issue: AI not calling functions
- Check function descriptions are clear
- Ensure parameters are well-documented
- Try adding examples in the system prompt
Issue: Wrong arguments
- Make parameter types explicit
- Add validation in your function
- Provide better descriptions
Issue: Infinite loops
- Set max iteration limit
- Track function call history
- Add timeout mechanisms
What You Have Learned
You now understand how to:
- Give AI access to real-time data and external tools
- Implement function calling in your applications
- Build MCP servers for standardized tool integration
- Choose between function calling and RAG
- Secure your function calling implementations
- Debug and optimize tool usage
Function calling transforms AI from a text generator into an intelligent agent that can actually interact with the world. Combined with good prompting and the right architecture, you can build AI systems that feel truly magical.
In the next lesson, we will cover how to take all of this to production - deployment strategies, monitoring, cost optimization, and everything you need to build reliable AI applications at scale.