MCP has three primitives: tools, resources, and prompts. Tools are the one you will use first and use most. They are the reason MCP exists in a practical sense — they let AI agents actually do things.

A tool is a function that an MCP server exposes and that an AI agent can call. Search a database. Create a GitHub issue. Send a Slack message. Resize an image. Every action an agent takes through MCP goes through a tool call.

The core idea

An MCP tool is a named function with a defined set of inputs, a description the model reads to decide when to use it, and a return value. The server tells the client what tools it has. The model picks which tool fits the conversation. The client executes the call and feeds the result back to the model.

This is different from resources, which are read-only data the agent can browse, and prompts, which are pre-built templates a user triggers. Tools are the “do” primitive. They run logic. They can have side effects. They change state.

If you have used function calling with OpenAI or Claude before, the shape is familiar. MCP tools formalize that pattern into a protocol that works across any server and any client. You define the tool once on the server side. Any MCP-compatible client — Claude Desktop, VS Code, Cursor, a custom agent — can discover it and use it without extra integration work.

How tools work

A server declares its tools through a single endpoint. The client calls tools/list and gets back an array of tool definitions:

{
  "tools": [
    {
      "name": "search_files",
      "description": "Search for files matching a pattern in the project directory",
      "inputSchema": {
        "type": "object",
        "properties": {
          "pattern": {
            "type": "string",
            "description": "Glob pattern to match against file paths"
          },
          "directory": {
            "type": "string",
            "description": "Root directory to search from"
          }
        },
        "required": ["pattern"]
      }
    }
  ]
}

Each tool has a name, a description that helps the model decide when to call it, and an inputSchema that defines what arguments it accepts using JSON Schema. The description is doing real work here — a vague description means the model will misfire on when to use the tool. A precise description means fewer wasted calls.

When the model decides to use a tool, the client sends a tools/call request:

{
  "method": "tools/call",
  "params": {
    "name": "search_files",
    "arguments": {
      "pattern": "**/*.ts",
      "directory": "/src"
    }
  }
}

The server executes the function and returns the result as an array of content blocks — text, images, or embedded resources:

{
  "content": [
    {
      "type": "text",
      "text": "Found 12 files matching **/*.ts in /src:\n- /src/index.ts\n- /src/config.ts\n..."
    }
  ],
  "isError": false
}

The isError flag tells the client whether the tool ran successfully. If something goes wrong, the server returns isError: true with a description of the failure. The model can then decide how to recover — try different arguments, switch to another tool, or tell the user what happened.

Model-controlled, human-approved

One detail separates MCP tools from the other primitives: the model decides when to call them, but the human can gate whether they actually run.

Resources are low-risk. Reading a file does not change anything. Prompts are user-initiated. The person picks the prompt and runs it. Tools sit in the middle — the model wants to take an action, and depending on what that action is, you might want to ask first.

MCP clients handle this with approval flows. Claude Desktop shows a confirmation dialog before running a tool that could modify data. Claude Code lets you set permission rules — auto-approve reads, require confirmation for writes. The protocol itself does not enforce a policy, but it gives clients the metadata they need to build one.

This is why the description field matters beyond model accuracy. It is also the text a human reads when deciding whether to approve the call. “Delete all records from the users table” is a description that earns a pause. “Count rows in the users table” is not.

Annotations: metadata that shapes behavior

The MCP specification added tool annotations to give servers a way to communicate what a tool actually does beyond its description. Annotations are structured metadata. They signal to the client how to handle the tool, not instructions to the model.

Two annotations matter most:

readOnlyHint tells the client whether the tool modifies anything. A tool that fetches stock prices is read-only. A tool that places a trade is not. Clients can use this to auto-approve read-only tools while gating everything else.

destructiveHint flags tools that delete or permanently alter data. A client might show a stronger warning for destructive tools, or block them entirely in certain modes.

These are hints, not guarantees — a server can annotate a tool as read-only and still have it write to a database. But for well-behaved servers, annotations let clients make smarter decisions about trust and approval without parsing natural language descriptions.

Why tools matter

As the directory of MCP servers grows past 25,000, the practical value of tools becomes clearer. Every server in the directory is, at its core, a bundle of tools that an agent can discover and use.

The pattern this enables is composability. An agent connected to a GitHub MCP server can create issues, review pull requests, and merge branches. Connect a Slack MCP server alongside it and the same agent can post updates to a channel after merging. Add a database MCP server and it can check deployment status before deciding whether to merge at all. Each server exposes its own tools. The agent picks the right ones for the task.

This is not theoretical. Developers building with Claude Code, Cursor, and other MCP clients are already chaining tools from multiple servers in single workflows. The protocol handles discovery and execution. The model handles orchestration.

FAQ

Q: How many tools can a single MCP server expose? A: There is no hard limit in the protocol, but practical constraints exist. Every tool definition adds to the context the model processes, so servers with dozens of tools can slow down inference. Most well-designed servers expose between 5 and 20 tools, grouped around a specific capability.

Q: Can a tool return images or files, or just text? A: Tools can return multiple content types. Text is the most common, but the protocol supports image content (base64-encoded with a MIME type) and embedded resource references. A screenshot tool returns an image directly. A file search tool returns resource URIs the client reads separately.

Q: What happens if a tool call fails? A: The server sets isError: true in the response and returns content describing what went wrong. The model sees this as part of the conversation and can decide how to handle it — retry with different parameters, try a different tool, or report the failure to the user. The protocol does not retry automatically.