Lesson 2 of 8 · 45 min · Lab included

Tools and MCP

In this lesson you will

  • Explain how MCP lists and calls tools and where its security rules apply
  • Split tools into read and write and add approval to the write class
  • Give each tool a scoped identity and grant it the minimum in Unity Catalog
  • Write a strict tool schema with a useful error path
Skip to the knowledge check

Tools turn text into actions

A model without tools can only write text. A model with tools can read a ticket, query a table or send a message. That is the moment an agent starts to carry risk. In this lesson we design tools so that a wrong decision by the model stays small.

We cover three rules: read first, approve consequences, and give every tool its own scoped identity. We do not cover how to build an MCP server from scratch. The official documentation does that well, and we link it at the end.

What MCP gives you

The Model Context Protocol (MCP) is an open protocol for exposing tools to models. A client asks a server which tools exist (tools/list) and then calls one (tools/call). Each tool has a name, a description and an input schema in JSON Schema. The model discovers and chooses tools from that metadata, which is why the description is part of your security surface, not only documentation.

The MCP specification says there SHOULD always be a human in the loop who can deny a tool call. It also says servers MUST validate all tool inputs, implement access controls, rate limit calls and sanitise outputs. It tells clients to treat tool annotations as untrusted unless they come from a trusted server.

A tool that fails should say so in a way the model can use. MCP separates protocol errors from tool execution errors. For the second kind, the result carries isError: true and a message such as "departure date must be in the future", so the model can correct itself and retry.

MCP on Databricks

Databricks describes three kinds of MCP servers: Databricks-managed servers (for example for SQL), external servers that you add, and custom servers you build, including servers that wrap a REST API through Databricks Apps. Unity Catalog permissions govern the managed and registered servers. Unity Gateway can control access, manage credentials and monitor usage, and you can restrict which tools an agent may see. Network access can be limited with IP access lists, network policies and private connectivity.

The current documentation does not give a status label for each server type. Check the release notes before you promise a client a managed server in production.

Rule 1: read first

Split your tools into two classes and treat them differently.

ClassExamplesDefault
ReadQuery a table, search documents, look up a ticketAllowed within the identity's scope
WriteCreate a ticket, send an e-mail, update a recordNeeds approval until measured quality supports less review

Start an agent with read tools only. Add a write tool when the evaluation set shows the agent reads correctly. For the first write tool, make the agent propose the action and let a person confirm it. A proposal is cheap to reject. A sent e-mail is not.

Rule 2: one scoped identity per tool

An agent that uses one powerful service account makes every tool as dangerous as the most dangerous tool. Give each tool, or each group of tools, its own service principal and grant it the minimum in Unity Catalog. In our delivery standards this is the least-privilege rule: permissions go to groups, privileged access is time-limited, and automation uses service principals, never personal accounts.

When the agent acts for a user, the call should run with that user's rights. Databricks apps can sign users in and call tools as each user, so the agent cannot read what the user cannot read. Our post on on-behalf-of access in Databricks Apps shows why one shared powerful identity is the wrong default.

sql
-- Give the agent's service principal read access to one schema, nothing more.
-- Group and schema names follow the NexOps naming convention.
GRANT USE CATALOG ON CATALOG prd_support TO `grp-uc-prd-support-gold-reader`;
GRANT USE SCHEMA  ON SCHEMA  prd_support.gold TO `grp-uc-prd-support-gold-reader`;
GRANT SELECT      ON SCHEMA  prd_support.gold TO `grp-uc-prd-support-gold-reader`;

The GRANT statements follow the Unity Catalog privilege syntax. The service principal of the agent is a member of the reader group. We cover this pattern in depth in the governance course.

Rule 3: design tools for the model

Good tool design is half of agent quality.

  • Few tools. Ten well-named tools beat forty overlapping ones. The model chooses better from a short list.
  • Narrow tools. get_order_status(order_id) is safer than run_sql(query). A free-form SQL tool lets a prompt injection ask for any table.
  • Strict schemas. Define types, enums and required fields. Reject everything else before it reaches your system.
  • Useful errors. Return what was wrong and what a valid call looks like.
  • Bounded output. Return a limited number of rows, and cut long text. Output that fills the context window pushes out the task.
  • Idempotent writes. A repeated call must not create a duplicate. Agents retry.

A tool definition

json
{
  "name": "get_order_status",
  "description": "Return the status of one order. Read only. Use when the user asks where an order is.",
  "inputSchema": {
    "type": "object",
    "properties": {
      "order_id": { "type": "string", "description": "Order number, for example SO-100234" }
    },
    "required": ["order_id"],
    "additionalProperties": false
  }
}

The shape follows the tool definition in the MCP specification. The additionalProperties: false line makes the schema strict, and the description states that the tool only reads.

Approvals

Add a human step for every action with consequences: money, external messages, deletes, access changes. Show the person the exact action and its inputs, not a summary written by the model. Log who approved what. Over time, the evaluation set and the approval logs show which actions can move to automatic. That is a decision with evidence, not a feeling.

Summary

Start with read tools. Give each tool its own scoped identity. Keep tools few, narrow and strict. Put a person in front of every action with consequences, and log the approval.

Next lesson: memory and state, the block that makes an agent useful across sessions and risky across users.

The idea in one picture

From request to tool result

  1. Model askstools/call
  2. Harness checksAllow list, schema
  3. ApprovalWrite actions only
  4. Tool runsScoped service principal
  5. Result returnsBounded, with source

Knowledge check

Answer each question. You see the reason behind every answer at once.

0 of 4 answered

  1. Question 1 of 4Why is a free-form run_sql tool risky for an agent?
  2. Question 2 of 4What does the MCP specification say about humans and tool calls?
  3. Question 3 of 4How should the agent act when it works for a specific user?
  4. Question 4 of 4What should a tool return when a call fails validation?

Hands-on lab

Goal. Write a strict tool schema and a validator that rejects bad calls with useful errors.

Where to run it

Local Python Run this lab on your own machine with Python 3.10 or newer in a virtual environment.

Python setup guide

Steps

  1. Save the get_order_status tool definition from the lesson as tool.json.
  2. Install the jsonschema package and write validate_call(args) that checks the arguments against inputSchema.
  3. Test five calls: valid, missing order_id, wrong type, extra field, and an order_id that does not match the pattern SO-<digits>.
  4. For each failure return a message that names the field and shows a valid example.

Expected result

One call passes. Four calls fail with messages that a model could use to repair the call.

Go deeper

Official documentation and recommended courses from the providers.

Read at NexOps

Articles from our own projects on the same topic.

Sources

Last verified 2026-11-11

We check every source against the official documentation. Products change, so confirm details there before you build.