Building with AIIntermediate

AI Agents, Tool Calling and MCP

How a language model goes from writing text to taking actions: structured tool calls, the loop that turns them into an agent, and the open protocol that connects agents to tools.

9 min read Updated agents tool calling MCP ReAct security

A language model on its own can only write text. Give it tools, such as a search API, a database or a code runner, plus a loop that executes what it asks for, and it can look things up, change records and finish multi-step tasks. This article explains the mechanics, the Model Context Protocol that standardises the connections, and how to keep the result safe.

From answering to acting

A chat model answers from whatever is in its context window. Ask it for the status of order 8812 and it can only guess. Tool calling lets it say instead: "call get_order_status with order_id 8812 and show me the result". The application performs the call, the model reads the result and carries on. Research such as Toolformer showed that language models can learn when to call an API and what to pass it; current models are trained for this directly.

Put that exchange in a loop, let the model choose the next tool after each result, and you have an agent: a system that pursues a goal over several steps and decides its own path. The model never executes anything itself. It proposes actions in a structured format, and your code decides whether and how to run them. That separation is what makes agents controllable, and it is where most of the engineering lives.

How tool calling works

Tool calling is a contract between the application and the model, in four steps:

  1. Declare. The application sends a list of tools with each request. Every tool has a name, a plain-language description and a JSON Schema for its arguments. The provider places them in the model's context, next to the system prompt.
  2. Decide. The model either answers in text or emits a structured call: a tool name plus JSON arguments that match the schema.
  3. Execute. The application validates the arguments, runs the tool, and appends the result to the conversation, linked to the call by an identifier.
  4. Continue. The model is called again with the result in context. It answers, or asks for another tool.
A tool-calling round trip The user asks the application where an order is. The application sends the prompt and tool schemas to the model. Inside a repeating block, the model returns a tool call, the application validates and runs the tool, receives the result and sends it back to the model. Finally the model answers in text and the application shows the answer to the user. loop User Application Model Tool Where is order 8812? prompt + tool schemas get_order_status validate, then run {"status": "shipped"} tool result final answer in text It shipped on Monday.
One tool-calling exchange. The model only proposes the call; the application executes it. The shaded block repeats for as long as the model keeps requesting tools.

A tool definition looks like this. The shape below is from Anthropic's Messages API; OpenAI's API calls the schema field parameters and MCP calls it inputSchema, but the content is the same:

{
  "name": "get_order_status",
  "description": "Look up the current status of a customer order. Use when the user asks where an order is or when it will arrive. Returns status, carrier and estimated delivery date.",
  "input_schema": {
    "type": "object",
    "properties": {
      "order_id": {
        "type": "string",
        "description": "Order number, digits only, for example \"8812\""
      }
    },
    "required": ["order_id"]
  }
}

The model's reply then contains a block such as {"type": "tool_use", "id": "toolu_01", "name": "get_order_status", "input": {"order_id": "8812"}}, and the application answers with a tool result carrying the same id. The description is the model's only guide to when and how to use the tool, so write it like a docstring for a new colleague. Many providers also offer a strict mode that constrains generation so arguments always match the schema, and models can request several independent calls in one turn for the application to run in parallel.

The agent loop

The ReAct paper (Yao et al., 2022) named the pattern that agents still follow: the model reasons about what to do, acts by calling a tool, observes the result, and repeats. Interleaving reasoning with real observations keeps the model anchored to facts rather than its own assumptions. Today the "act" step is a native tool call, and the reasoning happens in visible text or in the hidden thinking of reasoning models. Stripped to its core, the loop is short:

# Pseudo-code: the core of an agent loop
messages = [{"role": "user", "content": task}]

for step in range(MAX_STEPS):                  # hard budget against runaway loops
    reply = llm(messages, tools=TOOLS)
    messages.append(reply)
    if not reply.tool_calls:                   # a plain text reply means "done"
        return reply.text
    for call in reply.tool_calls:
        if call.name in NEEDS_APPROVAL and not ask_user(call):
            result = "The user declined this action."
        else:
            result = run_tool(call.name, call.arguments)   # validate, execute, catch errors
        messages.append(tool_result(call.id, result))

