AiCore logo

Lesson 2 — Tools, Actions and MCP

Module 5, Unit 3 | Lesson 2 of 3

By the end of this lesson, you will be able to:

  • Explain how function calling works and write a valid tool schema (K9, S27)
  • Implement a Python function that an LLM can call as a tool (K9)
  • Describe what the Model Context Protocol (MCP) is and why it matters for building interoperable AI systems (K9, K25)

From asking to doing

In the previous lesson, the LLM answered a question. In this lesson, it takes action.

Tool calling is the mechanism that transforms an LLM from a text generator into a system that can do things — look up a database, call an internal API, run a calculation, write a file. The LLM does not execute the code; it decides when to call a function and with what parameters. Your code executes the function and hands the result back.

This is the architecture underlying almost every production AI system that does anything useful.

How function calling works

The flow has four steps:

  1. You define a tool — a JSON schema describing a function: its name, what it does, and what parameters it accepts.
  2. You send the tool definition alongside the prompt — the LLM knows it has access to this function.
  3. The LLM responds with a tool call — instead of (or in addition to) a text response, it returns a structured instruction: "call this function with these arguments."
  4. Your code runs the function and sends the result back to the LLM, which uses it to generate a final response.

The LLM never directly touches your systems. It only asks — your code decides whether and how to act.

🔑 Key term — Tool schema: A JSON object that describes a callable function to an LLM — its name, description, and parameter types. The description is the most important field: it is how the model decides when to use the tool.

A working example

from openai import OpenAI
import json
import os
from dotenv import load_dotenv

load_dotenv()
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

# The function your code will actually run
def get_order_status(order_id: str) -> dict:
    # In production this would query your database
    mock_orders = {
        "ORD-001": {"status": "shipped", "eta": "2024-01-15"},
        "ORD-002": {"status": "processing", "eta": "2024-01-17"},
    }
    return mock_orders.get(order_id, {"status": "not found"})

# The schema you give to the LLM
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_order_status",
            "description": "Retrieve the current status and estimated delivery date for a customer order. Use this when the user asks about an order.",
            "parameters": {
                "type": "object",
                "properties": {
                    "order_id": {
                        "type": "string",
                        "description": "The order ID in the format ORD-XXX"
                    }
                },
                "required": ["order_id"]
            }
        }
    }
]

# First API call — model may request a tool call
response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "What is the status of order ORD-001?"}],
    tools=tools,
    tool_choice="auto"
)

message = response.choices[0].message

# Handle the tool call if the model requested one
if message.tool_calls:
    tool_call = message.tool_calls[0]
    args = json.loads(tool_call.function.arguments)
    result = get_order_status(args["order_id"])

    # Second API call — send the function result back
    final_response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {"role": "user", "content": "What is the status of order ORD-001?"},
            message,
            {
                "role": "tool",
                "tool_call_id": tool_call.id,
                "content": json.dumps(result)
            }
        ],
        tools=tools
    )
    print(final_response.choices[0].message.content)

# If the model chose not to use a tool, it may respond directly:
if not message.tool_calls:
    print(message.content)

In production, set tool_choice='none' on the second call to prevent the model from requesting another tool call in the same turn.

Run this. Then modify get_order_status to return different data and observe what changes in the output.

What is MCP?

The Model Context Protocol (MCP) is an open standard — developed by Anthropic and adopted across the industry — that defines how AI models connect to tools, data sources and external systems in a consistent, interoperable way.

Without a standard, every AI tool integration is bespoke: a different connection pattern for every system. MCP replaces this with a common protocol. An MCP server exposes capabilities (tools, data, prompts) in a standardised format. Any MCP-compatible client — including Claude, many coding assistants and agent frameworks — can connect to it.

Why this matters for your work:

  • When your AI system needs to connect to a CRM, a document store, a calendar, or an internal database, MCP gives you a connection pattern that will work across providers — not just one.
  • The growing ecosystem of pre-built MCP servers means you can integrate many enterprise systems without writing custom connectors.
  • Understanding MCP positions you to build AI systems that are modular and maintainable rather than tightly coupled to a single provider.

🔑 Key term — Model Context Protocol (MCP): An open standard protocol that enables AI models to connect to external tools and data sources in a consistent, provider-agnostic way. Defined by Anthropic; supported by a growing range of clients and servers.

Coach Cora

Coach Cora

The tool description is where most developers go wrong. The LLM reads the description to decide whether to use the tool — not the parameter names, not the function name. Write it as if you are explaining to a capable colleague: what the tool does and specifically when to use it.

Activity — Tool calling

  1. Run the order status example above until you understand each step
  2. Write a new tool relevant to your workplace project. Ideas: look up a product, check a date, retrieve a category label, call a mock internal API
  3. Define the schema, implement the function, and build the complete two-call loop
  4. Add at least one error case: what happens if the order ID does not exist? Handle it gracefully.
  5. Commit your working code to GitHub

Add to your Commit Log: the name and purpose of the tool you built, a screenshot of the working output, and a one-paragraph note on how this pattern could be applied in your organisation.

Checklist

  • I can explain in my own words what happens in each of the four steps of a tool call
  • I have written a working tool schema with a clear description
  • My tool is implemented, handles at least one error case, and is committed to GitHub
  • My Commit Log includes a note on how this pattern applies to my organisation
  • I can explain what MCP is and describe one scenario in my organisation where an MCP server would be preferable to building a custom integration.

An LLM is given a tool called search_knowledge_base with the description: 'Searches documents.' A user asks: 'Can you find the refund policy for online orders?' The model does not use the tool. What is the most likely cause?


KSB evidence focus

  • K9 — AI and automation concepts, models and limitations. Tool calling is one of the most important architectural concepts in practical AI development. Understanding how it works — and where it can fail — is core K9 knowledge.

  • S27 — The tool you built for your workplace use case is a direct application of S27: translating a business need into a technical capability. Your Commit Log note on organisational application is your evidence.

  • K25 — MCP is a current and rapidly evolving standard. Understanding it now, before it becomes ubiquitous, is the kind of current-awareness K25 asks for.


Up next: Lesson 3 addresses what happens when your architecture scales — managing token costs, rate limits and real-time constraints.