Tools

The Model Context Protocol (MCP) enables language models to interact with external systems through a concept called "tools." Tools are server-defined functions that can be invoked by clients (like LLMs or user interfaces) to perform specific actions—such as calculations, data lookups, or API calls—using a standardized protocol.
In MCP, each tool is uniquely named and described, and its expected input parameters are defined using a JSON Schema. When a client wants to use a tool, it sends a request specifying the tool's name and the required arguments. The server executes the tool and returns the result in a structured format, which can include text, images, audio, or even references to additional resources.
In the future, hopefully we can even respond with UI! For more on this, check out my post The future of AI interaction: Beyond just text.
From the MCP Spec: Tools in MCP are designed to be model-controlled, meaning that the language model can discover and invoke tools automatically based on its contextual understanding and the user's prompts. However, implementations are free to expose tools through any interface pattern that suits their needs—the protocol itself does not mandate any specific user interaction model.
ServerClientServerClientJSON-RPC Request {"method": "tools/call", "params": {"name": "hello", "arguments": {}}}JSON-RPC Response {"result": {"content": [{"type": "text", "text": "Hello, world!"}]}}
For trust and safety, MCP encourages a "human-in-the-loop" approach: users should be able to see which tools are available, confirm sensitive operations, and review tool inputs and outputs. The protocol also supports dynamic discovery of available tools and notifies clients if the tool list changes.
The whole flow looks something like this:
ServerClientLLMAppUserServerClientLLMAppUseralt[User confirms][User rejects]loop[Tool call(s) until generation]Enter promptSend promptTool call requestHuman-in-the-loop confirmationForward tool callJSON-RPC tool callJSON-RPC responseTool call resultTool call resultGenerationDisplay responseRejection (skip tool call)Generation (final response)Display response
Note: The only part of this which is technically specified by MCP is the communication between the client and server. The rest of the user experience is up to however the host application wants to do it.

Example: Defining a Simple Tool

import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'

const server = new McpServer(
	{ name: 'hello-world-server', version: '1.0.0' },
	{ capabilities: { tools: {} }, instructions: 'A simple hello world server.' },
)

server.registerTool(
	// llm-facing name
	'hello',
	{
		// user-facing title
		title: 'Hello',
		// llm-facing description (clients could also display this to the user)
		description: 'Say hello',
	},
	async () => {
		return {
			content: [{ type: 'text', text: 'Hello, world!' }],
		}
	},
)
Example client request:
{
	"jsonrpc": "2.0",
	"id": 1,
	"method": "tools/call",
	"params": {
		"name": "hello",
		"arguments": {}
	}
}
Example server response:
{
	"jsonrpc": "2.0",
	"id": 1,
	"result": {
		"content": [
			{
				"type": "text",
				"text": "Hello, world!"
			}
		],
		"isError": false
	}
}
In this exercise, you'll extend your MCP server to declare support for tools and implement a simple tool that performs addition. You'll start by exposing a basic tool with a hardcoded response, then evolve it to accept parameters and return dynamic results based on user input. This will give you hands-on experience with MCP's tool registration, input validation (using Zod schemas), and result formatting.