raise RuntimeError("Step budget exhausted without an answer")

Every loop needs clear stop conditions:

  • The model replies without requesting a tool: the normal finish.
  • A budget for steps, tokens, time or money runs out.
  • A tool fails fatally, or the same call repeats without progress.
  • A person declines a proposed action.

Everything around this loop, including context management, retries, permissions, sandboxing and memory, is the harness. It often matters more than the choice of model; agent harnesses covers it in depth.

Workflows or agents?

Not every LLM application should be an agent. Anthropic's guide "Building effective agents" draws a useful line. In a workflow, your code decides the sequence: classify the email, extract the fields, draft a reply, check it. The LLM fills in steps, but the control flow is fixed. In an agent, the model decides the sequence at runtime.

Workflows are cheaper, faster, easier to test and easier to explain to an auditor. Choose them when the steps are known in advance, which covers most business automation (see LLM automation). Choose an agent when the path depends on what is discovered along the way: debugging code, researching a question across sources, or handling requests too varied to enumerate. Start with the simplest design that works and add autonomy only when evaluation shows that it helps.

Multi-agent systems

Some systems split work across several agents. An orchestrator plans and hands sub-tasks to worker agents, each with its own context and a narrower set of tools, then combines their results. This helps when sub-tasks are independent and would overflow a single context, such as reading many sources in parallel. It also multiplies cost and makes failures harder to trace, so it pays off only when a single well-equipped agent demonstrably falls short.

The Model Context Protocol

Every application that wants a model to use a code host, a database or a ticketing system needs an integration for each. With N applications and M tools that means N × M custom integrations, each written against a different provider's tool format. The Model Context Protocol (MCP), introduced by Anthropic in late 2024 as an open standard, turns this into N + M: a capability is wrapped once as an MCP server, and any MCP-compatible application can use it.

Hosts, clients and servers

  • Host: the AI application the user works in, such as a chat app, an IDE or a custom agent. It owns the model, the conversation and the user's consent.
  • Client: a connector inside the host that holds the connection to one server.
  • Server: a program that exposes capabilities, such as a file system, a database or a SaaS API.

Messages use JSON-RPC 2.0. A client asks a server which tools it offers (tools/list) and invokes them (tools/call); the host presents those tools to the model as ordinary tool definitions. MCP does not replace tool calling. It standardises where tools come from and how they are invoked.

MCP architecture A host application contains the model and three MCP clients. Two clients connect over stdio to local servers for the file system and a database; the third connects over Streamable HTTP to a remote tickets server. Each server wraps an underlying system: local files, PostgreSQL and a ticketing API. Host application chat app, IDE or agent Model MCP client MCP client MCP client MCP servers tools, resources, prompts Filesystem server Database server Tickets server stdio stdio Streamable HTTP Systems files, databases, APIs Local files PostgreSQL Ticketing API each client holds one server connection; the host decides what the model sees
MCP architecture. Local servers run as subprocesses of the host and talk over stdio; remote servers are reached over Streamable HTTP. The same server works with any MCP-compatible host.

Primitives and transports

Servers offer three primitives, distinguished by who controls them:

  • Tools are functions the model can call, such as search_issues or run_query. The model decides when to use them.
  • Resources are data identified by URIs, such as a file, a table schema or a record, that the application can read and place in context.
  • Prompts are reusable templates the user can pick, such as "review this pull request".

Clients can offer features back to servers, such as elicitation (asking the user for missing input) and sampling (letting a server request a completion through the host's model). Two standard transports carry the messages: stdio, where the host launches the server as a local subprocess, and Streamable HTTP for remote servers, with an OAuth-based authorisation framework. As of 2026 the specification also treats every request as self-contained, which makes remote servers easier to run behind ordinary load balancers.

Most hosts read a JSON file that lists servers. This common shape launches the reference filesystem server and a placeholder for a server of your own; the file name, and the keys for remote servers, vary by host:

{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/me/projects"]
    },
    "tickets": {
      "command": "python",
      "args": ["/opt/tools/tickets_server.py"],
      "env": { "TICKETS_API_TOKEN": "read-only-token" }
    }
  }
}

