ZAP Protocol
Language Bindings

Go

ZAP Go SDK - Idiomatic Go binding

Go Binding

The Go binding provides an idiomatic Go interface to ZAP with goroutine-safe clients and full Gateway support.

Installation

go get github.com/zap-protocol/zap-go

Quick Start

package main

import (
    "context"
    "encoding/json"
    "fmt"
    "log"

    zap "github.com/zap-protocol/zap-go"
)

func main() {
    ctx := context.Background()

    // Connect to ZAP server
    client, err := zap.Connect(ctx, "zap://localhost:9000")
    if err != nil {
        log.Fatal(err)
    }
    defer client.Close()

    // Initialize
    server, err := client.Init(ctx, &zap.ClientInfo{
        Name:    "my-agent",
        Version: "1.0.0",
    })
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Connected to: %s v%s\n", server.Name, server.Version)

    // List tools
    tools, err := client.ListTools(ctx)
    if err != nil {
        log.Fatal(err)
    }
    for _, tool := range tools {
        fmt.Printf("  %s - %s\n", tool.Name, tool.Description)
    }

    // Call a tool
    args, _ := json.Marshal(map[string]string{"path": "/etc/hosts"})
    result, err := client.CallTool(ctx, &zap.ToolCall{
        ID:   "call-1",
        Name: "read_file",
        Args: args,
    })
    if err != nil {
        log.Fatal(err)
    }

    if result.Error != "" {
        log.Printf("Error: %s", result.Error)
    } else {
        fmt.Printf("Result: %s\n", result.Content)
    }
}

Client API

Connection

// Simple connection
client, err := zap.Connect(ctx, "zap://localhost:9000")

// With options
client, err := zap.Connect(ctx, "zap://localhost:9000",
    zap.WithTimeout(30*time.Second),
    zap.WithIdentity(identity),
    zap.WithTLS(tlsConfig),
)

Tools

// List tools
tools, err := client.ListTools(ctx)

// Call tool
result, err := client.CallTool(ctx, &zap.ToolCall{
    ID:   uuid.New().String(),
    Name: "my_tool",
    Args: []byte(`{"param": "value"}`),
})

Resources

// List resources
resources, err := client.ListResources(ctx)

// Read resource
content, err := client.ReadResource(ctx, "file:///etc/hosts")

// Subscribe to updates
stream, err := client.Subscribe(ctx, "file:///var/log/app.log")
for {
    update, done, err := stream.Next(ctx)
    if err != nil {
        log.Fatal(err)
    }
    if done {
        break
    }
    fmt.Printf("Update: %s\n", update.Content)
}

Prompts

// List prompts
prompts, err := client.ListPrompts(ctx)

// Get prompt messages
messages, err := client.GetPrompt(ctx, "code_review", zap.Metadata{
    {"topic", "go"},
})

Gateway

// Create gateway
gateway := zap.NewGateway()

// Add MCP server
fsID, err := gateway.AddServer(ctx, "filesystem",
    "stdio://npx -y @modelcontextprotocol/server-filesystem /tmp",
    &zap.ServerConfig{
        Transport: zap.TransportStdio,
    })

// Add with authentication
ghID, err := gateway.AddServer(ctx, "github",
    "stdio://npx -y @modelcontextprotocol/server-github",
    &zap.ServerConfig{
        Transport: zap.TransportStdio,
        Auth: &zap.Auth{
            Bearer: os.Getenv("GITHUB_TOKEN"),
        },
        Timeout: 60000,
    })

// List all tools
tools, _ := gateway.ListTools(ctx)

// Check status
status, _ := gateway.ServerStatus(ctx, fsID)
fmt.Printf("Status: %v\n", status)

// Remove server
gateway.RemoveServer(ctx, fsID)

Post-Quantum TLS

import "github.com/zap-protocol/zap-go/pq"

// Generate identity
identity, err := pq.GenerateIdentity()

// Or load from file
identity, err := pq.LoadIdentity("./identity.key")

