Simple Server Example
A complete MCP server example with tools, resources, prompts, and logging.
Overview
This example shows how to:
- create an MCP server
- register tools (greet, echo)
- register a resource (info://server/about)
- register a prompt (introduce)
- run over stdio (and optionally HTTP)
Full Source Code
zig
//! Simple MCP Server Example
//!
//! This example demonstrates how to create a basic MCP server
//! with tools, resources, and prompts.
const std = @import("std");
const mcp = @import("mcp");
pub fn main(init: std.process.Init) void {
run(init.io, init.gpa) catch |err| {
mcp.reportError(err);
};
}
fn run(io: std.Io, allocator: std.mem.Allocator) !void {
// Check for updates in background
if (mcp.report.checkForUpdates(io, allocator)) |t| t.detach();
// Create server
var server: mcp.Server = .init(allocator, .{
.name = "simple-server",
.version = "1.0.0",
.title = "Simple MCP Server",
.description = "A simple example MCP server",
.instructions = "This server provides basic greeting and echo tools.",
});
defer server.deinit();
// Add a greeting tool
try server.addTool(.{
.name = "greet",
.description = "Greet a user by name",
.title = "Greeting Tool",
.annotations = .{
.readOnlyHint = true,
.idempotentHint = true,
.destructiveHint = false,
},
.handler = greetHandler,
});
// Add an echo tool
try server.addTool(.{
.name = "echo",
.description = "Echo back the input message",
.title = "Echo Tool",
.annotations = .{
.readOnlyHint = true,
.idempotentHint = true,
.destructiveHint = false,
},
.handler = echoHandler,
});
// Add a simple resource
try server.addResource(.{
.uri = "info://server/about",
.name = "About",
.description = "Information about this server",
.mimeType = "text/plain",
.handler = aboutHandler,
});
// Add a prompt
try server.addPrompt(.{
.name = "introduce",
.description = "Introduce the server capabilities",
.title = "Introduction Prompt",
.arguments = &[_]mcp.prompts.PromptArgument{
.{ .name = "style", .description = "Introduction style (formal/casual)", .required = false },
},
.handler = introduceHandler,
});
// Enable logging
server.enableLogging();
server.enableTasks();
// Run the server
try server.run(io, allocator, .stdio);
// To run with HTTP transport:
// try server.run(io, allocator, .{ .http = .{ .host = "localhost", .port = 8080 } });
}
fn greetHandler(_: ?*anyopaque, _: std.Io, allocator: std.mem.Allocator, args: ?std.json.Value) mcp.tools.ToolError!mcp.tools.ToolResult {
const name = mcp.tools.getString(args, "name") orelse "World";
const greeting = std.fmt.allocPrint(allocator, "Hello, {s}! Welcome to MCP.", .{name}) catch return mcp.tools.ToolError.OutOfMemory;
return mcp.tools.textResult(allocator, greeting) catch return mcp.tools.ToolError.OutOfMemory;
}
fn echoHandler(_: ?*anyopaque, _: std.Io, allocator: std.mem.Allocator, args: ?std.json.Value) mcp.tools.ToolError!mcp.tools.ToolResult {
const message = mcp.tools.getString(args, "message") orelse "No message provided";
// Demonstrate structured result
var obj: std.json.ObjectMap = .empty;
obj.put(allocator, "echo", .{ .string = message }) catch {};
obj.put(allocator, "timestamp", .{ .integer = 0 }) catch {};
return mcp.tools.structuredResult(allocator, .{ .object = obj }) catch return mcp.tools.ToolError.OutOfMemory;
}
fn aboutHandler(_: ?*anyopaque, _: std.Io, _: std.mem.Allocator, uri: []const u8) mcp.resources.ResourceError!mcp.resources.ResourceContent {
return .{
.uri = uri,
.mimeType = "text/plain",
.text = "Simple MCP Server v1.0.0\n\nThis is an example MCP server built with mcp.zig.",
};
}
fn introduceHandler(_: ?*anyopaque, _: std.Io, allocator: std.mem.Allocator, args: ?std.json.Value) mcp.prompts.PromptError![]const mcp.prompts.PromptMessage {
const style = mcp.prompts.getStringArg(args, "style") orelse "casual";
_ = style;
const messages = allocator.alloc(mcp.prompts.PromptMessage, 1) catch return mcp.prompts.PromptError.OutOfMemory;
messages[0] = mcp.prompts.userMessage("Please introduce this MCP server and explain what tools it provides.");
return messages;
}Manual Test Commands
Initialize over stdio (POSIX shell):
bash
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"test","version":"1.0.0"}}}' | ./zig-out/bin/example-serverCall greet tool over stdio (POSIX shell):
bash
echo '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"greet","arguments":{"name":"Alice"}}}' | ./zig-out/bin/example-serverPowerShell (Windows) STDIO test:
powershell
.\zig-out\bin\example-server.exeThen paste one JSON-RPC line and press Enter:
json
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"test","version":"1.0.0"}}}Stop the server with Ctrl+C when you are done.
To test HTTP mode, switch server.run(io, allocator, .stdio) to HTTP in the source and then run:
bash
curl -X POST http://localhost:8080 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"test","version":"1.0.0"}}}'PowerShell HTTP initialize (HTTP mode):
powershell
$body = '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"test","version":"1.0.0"}}}'
Invoke-RestMethod -Method Post -Uri http://localhost:8080 -ContentType 'application/json' -Body $bodyExpected Output Pattern
You should receive JSON-RPC responses containing:
- initialize result with server capabilities
- tools/call result with text content from the selected tool