Build powerful tool providers that enable AI to take actions, execute commands, and interact with your systems safely
While resources provide AI with data to read, tools give AI the power to take action. Tools are the bridge between AI intelligence and real-world systems, enabling everything from file manipulation to API calls and system administration.
Tools represent a fundamental shift from passive data access to active system interaction. With great power comes great responsibility - tools must be designed with security, validation, and error handling as first-class concerns.
Security First: Tools can modify your system, call external APIs, and perform irreversible actions. Always implement proper validation, access controls, and safety checks.
Aspect | Resources | Tools |
---|---|---|
Purpose | Read data | Perform actions |
Mutability | Read-only | Can modify state |
Parameters | URI-based | Structured arguments |
Security | Lower risk | Higher risk |
Examples | Files, databases, APIs | Write files, send emails, execute commands |
<CodeExample title="Tool Structure" language="typescript" code={`interface Tool { name: string; // Unique tool identifier description: string; // What the tool does inputSchema: { type: 'object'; properties: Record<string, any>; // Parameter definitions required?: string[]; // Required parameters }; }
interface ToolResult { content: Array<{ type: 'text' | 'image' | 'resource'; text?: string; // Text output data?: string; // Base64 encoded data resource?: { uri: string }; // Resource reference }>; isError?: boolean; // Indicates if tool failed }`} highlightLines={[1, 2, 3, 11, 16, 17]} />
Understanding the tool execution lifecycle helps you build robust implementations:
tools/list
to see available toolsThe anatomy of an MCP tool definition
Let's start with simple tools to understand the basic patterns, then build toward more complex implementations.
<CodeExample title="Simple Echo Tool" language="typescript" code={`// Register the echo tool server.setRequestHandler('tools/list', async () => { return { tools: [ { name: 'echo', description: 'Echo the provided text back to the user', inputSchema: { type: 'object', properties: { text: { type: 'string', description: 'The text to echo back' } }, required: ['text'] } } ] }; });
// Handle tool execution server.setRequestHandler('tools/call', async (request) => { const { name, arguments: args } = request.params;
if (name === 'echo') { return { content: [ { type: 'text', text: `Echo: ${args.text}` } ] }; }
throw new Error(`Unknown tool: ${name}`); });`} highlightLines={[6, 7, 27, 28, 29, 30, 31, 32, 33, 34]} />
This simple example demonstrates:
text
parameter<CodeExample title="Calculator Tool" language="typescript" code={`const calculatorTool = { name: 'calculator', description: 'Perform basic mathematical calculations', inputSchema: { type: 'object', properties: { expression: { type: 'string', description: 'Mathematical expression to evaluate (e.g., "2 + 2", "10 * 5")' } }, required: ['expression'] } };
// Safe expression evaluator function evaluateExpression(expr: string): number { // Allow only numbers, basic operators, and parentheses const safePattern = /^[0-9+\-*/().\s]+$/;
if (!safePattern.test(expr)) { throw new Error('Invalid expression: only numbers and basic operators allowed'); }
try { // Use Function constructor for safer evaluation return Function('"use strict"; return (' + expr + ')')(); } catch (error) { throw new Error('Invalid mathematical expression'); } }
// Tool execution handler if (name === 'calculator') { try { const result = evaluateExpression(args.expression); return { content: [ { type: 'text', text: `${args.expression} = ${result}` } ] }; } catch (error) { return { content: [ { type: 'text', text: `Error: ${error.message}` } ], isError: true }; } }`} />
This example shows important patterns:
isError: true
indicates tool failureBest Practice: Always validate input thoroughly. Never use eval()
or similar dynamic code execution without strict input sanitization.
A basic tool that echoes input text
A tool that performs mathematical calculations
Module content not available.
API tools enable your MCP server to interact with external services, making it a powerful integration hub.
<CodeExample title="HTTP Request Tool" language="typescript" code={`const httpRequestTool = { name: 'http_request', description: 'Make HTTP requests to external APIs', inputSchema: { type: 'object', properties: { url: { type: 'string', description: 'The URL to request' }, method: { type: 'string', enum: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'], default: 'GET', description: 'HTTP method to use' }, headers: { type: 'object', description: 'HTTP headers to include', additionalProperties: { type: 'string' } }, body: { type: 'string', description: 'Request body (for POST, PUT, PATCH)' }, timeout: { type: 'number', default: 30000, description: 'Request timeout in milliseconds' } }, required: ['url'] } };
// HTTP request handler with safety checks async function executeHttpRequest(args: any) { const { url, method = 'GET', headers = {}, body, timeout = 30000 } = args;
// Security: Validate URL try { const urlObj = new URL(url);
// Block localhost and private IPs for security
if (urlObj.hostname === 'localhost' ||
urlObj.hostname.startsWith('127.') ||
urlObj.hostname.startsWith('192.168.') ||
urlObj.hostname.startsWith('10.')) {
throw new Error('Requests to local/private networks are not allowed');
}
} catch (error) { throw new Error(`Invalid URL: ${error.message}`); }
try { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeout);
const response = await fetch(url, {
method,
headers: {
'User-Agent': 'MCP-Server/1.0',
...headers
},
body: method !== 'GET' && method !== 'HEAD' ? body : undefined,
signal: controller.signal
});
clearTimeout(timeoutId);
const responseText = await response.text();
return {
content: [
{
type: 'text',
text: \`HTTP \${method} \${url}\\n\` +
\`Status: \${response.status} \${response.statusText}\\n\` +
\`Headers: \${JSON.stringify(Object.fromEntries(response.headers.entries()), null, 2)}\\n\\n\` +
\`Response:\\n\${responseText}\`
}
]
};
} catch (error) { return { content: [ { type: 'text', text: `HTTP request failed: ${error.message}` } ], isError: true }; } }`} />
Critical Security Measures:
Consider building specialized tools for common APIs:
Slack Messaging Tool:
Email Tool:
A tool for making HTTP requests to external APIs
As your tools become more sophisticated, you'll need advanced patterns for complex scenarios.
<CodeExample title="Long-Running Tool with Progress" language="typescript" code={`class LongRunningTool { async executeLongOperation(args: any, progressCallback?: (progress: number, message: string) => void) { const totalSteps = 100;
for (let step = 0; step < totalSteps; step++) {
// Simulate work
await new Promise(resolve => setTimeout(resolve, 100));
// Report progress
if (progressCallback) {
progressCallback((step + 1) / totalSteps * 100, \`Processing step \${step + 1}\`);
}
// Check for cancellation
if (this.shouldCancel) {
throw new Error('Operation cancelled');
}
}
return {
content: [
{
type: 'text',
text: 'Long operation completed successfully!'
}
]
};
} }`} />
<CodeExample title="Conditional Tool Availability" language="typescript" code={`class ConditionalToolProvider { constructor(private config: { adminMode: boolean; apiKey?: string }) {}
getAvailableTools() { const tools = [ // Always available { name: 'status', description: 'Get system status', inputSchema: { type: 'object', properties: {} } } ];
// Admin-only tools
if (this.config.adminMode) {
tools.push({
name: 'restart_service',
description: 'Restart a system service',
inputSchema: {
type: 'object',
properties: {
service: { type: 'string', description: 'Service name' }
},
required: ['service']
}
});
}
// API-dependent tools
if (this.config.apiKey) {
tools.push({
name: 'external_api_call',
description: 'Call external API',
inputSchema: {
type: 'object',
properties: {
endpoint: { type: 'string', description: 'API endpoint' }
},
required: ['endpoint']
}
});
}
return tools;
} }`} />
Advanced MCP servers can compose tools for complex workflows:
Handle long operations with progress updates
Show tools only when certain conditions are met