About Mcp.zig
Mcp.zig is an MCP server published by muhammad-fiaz in the AI category: a Model Context Protocol (MCP) library for the Zig ecosystem.
The repository has 40 stars and 8 forks, with the last commit yesterday. Recent commit activity is the strongest signal that reported issues will get attention.
Install
git clone https://github.com/muhammad-fiaz/mcp.zigThis server has no ConduID identity, so agent calls to it are not receipted. Pin the version you install and review the source before granting it credentials.
Ask AI
Ask AI about Mcp.zig
Powered by Claude · Grounded in docs
Security checks
Static checks derived from the repository 16 hours ago. Heuristics on the source, not an audit.
- ✓README presentThe repository ships documentation.
- ✓License declaredUsable under a stated license.
- !Tests presentNo tests found. Behaviour changes may go unnoticed between versions.
- ✓Dependencies pinnedA lockfile pins dependency versions.
- !Scoped permissionsDoesn't declare a permission scope. Assume it can do anything its process can.
1 check has not been run yet.
Releases
README
MCP.zig
Documentation | API Reference | Quick Start | Contributing
What is MCP?
Model Context Protocol (MCP) is an open standard for connecting AI applications to external systems. It defines a common wire format and vocabulary for the tools, resources, and prompts an application exposes.
Why mcp.zig?
The Model Context Protocol (MCP) is an open standard by Anthropic for connecting AI applications to external systems. While MCP has official SDKs for TypeScript, Python, and other languages, Zig currently lacks proper MCP support.
mcp.zig is a native Zig implementation of MCP revision 2026-07-28, with
both a server and a client, so that Zig programs can:
- Serve tools, resources, resource templates, and prompts to any MCP client
- Act as an MCP client over STDIO or Streamable HTTP
- Depend on a single external package (
httpx.zig) rather than a runtime
Features
- Server framework - Expose tools, resource templates, and prompts over STDIO or HTTP
- Client framework - Connect over STDIO or HTTP and declare elicitation, sampling, roots, and extension capabilities
- MCP 2026-07-28 - Stateless per-request model:
server/discoveras the entry point,_metaon every request,resultTypeon every result, strictly advertised active revisions, noinitializehandshake - MCP Apps extension - Full
io.modelcontextprotocol/uisupport: serve interactive HTML interfaces with MIMEtext/html;profile=mcp-app(mcp.MIME_TYPE_MCP_APP), link tools via_meta.ui.resourceUri(ToolBuilder.ui), and return UI results (uiResult) rendered in host viewports (Claude Desktop, Zed, Goose, etc.) - MRTR - Multi Round-Trip Requests: a handler returning
error.InputRequiredyields aninput_requiredresult that the client resolves and retries under a new request id - Tasks extension - Opt-in
io.modelcontextprotocol/taskssupport: atools/callaugmented with_meta["io.modelcontextprotocol/task"]returnsresultType: "task", results carry_meta["io.modelcontextprotocol/relatedTask"], and the server pushesnotifications/tasks;tasks/get,tasks/update, andtasks/cancelare available on both sides - OAuth 2.0 / 2.1 & Authorization - Bearer tokens,
WWW-Authenticatechallenge parsing (RFC 6750), Protected Resource Metadata discovery (RFC 9728), Authorization Server Metadata discovery (RFC 8414), and Client Credentials grant (requestClientCredentialsToken) - Cooperative Cancellation - Thread-safe, atomic
CancellationToken(check(),isCancelled(),cancel()) for long-running operations and handlers - Content types - Text, image, audio, resource-link, and structured tool results
- Transports - STDIO and Streamable HTTP, built on httpx.zig with full JSON and SSE (
text/event-stream) support - Middleware - Optional rate limiting, request validation, batch requests, and logging around tool handlers
- Tests - Unit tests for the protocol layer, transports, server, client, and cross-target CI
Documentation
Full documentation is available at muhammad-fiaz.github.io/mcp.zig
For the official MCP specification and resources, visit:
Related Zig Projects
- For Env.zig (.env parsing), check out env.zig.
- For TUI support, check out tui.zig.
- For ZON file format support, check out zon.zig.
- For Spinners/loading/progress bar support, check out loaders.zig.
- For Terminal colors & text styles support, check out tint.zig.
- For MCP support, check out mcp.zig.
- For API framework support, check out api.zig.
- For Web framework support, check out zix.
- For archive/compression support, check out archive.zig.
- For compression file format support, check out zigx.
- For CUDA support, check out cuda.zig.
- For Simplified build.zig config support, check out buildx.zig.
- For SQLite (zig-native implementation) support, check out sqlite.zig.
- For File downloading support, check out downloader.zig.
- For update checker/auto-updater support, check out updater.zig.
- For Numerical computing support, check out num.zig.
- For Logging support, check out logly.zig.
- For Data validation and serialization support, check out zigantic.
- For UUID support, check out uuid.zig.
- For Key-Value database support, check out zkv.zig.
- For Terminal color & text styles support, check out hint.zig.
- For Brotli compression support, check out brotli.zig.
- For Zstd compression support, check out zstd.zig.
- For Tree-Sitter support, check out tree-sitter.zig.
- For HTTP client and server support, check out httpx.zig.
Quick Start
Installation
mcp.zig requires Zig 0.17.0 or later (minimum_zig_version in build.zig.zon).
Add the package to your project using Zig's package manager:
Stable Release (v0.0.9)
zig fetch --save https://github.com/muhammad-fiaz/mcp.zig/archive/refs/tags/0.0.9.tar.gz
Nightly / Development (main branch)
zig fetch --save git+https://github.com/muhammad-fiaz/mcp.zig.git
In build.zig.zon
zig fetch --save automatically populates your build.zig.zon with the dependency and its content hash:
.{
.name = .my_project,
.version = "0.1.0",
.fingerprint = 0x1234567890abcdef,
.dependencies = .{
// Stable release:
.mcp = .{
.url = "https://github.com/muhammad-fiaz/mcp.zig/archive/refs/tags/0.0.9.tar.gz",
.hash = "...", // generated automatically by zig fetch --save
},
// Or nightly development:
// .mcp = .{
// .url = "git+https://github.com/muhammad-fiaz/mcp.zig.git",
// .hash = "...",
// },
},
.paths = .{ "" },
}
In build.zig
Wire the module into your executable or library:
const mcp_dep = b.dependency("mcp", .{
.target = target,
.optimize = optimize,
});
exe.root_module.addImport("mcp", mcp_dep.module("mcp"));
Build your project with zig build:
zig build # build your project
Creating a Server
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 {
// Create server
var server: mcp.Server = .init(allocator, .{
.name = "my-server",
.version = "1.0.0",
});
defer server.deinit();
// Add a tool
try server.addTool(.{
.name = "greet",
.description = "Greet a user",
.handler = greetHandler,
});
// Run with STDIO transport
try server.run(io, allocator, .stdio);
// Or serve Streamable HTTP on POST /mcp:
// try server.run(io, allocator, .{ .http = .{ .host = "127.0.0.1", .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 message = try std.fmt.allocPrint(allocator, "Hello, {s}!", .{name});
return mcp.tools.textResult(allocator, message);
}
Creating a Client
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 {
var client: mcp.Client = .init(io, allocator, .{
.name = "my-client",
.version = "1.0.0",
});
defer client.deinit();
// Declare client capabilities before the first request
client.enableSampling(); // Deprecated in 2026-07-28, still supported
client.enableRoots(); // Deprecated in 2026-07-28, presence-only
client.enableElicitation(); // Form and URL elicitation
try client.enableTasks();
// Connect to a server
try client.connectStdio("my-server-binary", &.{});
// server/discover is the entry point in 2026-07-28; there is no
// initialize handshake and no session id
try client.discover();
try client.listTools();
}
Request helpers such as discover, listTools, callTool, and readResource
return !void: they write the request to the transport and do not hand back
a request id. Results arrive as separate messages.
handleMessage(payload)takes one raw JSON-RPC message, resolves the matching pending request, and stores its final result.takeResult(id)returns the owned raw JSON of that result, which the caller mustfree, ornullwhile the request is still outstanding.
callToolResolved, readResourceResolved, and getPromptResolved are the
request helpers that return the id (!i64), because they drive MRTR rounds
automatically. callToolAsTask(name, arguments, ttlMs) asks the server to run a
tool as a task; like callTool it returns !void.
const Resolver = struct {
fn resolve(
_: *anyopaque,
allocator: std.mem.Allocator,
key: []const u8,
_: std.json.Value,
) anyerror!std.json.Value {
var answer: std.json.ObjectMap = .empty;
try answer.put(allocator, key, .{ .string = "accept" });
return .{ .object = answer };
}
};
fn call(io: std.Io, allocator: std.mem.Allocator) !void {
var client: mcp.Client = .init(io, allocator, .{ .name = "my-client", .version = "1.0.0" });
defer client.deinit();
try client.connectStdio("my-server-binary", &.{});
const id = try client.callToolResolved("greet", null, .{
.ctx = undefined,
.resolve = Resolver.resolve,
});
// Pump every message the transport delivers
const t = client.transport orelse return error.NotConnected;
if (try t.receive(io, allocator)) |message| {
defer allocator.free(message);
try client.handleMessage(message);
}
if (client.takeResult(id)) |raw| {
defer allocator.free(raw);
// raw is the JSON text of the result
}
}
Examples
The examples/ directory contains fifteen runnable programs, each wired to its own
build step:
| Example | Description | Run step |
|---|---|---|
| simple_server.zig | Minimal server: two tools, one resource, one prompt | run-server |
| simple_client.zig | Connect, discover, list tools/resources/prompts | run-client |
| full_client.zig | Client and server over a loopback transport, asserting every response | run-full-client |
| weather_server.zig | Multi-tool weather server with a resource template | run-weather |
| calculator_server.zig | Arithmetic tools with structured output schemas | run-calc |
| advanced_server.zig | JSON Schema 2020-12, icons, annotations, templates, tasks extension | run-advanced |
| filesystem_server.zig | Dynamic filesystem resources and URI templates | run-filesystem |
| notes_server.zig | Stateful in-memory notes with list-change notifications | run-notes |
| http_server.zig | HTTP transport server on 127.0.0.1:8080 |
run-http |
| middleware_server.zig | Wrapping tool handlers in middleware | run-middleware |
| batch_client.zig | Sending several requests as one JSON-RPC batch | run-batch |
| rate_limiter_example.zig | Rate limiting tool calls | run-rate-limiter |
| health_check_example.zig | The built-in health/check method |
run-health-check |
| shutdown_example.zig | Graceful shutdown after the current request | run-shutdown |
| validator_example.zig | Validating tool arguments against a schema | run-validator |
Build every example without running it, or run one:
zig build run-all-examples
zig build run-server
zig build run-weather
zig build run-calc
zig build run-advanced
Architecture
src/
├── mcp.zig # Module entry point and re-exports
├── version.zig # Library version
├── report.zig # Error reporting (mcp.reportError, mcp.ISSUES_URL)
├── protocol/
│ ├── protocol.zig # Methods, _meta keys, error codes, HTTP headers
│ ├── types.zig # Payload and capability types
│ ├── jsonrpc.zig # JSON-RPC 2.0 codec
│ ├── schema.zig # JSON Schema 2020-12 builders
│ └── param_headers.zig # x-mcp-header parameter annotations
├── transport/
│ └── transport.zig # Transport vtable, STDIO and HTTP (httpx.zig)
├── server/
│ ├── server.zig # Server implementation
│ ├── http_rules.zig # Streamable HTTP header and origin validation
│ ├── tools.zig # Tool primitive and ToolBuilder
│ ├── resources.zig # Resource primitive and templates
│ └── prompts.zig # Prompt primitive
├── client/
│ ├── client.zig # Client implementation
│ ├── elicitation.zig # Elicitation support
│ ├── roots.zig # Roots support (deprecated)
│ └── sampling.zig # Sampling support (deprecated)
└── utils/
├── mod.zig # Utility module index
├── logging.zig # Logging helpers
├── middleware.zig # Rate limiting, batching, validation
└── progress.zig # Progress reporting
Server Features
Tools
Tools are functions that an AI application can invoke. A handler receives
(?*anyopaque, std.Io, std.mem.Allocator, ?std.json.Value) and returns
mcp.tools.ToolError!mcp.tools.ToolResult:
try server.addTool(.{
.name = "search_files",
.description = "Search for files matching a pattern",
.handler = searchHandler,
});
mcp.ToolBuilder.init(name) takes one argument and .build() is not
fallible, so a builder chain never needs try on build():
var builder: mcp.ToolBuilder = .init("search_files");
const tool = builder
.description("Search for files matching a pattern")
.inputSchema(schema)
.readOnly()
.handler(searchHandler)
.build();
try server.addTool(tool);
inputSchema and outputSchema are complete JSON Schema 2020-12 documents.
Keywords the typed schema builder cannot express are supplied through
InputSchema.fromJson(value) or ToolBuilder.inputSchemaFromRaw(value); the
server emits that document verbatim before overlaying the typed fields.
Resources
Resources expose read-only data. A handler receives
(?*anyopaque, std.Io, std.mem.Allocator, []const u8) and returns
mcp.resources.ResourceError!mcp.resources.ResourceContent:
try server.addResource(.{
.uri = "file:///docs/readme.md",
.name = "README",
.mimeType = "text/markdown",
.handler = readFileHandler,
});
Parameterised URIs use addResourceTemplate with a uriTemplate.
Prompts
Prompts are reusable message templates. A handler receives
(?*anyopaque, std.Io, std.mem.Allocator, ?std.json.Value) and returns
mcp.prompts.PromptError![]const mcp.prompts.PromptMessage:
try server.addPrompt(.{
.name = "summarize",
.description = "Summarize a document",
.arguments = &.{
.{ .name = "document", .required = true },
},
.handler = summarizeHandler,
});
Tasks
The tasks extension (io.modelcontextprotocol/tasks) is opt-in on both sides.
Server.enableTasks() declares it, and a tool opts into task augmentation with
ToolBuilder.taskSupport("optional" | "required" | "forbidden"):
var builder: mcp.ToolBuilder = .init("reindex");
const tool = builder
.description("Rebuild the search index")
.taskSupport("optional")
.handler(reindexHandler)
.build();
try server.enableTasks();
try server.addTool(tool);
A tools/call carrying _meta["io.modelcontextprotocol/task"] = {"ttl": <ms>}
then answers with resultType: "task" and a task snapshot, and repeats the task
id under _meta["io.modelcontextprotocol/relatedTask"] = {"taskId": ...}.
Clients opt in with Client.callToolAsTask(name, arguments, ttlMs) and observe
the outcome by polling getTask, answering blocked rounds with updateTask, or
cancelling with cancelTask.
Servers that manage task lifetime directly can storeTask, updateTask with a
Server.TaskUpdate, getTask, and removeTask; notifyTask pushes a single
notifications/tasks snapshot, and updateTask pushes it automatically.
notifications/tasks reaches only the subscriptions whose
types.SubscriptionFilter.taskIds list the task. tasks/* is refused with
-32021 unless the client also declares the extension.
Client Features
Roots
Advertise the filesystem boundaries a server may work within. Deprecated in
2026-07-28 but still supported; the capability is presence-only, there is no
listChanged flag, and the server ignores
notifications/roots/list_changed:
client.enableRoots();
try client.addRoot("file:///projects", "Projects");
Sampling
Allow a server to request an LLM completion, in practice through an
input_required round. Also deprecated in 2026-07-28 but supported:
client.enableSampling();
// Or, with optional context and tools support:
client.enableSamplingAdvanced(true, true);
Elicitation
client.enableElicitation(); // Form and URL elicitation
client.enableElicitationForm();
client.enableElicitationUrl();
Testing
Run the unit tests:
zig build test
zig build test --summary all
Compile the tests and every example for another target without executing them, which works for targets that cannot run on the host:
zig build test-compile -Dtarget=x86_64-linux
zig build test-compile -Dtarget=x86_64-windows
zig build test-compile -Dtarget=x86_64-macos
CI runs these three commands on Linux, Windows, and macOS: zig build test,
zig build run-all-examples, and zig fmt --check src examples build.zig.
zig fmt --check src examples build.zig should also pass locally before you
open a pull request.
Generating Documentation
Generate native, interactive HTML API documentation for the library:
zig build docs
The generated files (including index.html, main.js, and main.wasm) will be located in the zig-out/docs/ directory. You can view them using any standard web browser.
Protocol Version
This library implements MCP protocol version 2026-07-28 (protocol.VERSION)
and speaks that revision everywhere. There is no initialize handshake, no
ping, no logging/setLevel, no resources/subscribe or resources/unsubscribe,
and no Mcp-Session-Id: server/discover is the entry point and every request is
independent. Each request carries _meta with
io.modelcontextprotocol/protocolVersion,
io.modelcontextprotocol/clientInfo, and
io.modelcontextprotocol/clientCapabilities; each result carries a resultType
of "complete", "input_required", or "task". Cacheable results may add
ttlMs and cacheScope.
| Version | Status |
|---|---|
| 2026-07-28 | Supported (latest, default) |
| 2025-11-25 | Unsupported / Deprecated (older session-based revision; methods removed in 2026-07-28) |
| 2025-06-18 | Unsupported / Deprecated (older session-based revision; methods removed in 2026-07-28) |
| 2025-03-26 | Unsupported / Deprecated (older session-based revision; methods removed in 2026-07-28) |
| 2024-11-05 | Unsupported / Deprecated (initial specification; methods removed in 2026-07-28) |
protocol.PROTOCOL_VERSION (2026-07-28) is the only revision this library actively speaks and serves. Earlier
revisions (2025-11-25, 2025-06-18, 2025-03-26, 2024-11-05) established a stateful session with an initialize
handshake, which this revision removed in favor of the stateless per-request model. There is no legacy fallback:
protocol.SUPPORTED_VERSIONS contains only ["2026-07-28"], server/discover advertises only ["2026-07-28"],
and requests specifying older revisions in MCP-Protocol-Version or body _meta are rejected with ErrorCode.UNSUPPORTED_PROTOCOL_VERSION (-32022).
Methods removed by 2026-07-28 and deliberately not implemented:
initialize, notifications/initialized, ping, logging/setLevel,
notifications/roots/list_changed, resources/subscribe,
resources/unsubscribe, tasks/result, and tasks/list. Resource
subscriptions use subscriptions/listen, which the server ends with
Server.endSubscription.
Deprecated but supported features
Roots, Sampling, and Logging are deprecated in 2026-07-28 but remain in the
specification, and mcp.zig implements them:
- Roots and Sampling are reachable through Multi Round-Trip Requests:
the MRTR schema still defines
ListRootsRequestandCreateMessageRequestas validinputRequestsentries, and a handler that returnserror.InputRequiredturns into such a request. - Logging is per-request. A client opts in with
_meta.io.modelcontextprotocol/logLevel, andServer.sendLogMessageemitsnotifications/messageonly for requests that supplied the field.logging/setLevelis not implemented. - Roots is presence-only (
roots?: {}). There is nolistChangedflag and nonotifyRootsChangedon the client; the server ignoresnotifications/roots/list_changed.
Prefer tool parameters, resource URIs, and stderr for new work.
Contributing
Bug reports and pull requests are welcome. See CONTRIBUTING.md and the Contributing guide. For security vulnerabilities, see SECURITY.md.
Support
If you find this project helpful, consider supporting its development:
License
This project is licensed under the MIT License. See LICENSE for details.
Resources
README fetched from the repository 16 hours ago. The original is authoritative.