Skip to lesson content

BUILD / UNDERSTAND / VERIFY · LESSON 15 OF 20

Tool calling and MCP: connect a model without surrendering control

A model requests get_order_status with an order ID. That is a proposal to your application, not proof that the caller owns the order. Tool calling becomes dependable when the schema, authorization, execution, and result all have clear boundaries.

3 min reading20–40 min suggested practiceBuilds on lesson 14

What you will learn

  • Describe the full tool-call round trip.
  • Validate a read-only lookup using trusted identity.
  • Explain what MCP standardizes and what the application still enforces.
A tool request crosses a trust boundary
  1. 01Model proposalName and typed arguments
  2. 02Application gateIdentity, policy and confirmation
  3. 03Tool executionConstrained operation
  4. 04Observed resultMatched call ID and actual status

Follow the complete round trip

Your application exposes tool names, descriptions, and argument schemas. The model returns a tool request. The application validates it, checks permissions, executes approved code, and returns the result with the appropriate call identity. The model can then produce an answer or request another allowed step.

Use clear, narrow tools. “Get the status of one authorized order” is easier to validate than “Run any database query.” Preserve the tool-call/result relationship according to the provider's protocol. A result from one call must not be attached to a different request, especially when several calls run concurrently.

Keep identity out of model-controlled arguments

This offline example demonstrates an authorization boundary. The authenticated customer is supplied by trusted server state, not the model. It intentionally gives the same outward result for a missing and unauthorized order to avoid revealing another customer's record.

python
ORDERS = {
    "N-100": {"customer": "alice", "status": "dispatched"},
    "N-200": {"customer": "bob", "status": "processing"},
}

def get_order_status(arguments, authenticated_customer):
    if not isinstance(arguments, dict) or set(arguments) != {"order_id"}:
        raise ValueError("Expected only order_id")
    order_id = arguments["order_id"]
    if not isinstance(order_id, str) or len(order_id) > 40:
        raise ValueError("Invalid order ID")
    order = ORDERS.get(order_id)
    if order is None or order["customer"] != authenticated_customer:
        return {"status": "unavailable"}
    return {"order_id": order_id, "status": order["status"]}

print(get_order_status({"order_id": "N-100"}, "alice"))
print(get_order_status({"order_id": "N-200"}, "alice"))

This toy store is not an authentication implementation. A production service needs real identity verification, durable storage, auditing appropriate to the action, and permission checks close to the data access.

Place MCP in the architecture

Model Context Protocol defines a common way for AI applications to connect with external capabilities. A host application manages clients that communicate with servers. Servers can expose tools, resources, and prompts. This standard interface can reduce custom integration work across compatible systems.

MCP is not the model itself, and using it does not automatically grant a user access to every connected system. The host and server still need trustworthy identity, appropriate authorization, constrained capabilities, and careful handling of untrusted results. Tool descriptions and returned documents can contain misleading instructions; connection through a standard protocol does not make their content authoritative.

Design writes more carefully than reads

For a state-changing tool, show the user the exact proposed action when confirmation is required. Bind approval to the validated action and arguments, then check permissions again at execution. Use idempotency where supported so a retry does not create two tickets or two cancellations.

Return explicit success or failure from the execution layer. The final answer should describe the observed outcome, not the model's intention. If a timeout leaves the outcome uncertain, look up the operation status before retrying a write. Test these cases with a mock tool before connecting any real customer system.

PUT IT TO WORK

Your practice task

Run the offline lookup as Alice for both orders. Then try adding a customer field to the arguments. Sketch the request/result IDs for two simultaneous tool calls and show how you would prevent their results from being swapped.

Checkpoint: compare your reasoning

Alice can see N-100 but receives unavailable for N-200. An extra customer argument is rejected. Authentication comes from the server's trusted context; it is not something a model can choose by supplying a convincing string.

References and further reading

Use these primary references for deeper study and current API details. Examples in this lesson use fictional Northstar data.