Install
$ agentstack add mcp-bkataru-zig-utcp ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ● Shell / process execution Used
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
zig-utcp
Universal Tool Calling Protocol (UTCP) implementation for Zig 0.15.2+
A vendor-agnostic standard for LLM-tool integration supporting HTTP, CLI, MCP, SSE, WebSocket, and more.
Features
- Zero dependencies - Pure Zig standard library only
- Explicit error handling - Zig error unions (no exceptions)
- Comptime polymorphism - Generic transports via
comptime - 9 Transport types - HTTP, CLI, MCP, SSE, WebSocket, Text, UDP, GraphQL, gRPC
- 4 Auth methods - API Key, Basic, Bearer, OAuth2 (with token refresh)
- 2 Tool loaders - JSON, OpenAPI
- Streaming - Incremental response processing
- Post-processors - Response transformation and validation
- Memory efficient - Arena allocators for request/response lifetimes
New in v0.2.0
- CLI Tool -
utcpcommand for testing tool definitions - Middleware - Request/response interceptors for logging, metrics, auth injection
- Circuit Breaker - Prevent cascading failures with automatic recovery
- Rate Limiting - Token bucket, sliding window, and fixed window algorithms
- Response Caching - TTL-based caching with configurable max entries
- Batch Requests - Execute multiple tool calls in parallel
- Debug Mode - Verbose logging with request/response timing
- Schema Validation - Validate tool inputs against JSON Schema
- Mock Transport - Unit testing without network calls
- Retry Policies - Exponential backoff with jitter for transient failures
- Benchmarks - Performance regression testing with
zig build bench - API Docs - Generate documentation with
zig build docs
Requirements
- Zig 0.15.2 or later
Installation
Option 1: Using zig fetch (Recommended)
zig fetch --save git+https://github.com/bkataru/zig-utcp.git
To fetch a specific version:
zig fetch --save git+https://github.com/bkataru/zig-utcp.git#v0.1.0
Option 2: Manual Configuration
Add to your build.zig.zon:
.dependencies = .{
.utcp = .{
.url = "git+https://github.com/bkataru/zig-utcp.git",
.hash = "...", // Run `zig build` to get the correct hash
},
},
Option 3: Local Path Dependency
.dependencies = .{
.utcp = .{
.path = "../zig-utcp",
},
},
Configuring build.zig
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
// Fetch the utcp dependency
const utcp_dep = b.dependency("utcp", .{
.target = target,
.optimize = optimize,
});
// Get the module from the dependency
const utcp_mod = utcp_dep.module("utcp");
// Create your executable
const exe = b.addExecutable(.{
.name = "my_tool_client",
.root_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
}),
});
// Add the utcp import to your executable
exe.root_module.addImport("utcp", utcp_mod);
b.installArtifact(exe);
}
Building from Source
# Clone the repository
git clone https://github.com/bkataru/zig-utcp.git
cd zig-utcp
# Build the library
zig build
# Run tests
zig build test
# Build examples
zig build examples
# Build release version
zig build -Doptimize=ReleaseFast
Quick Start
const std = @import("std");
const utcp = @import("utcp");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// Create HTTP transport
var transport = utcp.HttpTransport.init(allocator);
defer transport.deinit();
try transport.loadEnv();
// Define a tool
const weather_tool = utcp.Tool{
.id = "weather_api",
.name = "Get Weather",
.description = "Fetch current weather for a city",
.call_template = .{
.http = .{
.method = "GET",
.url = "https://wttr.in/{city}?format=j1",
.timeout_ms = 30000,
},
},
};
// Prepare request
var inputs_obj = std.json.ObjectMap.init(allocator);
defer inputs_obj.deinit();
try inputs_obj.put("city", .{ .string = "London" });
const request = utcp.ToolCallRequest{
.tool_id = "weather_api",
.inputs = .{ .object = inputs_obj },
};
// Call the tool
const response = try transport.call(weather_tool, request, null);
std.debug.print("Response: {any}\n", .{response.output});
}
Examples
Run the included examples:
# HTTP client (calls wttr.in weather API)
zig build run-http
# CLI client
zig build run-cli
# MCP client
zig build run-mcp
# Streaming example
zig build run-streaming
# Post-processor example
zig build run-postprocessor
# OAuth2 example
zig build run-oauth2
# UTCP CLI tool
zig build run-utcp -- help
zig build run-utcp -- info
zig build run-utcp -- load tools.json
See examples/ for complete working examples.
API Reference
Core Types
| Type | Description | |------|-------------| | Tool | Tool definition with id, name, description, calltemplate | | ToolCallRequest | Request with toolid and inputs | | ToolCallResponse | Response with output and optional error | | Provider | Provider with auth configuration | | CallTemplate | Transport-specific call configuration |
Transports
| Transport | Description | |-----------|-------------| | HttpTransport | HTTP/HTTPS with OAuth2 support | | CliTransport | CLI subprocess execution | | McpTransport | MCP JSON-RPC (stdio + HTTP modes) | | SseTransport | Server-Sent Events | | WebSocketTransport | WebSocket connections | | TextTransport | Text output (plain/json/xml) | | UdpTransport | UDP datagrams | | GraphqlTransport | GraphQL over HTTP | | GrpcTransport | gRPC-Web compatible |
Loaders
| Loader | Description | |--------|-------------| | loadToolsFromJson | Load tools from JSON | | convertFromString | Convert OpenAPI spec to UTCP tools |
Streaming
const stream = utcp.fromBytes(allocator, data);
defer stream.deinit();
while (stream.next()) |chunk| {
// Process chunk
if (chunk.is_final) break;
}
Post-processors
var chain = utcp.PostProcessorChain.init(allocator);
defer chain.deinit();
try chain.addFn("trim", utcp.trimProcessor);
try chain.addFn("log", utcp.logProcessor);
try chain.process(&response);
Middleware
var chain = utcp.MiddlewareChain.init(allocator);
defer chain.deinit();
// Add logging middleware
try chain.add(.{
.name = "logger",
.on_request = logRequest,
.on_response = logResponse,
});
// Process request through middleware
try chain.processRequest(&context);
const response = try transport.call(tool, request, provider);
try chain.processResponse(&context, &response);
Circuit Breaker
var breaker = utcp.CircuitBreaker.init(.{
.failure_threshold = 5,
.reset_timeout_ms = 30000,
});
if (breaker.canExecute()) {
const result = transport.call(tool, request, provider);
if (result) |resp| {
breaker.recordSuccess();
} else |_| {
breaker.recordFailure();
}
}
Rate Limiting
var limiter = utcp.TokenBucket.init(.{
.burst_size = 100,
.refill_rate = 10.0, // tokens per second
});
if (limiter.tryAcquire()) {
// Proceed with request
} else {
// Rate limited
}
Response Caching
var cache_inst = utcp.ResponseCache.init(allocator, .{
.max_entries = 1000,
.default_ttl_ms = 300000, // 5 minutes
});
defer cache_inst.deinit();
// Check cache
if (cache_inst.get(cache_key)) |cached| {
return cached;
}
// Make request and cache
const response = try transport.call(tool, request, provider);
try cache_inst.put(cache_key, response);
Mock Transport (Testing)
var mock = utcp.MockTransportBuilder.init(allocator)
.expectCall("weather_api", .{ .output = .{ .string = "{\"temp\":20}" } })
.expectCall("translate", .{ .output = .{ .string = "Bonjour" } })
.build();
defer mock.deinit();
const response = try mock.call(tool, request, null);
try mock.verify(); // Fails if expectations not met
Architecture
src/
├── core/ # Core types
│ ├── tool.zig # Tool, ToolCallRequest, ToolCallResponse
│ ├── provider.zig # Provider, Auth types
│ ├── errors.zig # Error types
│ ├── substitution.zig # Variable substitution
│ ├── streaming.zig # Streaming responses
│ ├── postprocessor.zig # Post-processors
│ ├── middleware.zig # Request/response interceptors
│ ├── circuit_breaker.zig # Circuit breaker pattern
│ ├── rate_limit.zig # Rate limiting algorithms
│ ├── cache.zig # Response caching
│ ├── batch.zig # Batch request execution
│ ├── validation.zig # JSON Schema validation
│ ├── debug.zig # Debug logging
│ ├── retry.zig # Retry policies
│ └── mock.zig # Mock transport for testing
├── repository/
│ └── memory.zig # InMemoryToolRepository with search
├── transports/ # Transport implementations
├── loaders/ # Tool loaders (JSON, OpenAPI)
├── cli.zig # CLI tool
└── utcp.zig # Public API
See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for detailed design.
Project Status
100% Feature Parity with Go/Rust/TypeScript implementations
| Component | Status | |-----------|--------| | HTTP Transport | ✅ Complete | | CLI Transport | ✅ Complete | | MCP Transport | ✅ Complete | | SSE Transport | ✅ Complete | | WebSocket Transport | ✅ Complete | | Text Transport | ✅ Complete | | UDP Transport | ✅ Complete | | GraphQL Transport | ✅ Complete | | gRPC-Web Transport | ✅ Complete | | All Auth Methods | ✅ Complete | | JSON/OpenAPI Loaders | ✅ Complete | | Streaming | ✅ Complete | | Post-processors | ✅ Complete | | CI/CD | ✅ Complete |
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
MIT License - see [LICENSE](LICENSE) for details.
Related Projects
Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: bkataru
- Source: bkataru/zig-utcp
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.