Skip to Content
MCP HubMCP Tools

What are MCP Tools?

MCP Tools are callable functions that MCP servers expose to the client. They allow interactions with real-world external environments, like querying databases, calling APIs, writing files, triggering workflows, and more.

Tools are how clients can perform actions and trigger side effects using MCP.

Lifecycle and request and response formats

Each tool lifecycle follows this pattern:

  1. The server registers a tool with a name, input parameters, output schema, and description.
  2. The client calls tools/list to discover available tools.
  3. The client calls tools/call with the tool name and arguments.
  4. The server calls the tool function with the provided arguments.
  5. The server runs the tool and returns a result or an error message.

Clients call tools using the tools/call method with structured arguments. Here is an example of a tool call request payload:

{
"method": "tools/call",
"params": {
"name": "write_note",
"arguments": {
"slug": "morning",
"content": "Start your day with clarity and confidence."
}
},
"id": 2
}

The name field is the name of the tool to call. The arguments field is an object containing the arguments to pass to the tool. The id field is a unique identifier for the request.

The response to a tool call has the following structure:

{
"jsonrpc": "2.0",
"id": 2,
"result": {
"status": "saved",
"slug": "morning"
}
}

If an error occurs, the response must conform to the standard JSON-RPC error format.

{
"jsonrpc": "2.0",
"id": 2,
"error": {
"code": 2050,
"message": "Invalid parameters",
"data": {
"name": "write_note",
"arguments": {
"slug": "morning",
"content": "Start your day with clarity and confidence."
}
}
}
}

Declaring a tool request in Python

To declare a tool, you annotate a function with the @tool decorator. A tool must accept and return only JSON-serializable data types such as str, int, float, bool, list, dict, and None.

The example below shows how to declare a tool using the official Python SDK for MCP servers  to place a stock trade using the Alpaca API.

In this example, we:

  • Register a tool called place_stock_order with a clear description and a JSON Schema specifying required inputs like symbol, qty, side, order_type, and time_in_force.
  • Use @list_tools to expose the tool metadata to the client. This is how agents and UI tools like Claude desktop discover available tools dynamically at runtime.
  • Implement @call_tool to handle the execution logic. When the tool is invoked by the client, the server builds an HTTP request to the Alpaca API using the provided arguments.
  • Format a confirmation message summarizing the placed order. The result is returned as a TextContent object, which is compatible with standard MCP tool responses.

We also make sure errors and missing data are handled gracefully, returning fallback messages if the API request fails.

💡
Note

The Python SDK also provides a higher-level interface called FastMCP, which simplifies tool declarations. The example above uses the low-level API, which is recommended only when you need fine-grained control over the tool lifecycle.

Error handling

MCP distinguishes between two types of errors, which must be handled in different ways.

1. Protocol-level errors

Use protocol-level errors (such as thrown exceptions or JSON-RPC error responses) only in the following cases:

  • Tool not found
  • Permission denied
  • Invalid parameter format
  • Server-side exceptions unrelated to tool functionality

2. Tool execution errors

Use structured error responses within successful JSON-RPC responses for:

  • Logic errors during tool execution
  • Invalid values
  • Failed operations
  • Any error the LLM should see and handle

This distinction matters because, while protocol-level errors aren’t seen by the LLM, tool execution errors are returned as content, allowing the model to reason about errors and retry or change its strategy.

Best practices and pitfalls to avoid

Here are some best practices to follow when writing MCP tools:

  • Keep names clear and purposeful: Agents or LLMs can use tool names in decision-making.
  • Validate all inputs: Never assume the client sends valid or safe data.
  • Write good docstrings: These become the tool descriptions in discovery and are often seen by agents or LLMs.
  • Return structured results: Use JSON objects with consistent shape and semantics.
  • Keep tools deterministic: Clients expect the same inputs to produce the same outputs unless side effects are explicit and intentional.

Avoid these common pitfalls:

  • Irreversible side effects without confirmation. Use sampling for validation if needed.
  • Unsafe commands (for example, os.system) or raw DB access.
  • Complex or deeply nested input schemas that are hard to validate.
  • Global state unless scoped per session.
  • Returning raw strings unless free-form output is the intent.

MCP Tools are often confused with MCP Resources, which are named, read-only data references addressable via URIs. Learn more about the difference in the Resources vs Tools section of the MCP Resources documentation.

Last updated on