Skip to main content
Lesson 3 of 14 · MCP for Developers: Servers and Integrations

Tools, Resources, and Prompts in MCP

Lesson 3 of 14 Updated November 2, 2026 Developer
On this page
  1. Three capabilities, three intents
  2. Tools: executable functions
  3. Resources: readable data
  4. Prompts: reusable templates
  5. Capability negotiation
  6. How many tools per server?
  7. Key takeaways

Updated November 2, 2026 — MCP Python SDK v2. This course now follows version 2 of the mcp SDK (2.3.0 as of October 2, 2026): MCPServer replaces FastMCP, transport settings move to run() or streamable_http_app(), and an application exposed under a real hostname must declare that hostname (transport_security). This is also the version required by the Mistral SDK: the mistralai[agents] extra requires mcp>=2.2.0,<3.0. Sources: What’s new in v2 · Migration Guide · Deploy & scale.

Three capabilities, three intents

An MCP server can expose three types of capabilities to its clients. They are not interchangeable: each addresses a distinct need, and confusing the three leads to servers that are difficult for the model to use. This lesson examines them one by one, before tackling the question that determines server quality: how many tools, and at what granularity.

Tools: executable functions

Tools are the most widely used capability of the protocol. A tool is a function that the model can decide to call to perform an action or retrieve information. Its definition relies on four elements: a unique, descriptive name (create_issue, get_weather), a text description telling the model when to call it, a JSON schema for input parameters with their types and constraints, and a return format—most often text or JSON.

from mcp.server.mcpserver import MCPServer

app = MCPServer("My Server")

@app.tool()
def get_weather(latitude: float, longitude: float) -> str:
    """Retrieves current weather for the given coordinates.

    Args:
        latitude: Latitude in decimal degrees (-90 to 90)
        longitude: Longitude in decimal degrees (-180 to 180)

    Returns:
        Text description of current weather
    """
    # Weather API call logic
    return f"Sunny, 22°C at ({latitude}, {longitude})"

Do not underestimate the description and typing: the model sees nothing else. It has no access to your code, cannot guess your intentions, and cannot test a call to see what happens. Vague descriptions or poorly typed parameters mechanically produce incorrect calls.

A few habits make your tools significantly more reliable. Name them by what they do—book_restaurant says everything, process_request says nothing—and describe both the action and the return content, since the model must anticipate what it will retrieve. Always annotate parameters in Python and write structured docstrings with Args and Returns: these feed the schema the model sees. Finally, return readable values rather than opaque identifiers; a tool that returns "John Smith" lets the model compose a sentence, while a tool that returns a UUID forces it to guess.

Resources: readable data

Resources expose data that the client can read without side effects. The nuance with tools comes down to one verb: a tool does something, a resource contains something. The contents of a configuration file, a knowledge base, API documentation, or the current state of a system are all natural resources.

@app.resource("config://database")
def get_db_config() -> str:
    """Current database configuration."""
    return json.dumps({
        "host": "localhost",
        "port": 5432,
        "database": "production"
    })

Each resource is identified by a URI—config://database, file:///path/to/doc—that the client can list and then read on demand. In practice, they are used less often than tools, since a simple tool returning data covers most needs. They remain useful when you want to publish static or semi-static data without execution logic behind it.

Prompts: reusable templates

MCP Prompts are parameterizable templates that the server makes available. They serve to share proven instructions for a given task, instead of letting everyone reinvent their phrasing.

@app.prompt()
def code_review(language: str, code: str) -> str:
    """Generates a code review prompt for the given language."""
    return f"""You are an expert in {language}.
Analyze the following code and identify:
1. Potential bugs
2. Performance issues
3. Readability improvements

Code to analyze:
```{language}
{code}
```"""

This usage makes the most sense in a team setting. A senior developer encodes their code review, debugging, or architecture analysis prompts into an MCP server, and the entire team accesses them from their own client with the same instruction quality.

Capability negotiation

When a client connects to a server, a negotiation phase precedes any usage:

  1. The client requests the server’s capabilities
  2. The server responds with its list of tools, resources, and prompts
  3. The client presents these capabilities to the language model
  4. The model uses the descriptions to decide what to call

This negotiation is dynamic, and that is an exploitable property: the same server can announce different capabilities depending on the connected user or call context—an administrator account seeing tools that a read-only account will never see.

How many tools per server?

The question seems trivial, yet it conditions your agent’s error rate. Keep your servers focused: three to ten well-defined tools work better than fifty generic ones. Beyond a certain volume, the model scatters its choices and ends up calling the wrong tool, or stacking unnecessary calls.

If your functional scope is broad, split it into several specialized servers—one for Git operations, one for project management, one for monitoring. The client knows how to connect to multiple servers simultaneously and activate only those required by the current task, which amounts to loading the useful context rather than the entire catalog.

One last trade-off deserves your attention: granularity. Exposing three tools to list restaurants, check availability, and then book forces the model to orchestrate a sequence, with a risk of error at every step. A single workflow tool—book_restaurant—encapsulates the complete sequence, reduces the number of round trips, and moves business logic where it is testable: in your code.

Key takeaways

  • Tools = executable actions (90% of use cases), require precise description and typing
  • Resources = readable data via URI, no side effects
  • Prompts = reusable templates, useful for teams
  • Keep your servers focused (3–10 tools maximum)
  • Prefer workflow tools over overly granular tools
  • Description quality determines model efficiency