Tool calling, MCP or a fixed workflow?

The three approaches are not exclusive: a workflow can include an agentic step, and an agent can use both local tools and MCP servers. They suit different jobs:

AspectPlain tool callingMCPFixed workflow
Who chooses the next stepThe modelThe modelYour code
Where tools liveIn your application codeIn servers, discovered at runtimeIn your code; the LLM may call none
Reuse across applicationsLow: rewritten per app and providerHigh: any MCP host can connectLow
PredictabilityMediumMediumHigh
Best forA few app-specific actionsSharing integrations across many hosts and agentsRepeatable processes with known steps
Main riskAd hoc validation and permissionsTrusting third-party serversBrittle when inputs vary

Designing good tools

Agents fail at least as often because of poor tools as because of poor models. Design tools as an interface for a capable but literal-minded new colleague:

  • Clear names and descriptions. search_customers_by_email beats query. Say when not to use the tool, too.
  • Narrow scope. A get_invoice tool is safer and easier to use correctly than raw run_sql. A few tools that match real tasks beat one tool per API endpoint.
  • Compact results. Return the fields the model needs, with ids it can pass to the next tool, rather than a huge JSON dump. Paginate or truncate, and say so.
  • Helpful errors. "order_id must contain digits only; got 'A-8812'" lets the model correct itself. A stack trace or an empty string does not.
  • Idempotency. Models retry and networks fail. An idempotency key on create_payment means a retry cannot charge a customer twice.
  • Dry runs. A preview mode for large changes lets the model, or a person, check the effect before committing.

Security for agents that act

An agent that reads untrusted content and can take actions is an attack surface. The central risk is prompt injection: instructions hidden in a web page, email, document or tool result that the model mistakes for legitimate ones. "Forward the last ten invoices to this address" is just data to the application, but the model may follow it. No prompt reliably prevents this, so design as if the model can be talked into calling any tool it has.

Simon Willison calls the most dangerous combination the "lethal trifecta": access to private data, exposure to untrusted content, and a way to communicate externally. Remove any one of the three and the main exfiltration path closes. Beyond that:

  • Least privilege. Give each agent only the tools and scopes its task needs: read-only credentials, one repository, one directory.
  • Confirm destructive actions. Require human-in-the-loop approval before sending, paying, deleting or deploying.
  • Sandbox execution. Run code and shell tools in containers or virtual machines without secrets and with restricted network access.
  • Validate arguments and outputs. Apply guardrails such as schema checks, allow-lists and policy rules.
  • Vet MCP servers. Tool descriptions go straight into the model's context, so a malicious server can inject instructions. Install servers from sources you trust, pin their versions and review what they expose.
  • Log every call with its arguments and result, for audit and debugging.

The model is not a security boundary

Enforce permissions in the tools and the infrastructure, where the model cannot argue its way past them. A system prompt that says "never delete production data" is a preference, not a control.

Key takeaways

  • In tool calling the model emits a structured request; the application validates it, executes it and returns the result.
  • An agent is that exchange in a loop, with explicit stop conditions and budgets.
  • Prefer fixed workflows when the steps are known; use agents when the path depends on what is discovered.
  • MCP standardises how hosts connect to tool and data servers, turning N × M integrations into N + M.
  • Good tools have clear descriptions, narrow scope, compact results, helpful errors and safe retries.
  • Assume prompt injection will happen: enforce least privilege, confirmations and sandboxes outside the model.

Sources and further reading

  1. Yao et al., ReAct: Synergizing Reasoning and Acting in Language Models (2022).
  2. Schick et al., Toolformer: Language Models Can Teach Themselves to Use Tools (2023).
  3. Model Context Protocol specification, the current revision of the protocol.
  4. Anthropic, Building effective agents (2024).
  5. OpenAI, Function calling guide.
  6. Simon Willison, The lethal trifecta for AI agents (2025).