Tools
Tools are the primary way for AI clients to interact with your MCP server. They represent actions that can be performed.
Defining a Tool
zig
try server.addTool(.{
.name = "tool_name",
.description = "What this tool does",
.handler = handlerFunction,
});Handler Functions
Tool handlers have this signature:
zig
fn handler(
allocator: std.mem.Allocator,
args: ?std.json.Value,
) mcp.tools.ToolError!mcp.tools.ToolResult;Example Handler
zig
fn greetHandler(
allocator: std.mem.Allocator,
args: ?std.json.Value,
) mcp.tools.ToolError!mcp.tools.ToolResult {
const name = mcp.tools.getStringArg(args, "name") orelse "World";
const message = try std.fmt.allocPrint(
allocator,
"Hello, {s}!",
.{name},
);
return mcp.tools.ToolResult{
.content = &.{mcp.Content.createText(message)},
};
}Input Schema
Define expected arguments using JSON Schema:
zig
var schema = mcp.schema.InputSchemaBuilder.init(allocator);
defer schema.deinit();
_ = try schema.addString("name", "The person's name", true);
_ = try schema.addNumber("age", "The person's age", false);
try server.addTool(.{
.name = "greet",
.description = "Greet a person",
.handler = greetHandler,
.input_schema = try schema.build(),
});Argument Helpers
Get String Argument
zig
const value = mcp.tools.getStringArg(args, "key");
if (value) |v| {
// Use v
}Get Number Argument
zig
const value = mcp.tools.getNumberArg(args, "key");
if (value) |v| {
// Use v (f64)
}Get Boolean Argument
zig
const value = mcp.tools.getBoolArg(args, "key");
if (value) |v| {
// Use v
}Return Values
Text Content
zig
return .{
.content = &.{mcp.Content.createText("Hello, World!")},
};Image Content
zig
return .{
.content = &.{mcp.Content.createImage(base64_data, "image/png")},
};Multiple Content Items
zig
return .{
.content = &.{
mcp.Content.createText("Result:"),
mcp.Content.createText("Item 1"),
mcp.Content.createText("Item 2"),
},
};Indicating Errors
zig
return .{
.content = &.{mcp.Content.createText("Error occurred")},
.isError = true,
};Error Handling
zig
fn handler(allocator: Allocator, args: ?json.Value) ToolError!ToolResult {
// Validation error
if (missing_required_arg) {
return error.InvalidArguments;
}
// Execution error
if (operation_failed) {
return error.ExecutionFailed;
}
// Out of memory
const data = allocator.alloc(u8, size) catch {
return error.OutOfMemory;
};
// Success
return .{ .content = &.{} };
}Tool Builder
Use the builder pattern for complex tools:
zig
var builder = mcp.tools.ToolBuilder.init(allocator, "advanced_tool");
defer builder.deinit();
const tool = builder
.description("An advanced tool with many options")
.handler(advancedHandler)
.addStringArg("input", "Input text", true)
.addNumberArg("count", "Number of iterations", false)
.addBoolArg("verbose", "Enable verbose output", false)
.build();
try server.addTool(tool);Best Practices
Do
- Provide clear, descriptive tool names
- Document all parameters in the schema
- Handle errors gracefully
- Return meaningful content
Don't
- Use side effects without documenting them
- Return empty content on success
- Ignore input validation
Complete Example
zig
const std = @import("std");
const mcp = @import("mcp");
fn calculateHandler(
allocator: std.mem.Allocator,
args: ?std.json.Value,
) mcp.tools.ToolError!mcp.tools.ToolResult {
const operation = mcp.tools.getStringArg(args, "operation") orelse {
return error.InvalidArguments;
};
const a = mcp.tools.getNumberArg(args, "a") orelse {
return error.InvalidArguments;
};
const b = mcp.tools.getNumberArg(args, "b") orelse {
return error.InvalidArguments;
};
const result: f64 = if (std.mem.eql(u8, operation, "add"))
a + b
else if (std.mem.eql(u8, operation, "subtract"))
a - b
else if (std.mem.eql(u8, operation, "multiply"))
a * b
else if (std.mem.eql(u8, operation, "divide"))
if (b != 0) a / b else return error.InvalidArguments
else
return error.InvalidArguments;
const message = try std.fmt.allocPrint(
allocator,
"Result: {d}",
.{result},
);
return .{
.content = &.{mcp.Content.createText(message)},
};
}Next Steps
- Resources Guide - Expose data to AI
- Prompts Guide - Create prompt templates
- Error Handling - Handle errors properly