// Connect with PQ-TLS
client, err := zap.Connect(ctx, "zap+pq://localhost:9000",
    zap.WithPQIdentity(identity),
)

DID Support

import "github.com/zap-protocol/zap-go/did"

// Create DID
d := did.New(did.MethodKey, publicKey)
fmt.Println(d) // did:key:z6Mk...

// Connect to registry
registry, err := did.ConnectRegistry(ctx, "zap://did-registry:9002")

// Resolve DID
doc, err := registry.Resolve(ctx, d)
fmt.Printf("Controller: %s\n", doc.Controller)

// Create Lux DID
luxDID, err := registry.Create(ctx, did.MethodLux, publicKey, nil)

Error Handling

result, err := client.CallTool(ctx, call)
if err != nil {
    var zapErr *zap.Error
    if errors.As(err, &zapErr) {
        switch zapErr.Type {
        case zap.ErrDisconnected:
            log.Println("Connection lost, reconnecting...")
            client.Reconnect(ctx)
        case zap.ErrTimeout:
            log.Println("Request timed out")
        default:
            log.Printf("ZAP error: %v", zapErr)
        }
    }
    return err
}

if result.Error != "" {
    log.Printf("Tool error: %s", result.Error)
}

Server Implementation

package main

import (
    "context"
    "encoding/json"

    zap "github.com/zap-protocol/zap-go"
    "github.com/zap-protocol/zap-go/server"
)

type MyServer struct{}

func (s *MyServer) Init(ctx context.Context, client *zap.ClientInfo) (*zap.ServerInfo, error) {
    return &zap.ServerInfo{
        Name:    "my-server",
        Version: "1.0.0",
        Capabilities: zap.Capabilities{
            Tools:     true,
            Resources: true,
        },
    }, nil
}

func (s *MyServer) ListTools(ctx context.Context) ([]*zap.Tool, error) {
    return []*zap.Tool{
        {
            Name:        "hello",
            Description: "Says hello",
            Schema:      []byte(`{"type":"object","properties":{"name":{"type":"string"}}}`),
        },
    }, nil
}

func (s *MyServer) CallTool(ctx context.Context, call *zap.ToolCall) (*zap.ToolResult, error) {
    switch call.Name {
    case "hello":
        var args struct{ Name string }
        json.Unmarshal(call.Args, &args)
        if args.Name == "" {
            args.Name = "World"
        }
        return &zap.ToolResult{
            ID:      call.ID,
            Content: []byte(fmt.Sprintf("Hello, %s!", args.Name)),
        }, nil
    default:
        return &zap.ToolResult{
            ID:    call.ID,
            Error: "Unknown tool",
        }, nil
    }
}

// ... other methods

func main() {
    srv := server.New(&MyServer{})
    if err := srv.ListenAndServe(":9000"); err != nil {
        log.Fatal(err)
    }
}

Concurrency

The Go client is goroutine-safe:

var wg sync.WaitGroup
for i := 0; i < 10; i++ {
    wg.Add(1)
    go func(id int) {
        defer wg.Done()
        result, err := client.CallTool(ctx, &zap.ToolCall{
            ID:   fmt.Sprintf("call-%d", id),
            Name: "my_tool",
            Args: []byte(`{}`),
        })
        if err != nil {
            log.Printf("Call %d failed: %v", id, err)
            return
        }
        log.Printf("Call %d: %s", id, result.Content)
    }(i)
}
wg.Wait()

Testing

package myapp

import (
    "testing"

    zap "github.com/zap-protocol/zap-go"
    "github.com/zap-protocol/zap-go/testing/mock"
)

func TestToolCall(t *testing.T) {
    m := mock.NewZap()
    m.On("ListTools").Return([]*zap.Tool{
        {Name: "test"},
    }, nil)

    tools, err := m.ListTools(context.Background())
    if err != nil {
        t.Fatal(err)
    }
    if len(tools) != 1 {
        t.Errorf("expected 1 tool, got %d", len(tools))
    }
}

Examples

Full examples at:

Last updated on

On this page