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-goQuick 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