AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
MCP verified MIT Self-run

Zig Utcp

mcp-bkataru-zig-utcp · by bkataru

Universal Tool Calling Protocol (UTCP) implementation for Zig. A vendor-agnostic standard for LLM-tool integration supporting HTTP, CLI, MCP, SSE, WebSocket, and more

No reviews yet
0 installs
29 views
0.0% view→install

Install

$ agentstack add mcp-bkataru-zig-utcp

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No 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.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/mcp-bkataru-zig-utcp)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
8mo ago

Declared compatibility

Claude CodeClaude DesktopCursorWindsurf

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

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 →
Are you the author of Zig Utcp? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

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 - utcp command 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.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.