Documentation for the Go Micro agent harness and service framework.
Overview
Go Micro is an agent harness and service framework for Go. A harness is the runtime around an agent: tools, memory, guardrails, workflows, state, discovery, and interop. Build an agent and it gets a model, memory, tools, planning, delegation, and service discovery; it is reachable over MCP and A2A. Write services and every endpoint becomes an AI-callable tool. Orchestrate the deterministic parts with durable flows. Agents, services, and flows come from the same primitives because an agent is a distributed system, and building one is building a service.
It’s built on a pluggable architecture of Go interfaces: service discovery, client/server RPC, pub/sub, plus auth, caching, and storage. Sane defaults out of the box, everything swappable.
Learn More
Start with Getting Started for install and the first local service. Then follow the first-agent on-ramp in the same order as the README: micro agent demo for the installed no-secret CLI affordance, micro agent quickcheck (or micro agent debug) for the short recovery map, micro examples for the provider-free examples map, micro zero-to-hero for the maintained lifecycle harness, examples wayfinding index for the runnable examples map, the smallest first-agent example for the fastest provider-free run, the 0→hero support reference for the full no-secret lifecycle example, No-secret first-agent transcript to run a mock-model support agent, Your First Agent to build a service-backed agent and talk to it with micro chat, Debugging your agent to use micro inspect agent <name> for runs and memory, and the 0→hero reference path to walk the full scaffold → run → chat → inspect → deploy dry-run lifecycle covered by CI.
Otherwise continue to read the docs for more information about the framework.
Go Micro is a framework for microservices development
Go Micro is a framework for microservices development.
It’s built on a powerful pluggable architecture using Go interfaces. Go Micro defines the foundations for distributed systems development which includes service discovery, client/server rpc and pubsub. Additionally Go Micro contains other primitives such as auth, caching and storage. All of this is encapsulated in a high level service interface.
1.1 - Getting Started
Go Micro has three core abstractions:
Go Micro has three core abstractions:
Abstraction
What
Constructor
Service
Capability — endpoints, data, business logic
micro.NewService("task")
Agent
Intelligence — manages services with an LLM
micro.NewAgent("task-mgr")
Flow
Orchestration — event-driven LLM triggers
micro.NewFlow("onboard")
Prerequisites
Go 1.24+ for development. The curl install below gives you the micro binary without Go, but micro run compiles your services, so you’ll want Go installed to build them.
An LLM provider key (Anthropic, OpenAI, Gemini, …) only for the AI features — micro run --prompt, micro chat, and agents. Plain services need no key. Set it before running, e.g. export ANTHROPIC_API_KEY=sk-ant-....
Before your first provider-backed agent run, check the local path with:
micro agent preflight
The preflight is read-only: it verifies Go 1.24+, the micro binary, provider-key setup, and whether the default micro run gateway port is free, without calling an LLM provider. When a check fails it prints the exact fix plus the next guide to open, so the scaffold → run → chat path stays walkable.
Install
# Binary (no Go required)curl -fsSL https://go-micro.dev/install.sh | sh
# Or with Gogo install go-micro.dev/v6/cmd/micro@latest
Quick Start: Generate from a Prompt
Prefer to start from a runnable reference? Clone the repository and run the maintained support-desk lifecycle example first:
git clone --depth 1 https://github.com/micro/go-micro.git
cd go-micro
go run ./examples/support
That example is the no-secret 0→hero path: services expose ticket/customer/notification tools, an agent handles the work, and an event-driven flow triggers the agent. See Learn by Example when you want more runnable starting points.
Describe what you need. The AI designs services, writes handlers, compiles, and starts them:
micro run --prompt "task management system"
You’ll see the design, confirm, and services + agent start:
The interactive console lets you talk to your services immediately:
> Create a project called Launch, then add a task called 'Write docs'
→ project_Project_Create({"name":"Launch"})
← {"record":{"id":"p1..."},"success":true}
→ task_Task_Create({"title":"Write docs","project_id":"p1..."})
Created project Launch and added task 'Write docs' to it.
The console discovers services from the registry and orchestrates across them via the agent. Use micro run -d for detached mode without the console, or micro chat as a standalone command.
If the agent surprises you while iterating, use the Debugging your agent guide to inspect service registration, tool calls, run history, memory, provider failures, and flow handoffs.
When you are ready to prove the whole path end to end, follow the 0→hero reference path. It is the canonical handoff from this quick start: scaffold a service, run it locally, chat with an agent, inspect durable agent/flow history, and finish with micro deploy --dry-run using the same commands exercised by make harness.
Quick Start: Write a Service
Create and run a service manually:
micro new helloworld
cd helloworld
micro run
Open http://localhost:8080 to see the dashboard, call endpoints, and chat with your service.
A service is a Go struct with methods. Doc comments and @example tags become tool descriptions for AI agents:
packagemainimport("context""go-micro.dev/v6")typeRequeststruct{Namestring`json:"name"`}typeResponsestruct{Messagestring`json:"message"`}typeSaystruct{}// Hello greets a person by name.// @example {"name": "Alice"}func(h*Say)Hello(ctxcontext.Context,req*Request,rsp*Response)error{rsp.Message="Hello "+req.Namereturnnil}funcmain(){service:=micro.NewService("greeter")service.Handle(new(Say))service.Run()}
micro run gives you:
Dashboard at http://localhost:8080
API Gateway at http://localhost:8080/api/{service}/{method}
Agent Playground at http://localhost:8080/agent
MCP Tools at http://localhost:8080/mcp/tools
Hot Reload — auto-rebuild on file changes
micro new scaffolds a reflection-based service by default — plain Go types, no code generation, so go run . works with nothing else installed. If you prefer Protocol Buffers, add --proto (this requires the protoc toolchain; the command tells you what to install).
Templates are available for common patterns. These use Protocol Buffers, so they need the protoc toolchain (protoc, protoc-gen-go, protoc-gen-micro — micro new prints the install commands if they’re missing):
micro new contacts --template crud
micro new events --template pubsub
micro new gateway --template api
Building Agents
For a complete service-backed walkthrough, start with Your First Agent. If you want to run before you write, use examples/support for the full services → agents → workflows lifecycle or examples/agent-plan-delegate for the smallest multi-agent planning/delegation path.
An Agent is an intelligent layer that manages one or more services:
packagemainimport"go-micro.dev/v6"funcmain(){agent:=micro.NewAgent("task-mgr",micro.AgentServices("task","project"),micro.AgentPrompt("You manage tasks and projects. You understand deadlines, priorities, and assignments."),micro.AgentProvider("anthropic"),micro.AgentAPIKey("sk-ant-..."),)agent.Run()}
An agent is a service — it has a proto-defined Agent.Chat RPC endpoint and registers in the registry like everything else. It:
Discovers its services from the registry
Only sees endpoints from its assigned services (scoped tools)
Maintains conversation memory in the store (persists across restarts)
Is callable via micro call, the interactive console, or any go-micro client
Use it programmatically:
resp,_:=agent.Ask(ctx,"What tasks are overdue for Alice?")fmt.Println(resp.Reply)
Or via the CLI:
micro agent list # list registered agentsmicro call task-mgr Agent.Chat '{"message": "What tasks are overdue?"}'
When multiple agents are registered, the console routes to the right agent automatically.
Event-Driven Flows
A Flow subscribes to a broker topic and triggers an LLM when events arrive. You can define flows in code or run them from the CLI.
In code:
f:=micro.NewFlow("onboard-user",micro.FlowTrigger("events.user.created"),micro.FlowPrompt("New user created: {{.Data}}. Send welcome email and create workspace."),micro.FlowProvider("anthropic"),micro.FlowAPIKey(os.Getenv("MICRO_AI_API_KEY")),)f.Register(service.Options().Registry,service.Options().Broker,service.Client())
From the CLI:
micro flow run --trigger events.user.created --prompt "New user: {{.Data}}. Send welcome email."micro flow exec --prompt "Summarize all open tickets and email the report."
The flow discovers all services as tools and lets the LLM decide which RPCs to call in response to the event.
CLI Workflow
Command
Purpose
micro run --prompt "..."
Generate services + agent, start with interactive console
micro run
Dev mode: hot reload, gateway, interactive console
micro run -d
Detached mode (no console)
micro chat
Standalone chat (when not using micro run)
micro agent list
List registered agents
micro flow run --trigger <topic>
Run an event-driven flow
micro flow exec --prompt "..."
Execute a one-shot flow
micro new myservice
Scaffold a service
micro call service endpoint '{}'
Call a service or agent
micro build
Compile production binaries
micro deploy user@server
Deploy via SSH + systemd
Next Steps
Learn by Example — runnable examples mapped to services, agents, and workflows
Go Micro is an AI-native microservices framework. Every service you build is automatically accessible to AI agents, and every service can call AI models. This page explains how the pieces fit together
Go Micro is an AI-native microservices framework. Every service you build is automatically accessible to AI agents, and every service can call AI models. This page explains how the pieces fit together.
The Stack
Your Services → write Go handlers, register with the framework
↓
Registry → automatic service discovery (mDNS, Consul, etcd)
↓
Gateways → micro api (HTTP→RPC) / micro mcp (MCP tools)
↓
ai.Tools → discovers services + executes RPCs programmatically
↓
ai.Model → calls LLMs (Anthropic, OpenAI, Gemini, Atlas Cloud, ...)
↓
agent / flow / micro chat → agent-managed, event-driven, or interactive orchestration
Every layer is optional. You can use go-micro without AI. You can use the ai package without MCP. But when you stack them, you get services that AI agents can discover and orchestrate automatically.
Layer by Layer
1. Services (your code)
Write normal Go handlers. Add doc comments for AI tool descriptions:
// CreateUser creates a new user account.// @example {"name": "Alice", "email": "alice@example.com"}func(h*Users)CreateUser(ctxcontext.Context,req*pb.CreateRequest,rsp*pb.CreateResponse)error{// your business logic}
The doc comment becomes the tool description. The @example tag gives the LLM a usage hint. No AI-specific code in your handler.
2. Registry (service discovery)
Services register automatically. The registry is the source of truth for what’s running:
service:=micro.NewService("users")service.Handle(handler.New())service.Run()// registers with the registry
Pluggable: mDNS (default, zero config), Consul, etcd, NATS.
3. MCP Gateway (services → tools)
The MCP gateway walks the registry and exposes every endpoint as a tool via the Model Context Protocol:
// One line to expose all services as AI toolsservice:=micro.NewService("myservice",mcp.WithMCP(":3001"))
Or run it standalone:
micro mcp serve # stdio for Claude Codemicro mcp serve --address :3000 # HTTP for web agents
Any MCP-compatible agent (Claude Code, ChatGPT, custom agents) can discover and call your services.
4. ai.Tools (discover + execute)
ai.Tools turns registered services into LLM-callable tools — discovery plus RPC execution in one type:
tools:=ai.NewTools(service.Registry())discovered,_:=tools.Discover()// []ai.Tool from all registered services// Wire execution into a model with one option:m:=ai.New("anthropic",ai.WithAPIKey(key),ai.WithTools(tools))
This is what powers micro chat and the agent playground. You can use it directly in your own services to build agentic workflows.
5. ai.Model (LLM providers)
The ai package provides a pluggable interface for calling LLMs:
import("go-micro.dev/v6/ai"_"go-micro.dev/v6/ai/anthropic")m:=ai.New("anthropic",ai.WithAPIKey(key))resp,_:=m.Generate(ctx,&ai.Request{Prompt:"What users are in the system?",Tools:discovered,// from ai.Tools})
Seven text providers, two image providers, one video provider. Same interface, swap with an import.
Provider
Text
Image
Video
Anthropic
yes
OpenAI
yes
yes
Google Gemini
yes
Atlas Cloud
yes
yes
yes
Groq
yes
Mistral
yes
Together AI
yes
6. micro chat (orchestration)
The CLI ties it all together — discovers services, builds the tool list, and lets you talk to your services:
ANTHROPIC_API_KEY=sk-ant-... micro chat --provider anthropic
> list all users
> send a welcome email to alice@example.com
> create an order for product-42
Multi-turn conversation with ai.History — the model remembers context across turns. Type reset to clear history.
7. micro flow (event-driven orchestration)
Subscribe to broker events and let an LLM orchestrate the response:
import"go-micro.dev/v6/flow"f:=flow.New("onboard",flow.Trigger("events.user.created"),flow.Prompt("New user: {{.Data}}. Send welcome email and create workspace."),flow.Provider("anthropic"),flow.APIKey(key),)f.Register(service.Registry(),service.Options().Broker,service.Client())
Or from the CLI:
micro flow run --trigger events.user.created \
--prompt "New user: {{.Data}}. Send welcome email."\
--provider anthropic
micro flow exec --prompt "List all users" --provider anthropic
8. micro api (HTTP gateway)
A standalone HTTP-to-RPC gateway for exposing services over HTTP without the full dashboard:
micro api # listen on :8080micro api --address :3000 # custom port# Call services through the gatewaycurl -XPOST -d '{"name":"Alice"}' http://localhost:8080/greeter/Greeter.Hello
What You Don’t Need
No agent framework — the building blocks compose; you don’t need a LangChain or CrewAI equivalent
No special handler code — your services are normal Go handlers with doc comments
No API key to use MCP — external agents bring their own models; your services just expose tools
No vendor lock-in — every provider implements the same interface; swap with one import
Getting Started
The fastest path:
# Create a service with MCP enabledmicro new myservice --template crud
cd myservice
# Run itmicro run
# Chat with itANTHROPIC_API_KEY=sk-ant-... micro chat --provider anthropic
> list all records
Go Micro provides built-in support for the Model Context Protocol (MCP), enabling AI agents like Claude to discover and interact with your microservices as tools.
Model Context Protocol (MCP)
Go Micro provides built-in support for the Model Context Protocol (MCP), enabling AI agents like Claude to discover and interact with your microservices as tools.
Overview
MCP gateway automatically exposes your microservices as AI-accessible tools through:
Automatic service discovery via the registry
Dynamic tool generation from service endpoints
Stdio transport for local AI tools (Claude Code, etc.)
HTTP/SSE transport for web-based agents
Automatic documentation extraction from Go comments
Quick Start
1. Add Documentation to Your Service
Simply write Go doc comments on your handler methods:
packagemainimport("context""go-micro.dev/v6")typeGreeterServicestruct{}// SayHello greets a person by name. Returns a friendly greeting message.//// @example {"name": "Alice"}func(g*GreeterService)SayHello(ctxcontext.Context,req*HelloRequest,rsp*HelloResponse)error{rsp.Message="Hello "+req.Namereturnnil}typeHelloRequeststruct{Namestring`json:"name" description:"Person's name to greet"`}typeHelloResponsestruct{Messagestring`json:"message" description:"Greeting message"`}funcmain(){service:=micro.NewService("greeter")service.Init()// Register handler - docs extracted automatically from comments!service.Handle(new(GreeterService))service.Run()}
That’s it! Documentation is automatically extracted from your Go comments.
2. Start the MCP Server
Option A: Stdio Transport (for Claude Code)
# Start your servicego run main.go
# In another terminal, start MCP server with stdiomicro mcp serve
Add to Claude Code config (`~/.claude/claude_desktop_config.json`):
MCP tool calls go through the same authentication and scope enforcement as regular API calls. This means you can control which tokens (and therefore which users, services, or AI agents) can invoke which tools.
Restricting MCP Tool Access
Set endpoint scopes — Visit /auth/scopes and set required scopes on service endpoints. For example, set internal on billing.Billing.Charge to restrict it.
Create scoped tokens — Visit /auth/tokens and create tokens with specific scopes:
A token with scope internal can call endpoints requiring internal
A token with scope * has unrestricted access (admin)
A token with no matching scope gets 403 Forbidden
Use the token — Pass it in the Authorization header for API/MCP calls:
# List available MCP tools (requires valid token)curl http://localhost:8080/mcp/tools \
-H "Authorization: Bearer <token>"# Call a specific tool (scope-checked)curl -X POST http://localhost:8080/mcp/call \
-H "Authorization: Bearer <token>"\
-d '{"tool":"greeter.GreeterService.SayHello","input":{"name":"World"}}'
Common MCP Token Patterns
Use Case
Token Scopes
What It Can Do
Internal tooling
internal
Call endpoints tagged with internal scope
Production AI agent
greeter, users
Only call greeter and user service endpoints
Admin / debugging
*
Full access to all tools
Read-only agent
readonly
Call endpoints tagged with readonly scope
Agent Playground
The agent playground at /agent uses the logged-in user’s session token. Scope checks apply based on the scopes of the user’s account. The default admin user has * scope (full access).
MCP Command Line
The `micro mcp` command provides tools for working with MCP:
# Start MCP server (stdio by default)micro mcp serve
# Start with HTTP transportmicro mcp serve --address :3000
# List available toolsmicro mcp list
# Test a specific toolmicro mcp test greeter.GreeterService.SayHello
Transport Options
Stdio - For local AI tools (Claude Code, recommended)
HTTP/SSE - For web-based agents
See examples for complete usage.
Examples
See `examples/mcp/documented` for a complete working example.
The %i is replaced with the service name. So micro@users.service runs /opt/micro/bin/users.
Deployment
Basic Deploy
micro deploy user@server
Deploy Specific Service
micro deploy user@server --service users
Force Rebuild
micro deploy user@server --build
Named Deploy Targets
Add to your micro.mu:
service users
path ./users
port 8081
service web
path ./web
port 8080
deploy prod
ssh deploy@prod.example.com
deploy staging
ssh deploy@staging.example.com
Then:
micro deploy prod # deploys to prod.example.commicro deploy staging # deploys to staging.example.com
Managing Services
Check Status
# Local servicesmicro status
# Remote servicesmicro status --remote user@server
These are automatically loaded by systemd when the service starts.
SSH Setup
Key-Based Authentication
# Generate key (if you don't have one)ssh-keygen -t ed25519
# Copy to serverssh-copy-id user@server
SSH Config
Add to ~/.ssh/config:
Host prod
HostName prod.example.com
User deploy
IdentityFile ~/.ssh/deploy_key
Host staging
HostName staging.example.com
User deploy
IdentityFile ~/.ssh/deploy_key
Then deploy with:
micro deploy prod
Troubleshooting
“Cannot connect to server”
✗ Cannot connect to myserver
SSH connection failed. Check that:
• The server is reachable: ping myserver
• SSH is configured: ssh user@myserver
• Your key is added: ssh-add -l
Once services are deployed and managed by systemd, you can optionally run micro server on the same machine to get a full web dashboard with authentication:
# On your servermicro server
This gives you:
Web Dashboard at http://your-server:8080 with JWT authentication
API Gateway with authenticated HTTP-to-RPC proxy
User Management — create accounts, generate/revoke API tokens
Logs & Status — view service logs and uptime from the browser
The server discovers services via the registry automatically. Default login: admin / micro.
Go Micro abstracts away the details of distributed systems. Here are the main features.
Authentication - Auth is built in as a first class citizen. Authentication and authorization enable secure
zero trust networking by providing every service an identity and certificates. This additionally includes rule
based access control.
Dynamic Config - Load and hot reload dynamic config from anywhere. The config interface provides a way to load application
level config from any source such as env vars, file, etcd. You can merge the sources and even define fallbacks.
Data Storage - A simple data store interface to read, write and delete records. It includes support for many storage backends
in the plugins repo. State and persistence becomes a core requirement beyond prototyping and Micro looks to build that into the framework.
Service Discovery - Automatic service registration and name resolution. Service discovery is at the core of micro service
development. When service A needs to speak to service B it needs the location of that service. The default discovery mechanism is
multicast DNS (mdns), a zeroconf system.
Load Balancing - Client side load balancing built on service discovery. Once we have the addresses of any number of instances
of a service we now need a way to decide which node to route to. We use random hashed load balancing to provide even distribution
across the services and retry a different node if there’s a problem.
Message Encoding - Dynamic message encoding based on content-type. The client and server will use codecs along with content-type
to seamlessly encode and decode Go types for you. Any variety of messages could be encoded and sent from different clients. The client
and server handle this by default. This includes protobuf and json by default.
RPC Client/Server - RPC based request/response with support for bidirectional streaming. We provide an abstraction for synchronous
communication. A request made to a service will be automatically resolved, load balanced, dialled and streamed.
Async Messaging - PubSub is built in as a first class citizen for asynchronous communication and event driven architectures.
Event notifications are a core pattern in micro service development. The default messaging system is a HTTP event message broker.
Pluggable Interfaces - Go Micro makes use of Go interfaces for each distributed system abstraction. Because of this these interfaces
are pluggable and allows Go Micro to be runtime agnostic. You can plugin any underlying technology.
# Use NATS for broker and transport, Consul for registryexportMICRO_BROKER=nats
exportMICRO_TRANSPORT=nats
exportMICRO_REGISTRY=consul
exportMICRO_REGISTRY_ADDRESS=127.0.0.1:8500
# Run your servicego run main.go
No code changes required. The framework internally wires the selected implementations.
Correlation – IDs flowing through all three signals
Logging
The default logger can be replaced. Use env vars to adjust level:
MICRO_LOG_LEVEL=debug go run main.go
Recommended fields:
service – service name
version – release identifier
trace_id – propagated context id
span_id – current operation id
Metrics
Patterns:
Emit counters for request totals
Use histograms for latency
Track error rates per endpoint
Example (pseudo-code):
// Wrap handler to record metricsfuncMetricsWrapper(fnmicro.HandlerFunc)micro.HandlerFunc{returnfunc(ctxcontext.Context,reqmicro.Request,rspinterface{})error{start:=time.Now()err:=fn(ctx,req,rsp)latency:=time.Since(start)metrics.Inc("requests_total",req.Endpoint(),errorLabel(err))metrics.Observe("request_latency_seconds",latency,req.Endpoint())returnerr}}
Tracing
Distributed tracing links calls across services.
Propagation strategy:
Extract trace context from incoming headers
Inject into outgoing RPC calls/broker messages
Create spans per handler and client call
Local Development Strategy
Start with only structured logs. Add metrics when operating multiple services. Introduce tracing once debugging multi-hop latency or failures.
This document outlines what hosting looks like for go-micro services, the options available today, and what an ideal hosting platform would provide.
Go Micro services are compiled Go binaries that communicate via RPC and event-driven messaging. Hosting them requires infrastructure that supports service discovery, inter-service communication, persistent storage, and configuration management. Because go-micro uses a pluggable architecture, the hosting environment can range from a single VPS to a fully orchestrated cluster.
Current Hosting Options
Single VPS or Bare Metal
The simplest approach. Deploy compiled binaries to a Linux server and manage them with systemd. This is the model described in the Deployment Guide.
Good for: Small teams, early-stage projects, predictable workloads.
Server
├── micro@users.service
├── micro@posts.service
├── micro@web.service
└── mdns for discovery
Use micro deploy to push binaries over SSH
systemd handles process supervision and restarts
mDNS provides zero-configuration service discovery on the local host
Run services across several machines. This requires replacing mDNS with a network-aware registry like Consul or Etcd so services can discover each other across hosts.
# Point all services at a shared registryMICRO_REGISTRY=consul MICRO_REGISTRY_ADDRESS=consul.internal:8500
Deploy with micro deploy to each target server
Use a central registry (Consul, Etcd, or NATS) for cross-host discovery
Place a load balancer or API gateway in front of public-facing services
Containers and Kubernetes
Package each service as a Docker image and deploy to a Kubernetes cluster or a simpler container runtime like Docker Compose.
Dockerfile example:
FROMgolang:1.21-alpineASbuildWORKDIR/appCOPY . .RUN go build -o service ./cmd/serviceFROMalpine:3.19COPY --from=build /app/service /serviceENTRYPOINT["/service"]
Kubernetes considerations:
Use the Kubernetes registry plugin or run Consul/Etcd as a StatefulSet
ConfigMaps and Secrets replace environment files
Kubernetes Services and Ingress handle external traffic
Horizontal Pod Autoscaler manages scaling
Liveness and readiness probes map to go-micro health checks
Platform as a Service (PaaS)
Deploy to managed platforms like Railway, Render, or Fly.io. Each service runs as a separate application.
Configuration via platform-provided environment variables
Managed TLS and load balancing out of the box
Use NATS or a hosted registry for service discovery between apps
Limited control over networking and co-location
What a Hosting Platform Needs
A purpose-built platform for go-micro services would integrate with the framework’s core abstractions rather than treating services as generic containers.
Service Discovery
The platform must run or integrate with a supported registry so services find each other automatically.
Environment
Recommended Registry
Single host
mDNS (default, zero config)
Multi-host / cloud
Consul, Etcd, or NATS
Kubernetes
Kubernetes registry plugin
RPC and Messaging
Services communicate over RPC (request/response) and asynchronous messaging (pub/sub). The platform must allow direct service-to-service communication on the configured transport.
Transport: HTTP (default), gRPC, or NATS
Broker: HTTP event broker (default), NATS, or RabbitMQ
Internal traffic should stay on a private network
External traffic flows through a gateway or load balancer
Configuration Management
Each service loads configuration from environment variables, files, or remote sources. The platform should provide:
Per-service environment variables or config files
Secret management with restricted access
Hot-reload support for dynamic configuration changes
Data Storage
go-micro’s store interface supports multiple backends. The platform should provide or connect to durable storage.
Development: In-memory store (default)
Production: Postgres, MySQL, Redis, or other supported backends
Persistent volumes or managed database services for stateful data
Health Checks and Observability
The platform should monitor service health and provide visibility into behavior.
Health endpoints for liveness and readiness
Structured logs collected and searchable
Metrics (request rates, latencies, error rates) scraped or pushed
Distributed tracing across service boundaries
See Observability for details on logs, metrics, and traces.
Security
TLS for all inter-service communication
Service-level authentication and authorization via go-micro’s auth interface
Network isolation between services and the public internet
Secret rotation and audit logging
Scaling
Horizontal scaling: run multiple instances of a service behind the client-side load balancer
The registry tracks all instances; the selector distributes requests
Auto-scaling based on resource usage or request volume
Ideal Platform Architecture
A hosting platform tailored for go-micro would look like this:
Performance — Performance characteristics and tuning
1.9 - Performance Considerations
go-micro is designed for developer productivity and ease of use while maintaining good performance for most use cases. This document explains the performance characteristics and trade-offs.
Reflection Usage
go-micro uses Go’s reflection package to enable its core feature: registering any Go struct as a service handler without code generation or boilerplate.
Why Reflection?
// Simple handler registration - no proto files, no code generationtypeGreeterServicestruct{}func(g*GreeterService)SayHello(ctxcontext.Context,req*Request,rsp*Response)error{rsp.Message="Hello "+req.Namereturnnil}server.Handle(server.NewHandler(&GreeterService{}))
This simplicity is only possible with reflection. Alternative approaches (like gRPC or psrpc) require:
Writing .proto files
Running code generators
Implementing generated interfaces
Managing generated code in version control
Performance Impact
Reflection adds approximately 40-60 microseconds (0.04-0.06ms) overhead per RPC call for:
Method discovery and validation (~5μs)
Dynamic method invocation (~30-40μs)
Request/response type construction (~10-15μs)
This totals ~50μs on average, though the exact overhead depends on the complexity of the handler signature and request/response types.
Context: In typical RPC scenarios:
Component
Typical Time
Network I/O
1-10ms
Protobuf serialization
0.1-0.5ms
Business logic
Variable (often 1-100ms+)
Reflection + framework overhead
~0.06ms (0.6-6% of total)
When Reflection Matters
Reflection overhead is only significant when ALL of these conditions are true:
✅ Request rate >100,000 RPS
✅ Business logic <100μs
✅ Local/loopback communication
✅ Sub-millisecond latency requirements
For 99% of applications, database queries, external services, and business logic dominate performance. Reflection is negligible.
Performance Best Practices
1. Profile Before Optimizing
Always measure before assuming reflection is your bottleneck:
# Enable pprof in your serviceimport _ "net/http/pprof"# Profile CPU usagego tool pprof http://localhost:6060/debug/pprof/profile?seconds=30
If reflection shows up as <5% of CPU time, optimizing elsewhere will have more impact.
2. Optimize Business Logic First
Common optimization opportunities (typically 10-100x more impact than removing reflection):
Database queries: Use connection pooling, indexes, query optimization
External API calls: Use caching, batching, async processing
Serialization: Use efficient protobuf instead of JSON
Concurrency: Use goroutines and channels effectively
3. Use Appropriate Transports
go-micro supports multiple transports:
HTTP: Good for debugging, ~1-2ms overhead
gRPC: Binary protocol, ~0.2-0.5ms overhead
In-memory: Development/testing, <0.1ms overhead
Choose based on your deployment:
import"go-micro.dev/v5/server/grpc"// Use gRPC for better performanceservice:=micro.NewService(micro.Server(grpc.NewServer()),)
4. Enable Connection Pooling
Reuse connections to avoid handshake overhead:
// Client-side connection pooling (enabled by default)client:=service.Client()
The registry is responsible for service discovery in Go Micro. It allows services to register themselves and discover other services.
Features
Service registration and deregistration
Service lookup
Watch for changes
Implementations
Go Micro supports multiple registry backends, including:
MDNS (default)
Consul
Etcd
NATS
You can configure the registry when initializing your service.
Plugins Location
Registry plugins live in this repository under go-micro.dev/v6/registry/<plugin> (e.g., consul, etcd, nats). Import the desired package and pass it via micro.Registry(...).
Configure via environment
MICRO_REGISTRY=etcd MICRO_REGISTRY_ADDRESS=127.0.0.1:2379 micro server
Common variables:
MICRO_REGISTRY: selects the registry implementation (mdns, consul, etcd, nats).
MICRO_REGISTRY_ADDRESS: comma-separated list of registry addresses.
Backend-specific variables:
Etcd: ETCD_USERNAME, ETCD_PASSWORD for authenticated clusters.
Example Usage
Here’s how to use a custom registry (e.g., Consul) in your Go Micro service:
The broker provides pub/sub messaging for Go Micro services.
Features
Publish messages to topics
Subscribe to topics
Multiple broker implementations
Implementations
Supported brokers include:
HTTP (default)
NATS (go-micro.dev/v6/broker/nats)
RabbitMQ (go-micro.dev/v6/broker/rabbitmq)
Memory (go-micro.dev/v6/broker/memory)
Plugins are scoped under go-micro.dev/v6/broker/<plugin>.
Configure the broker in code or via environment variables.
Example Usage
Here’s how to use the broker in your Go Micro service:
packagemainimport("go-micro.dev/v6""go-micro.dev/v6/broker""log")funcmain(){service:=micro.NewService("publisher")service.Init()// Publish a messageiferr:=broker.Publish("topic",&broker.Message{Body:[]byte("hello world")});err!=nil{log.Fatal(err)}// Subscribe to a topic_,err:=broker.Subscribe("topic",func(pbroker.Event)error{log.Printf("Received message: %s",string(p.Message().Body))returnnil})iferr!=nil{log.Fatal(err)}// Run the serviceiferr:=service.Run();err!=nil{log.Fatal(err)}}
Using the built-in configuration flags/env vars (no code changes):
MICRO_BROKER=nats MICRO_BROKER_ADDRESS=nats://127.0.0.1:4222 go run main.go
Common variables:
MICRO_BROKER: selects the broker implementation (http, nats, rabbitmq, memory).
MICRO_BROKER_ADDRESS: comma-separated list of broker addresses.
Notes:
NATS addresses should be prefixed with nats://.
RabbitMQ addresses typically use amqp://user:pass@host:5672.
2.4 - Transport
The transport layer is responsible for communication between services.
Features
Pluggable transport implementations
Secure and efficient communication
Implementations
Supported transports include:
HTTP (default)
NATS (go-micro.dev/v6/transport/nats)
gRPC (go-micro.dev/v6/transport/grpc)
Memory (go-micro.dev/v6/transport/memory)
Important: Transport vs Native gRPC
The gRPC transport uses gRPC as an underlying communication protocol, similar to how NATS or RabbitMQ might be used. It does not provide native gRPC compatibility with tools like grpcurl or standard gRPC clients generated by protoc.
If you need native gRPC compatibility (to use grpcurl, polyglot gRPC clients, etc.), you must use the gRPC server and client packages instead:
Plugins are scoped under go-micro.dev/v6/store/<plugin>.
Configure the store in code or via environment variables.
Example Usage
Here’s how to use the store in your Go Micro service:
packagemainimport("go-micro.dev/v6""go-micro.dev/v6/store""log")funcmain(){service:=micro.NewService("store-example")service.Init()// Write a recordiferr:=store.Write(&store.Record{Key:"foo",Value:[]byte("bar")});err!=nil{log.Fatal(err)}// Read a recordrecs,err:=store.Read("foo")iferr!=nil{log.Fatal(err)}log.Printf("Read value: %s",string(recs[0].Value))}
Plugins are scoped under each interface directory within this repository. To use a plugin, import it directly from the corresponding interface subpackage and pass it to your service via options.
For native gRPC compatibility (required for grpcurl, polyglot gRPC clients, etc.), use the gRPC server and client plugins. Note: This is different from the gRPC transport.
Defaults: If you don’t set an implementation, Go Micro uses sensible in-memory or local defaults (e.g., mDNS for registry, HTTP transport, memory broker/store).
Options: Each plugin exposes constructor options to configure addresses, credentials, TLS, etc.
Imports: Only import the plugin you need; this keeps binaries small and dependencies explicit.
3 - Examples
Learn by building real microservices with go-micro
Learn by Example
Runnable examples are the fastest way to move from reading the guides to changing
one thing. Start with the path that matches where you are in the services →
agents → workflows lifecycle.
// services/users/main.gopackagemainimport("context""database/sql""go-micro.dev/v6""go-micro.dev/v6/server"_"github.com/lib/pq")typeUserstruct{IDint64`json:"id"`Emailstring`json:"email"`Namestring`json:"name"`}typeUsersServicestruct{db*sql.DB}typeGetUserRequeststruct{IDint64`json:"id"`}typeGetUserResponsestruct{User*User`json:"user"`}func(s*UsersService)Get(ctxcontext.Context,req*GetUserRequest,rsp*GetUserResponse)error{varuUsererr:=s.db.QueryRow("SELECT id, email, name FROM users WHERE id = $1",req.ID).Scan(&u.ID,&u.Email,&u.Name)iferr!=nil{returnerr}rsp.User=&ureturnnil}funcmain(){db,err:=sql.Open("postgres","postgres://user:pass@localhost/users?sslmode=disable")iferr!=nil{panic(err)}deferdb.Close()svc:=micro.NewService("users",micro.Version("1.0.0"),)svc.Init()server.RegisterHandler(svc.Server(),&UsersService{db:db})iferr:=svc.Run();err!=nil{panic(err)}}
# Terminal 1: Users servicecd services/users
go run main.go
# Terminal 2: Products servicecd services/products
go run main.go
# Terminal 3: Orders servicecd services/orders
go run main.go
# Terminal 4: API Gatewaycd gateway
go run main.go
Testing
# Get usercurl http://localhost:8080/users?id=1# Create ordercurl -X POST http://localhost:8080/orders \
-H 'Content-Type: application/json'\
-d '{"user_id": 1, "product_id": 100, "amount": 99.99}'
Properly shutting down services to avoid dropped requests and data loss.
The Problem
Without graceful shutdown:
In-flight requests are dropped
Database connections leak
Resources aren’t cleaned up
Load balancers don’t know service is down
Solution
Go Micro handles SIGTERM/SIGINT by default, but you need to implement cleanup logic.
Basic Pattern
packagemainimport("context""os""os/signal""syscall""time""go-micro.dev/v6""go-micro.dev/v6/logger")funcmain(){svc:=micro.NewService("myservice",micro.BeforeStop(func()error{logger.Info("Service stopping, running cleanup...")returncleanup()}),)svc.Init()// Your service logiciferr:=svc.Handle(new(Handler));err!=nil{logger.Fatal(err)}// Run with graceful shutdowniferr:=svc.Run();err!=nil{logger.Fatal(err)}logger.Info("Service stopped gracefully")}funccleanup()error{// Close database connections// Flush logs// Stop background workers// etc.returnnil}
Database Cleanup
typeServicestruct{db*sql.DB}func(s*Service)Shutdown(ctxcontext.Context)error{logger.Info("Closing database connections...")// Stop accepting new requestss.db.SetMaxOpenConns(0)// Wait for existing connections to finish (with timeout)done:=make(chanstruct{})gofunc(){s.db.Close()close(done)}()select{case<-done:logger.Info("Database closed gracefully")returnnilcase<-ctx.Done():logger.Warn("Database close timeout, forcing")returnctx.Err()}}
packagemainimport("context""database/sql""fmt""os""os/signal""sync""syscall""time""go-micro.dev/v6""go-micro.dev/v6/logger")typeApplicationstruct{db*sql.DBworkers[]*Workerwgsync.WaitGroupmusync.RWMutexclosingbool}funcNewApplication(db*sql.DB)*Application{return&Application{db:db,workers:make([]*Worker,0),}}func(app*Application)AddWorker(w*Worker){app.workers=append(app.workers,w)w.Start()}func(app*Application)Shutdown(ctxcontext.Context)error{app.mu.Lock()ifapp.closing{app.mu.Unlock()returnnil}app.closing=trueapp.mu.Unlock()logger.Info("Starting graceful shutdown...")// Stop accepting new worklogger.Info("Stopping workers...")for_,w:=rangeapp.workers{iferr:=w.Stop(5*time.Second);err!=nil{logger.Warnf("Worker failed to stop: %v",err)}}// Wait for in-flight requests (with timeout)shutdownComplete:=make(chanstruct{})gofunc(){app.wg.Wait()close(shutdownComplete)}()select{case<-shutdownComplete:logger.Info("All requests completed")case<-ctx.Done():logger.Warn("Shutdown timeout, forcing...")}// Close resourceslogger.Info("Closing database...")iferr:=app.db.Close();err!=nil{logger.Errorf("Database close error: %v",err)}logger.Info("Shutdown complete")returnnil}funcmain(){db,err:=sql.Open("postgres",os.Getenv("DATABASE_URL"))iferr!=nil{logger.Fatal(err)}app:=NewApplication(db)// Add background workersapp.AddWorker(&Worker{name:"cleanup"})app.AddWorker(&Worker{name:"metrics"})svc:=micro.NewService("myservice",micro.BeforeStop(func()error{ctx,cancel:=context.WithTimeout(context.Background(),30*time.Second)defercancel()returnapp.Shutdown(ctx)}),)svc.Init()handler:=&Handler{app:app}iferr:=svc.Handle(handler);err!=nil{logger.Fatal(err)}// Run serviceiferr:=svc.Run();err!=nil{logger.Fatal(err)}}
Kubernetes Integration
Liveness and Readiness Probes
func(h*Handler)Health(ctxcontext.Context,req*struct{},rsp*HealthResponse)error{// Liveness: is the service alive?rsp.Status="ok"returnnil}func(h*Handler)Ready(ctxcontext.Context,req*struct{},rsp*ReadyResponse)error{h.app.mu.RLock()closing:=h.app.closingh.app.mu.RUnlock()ifclosing{// Stop receiving traffic during shutdownreturnfmt.Errorf("shutting down")}// Check dependenciesiferr:=h.app.db.Ping();err!=nil{returnfmt.Errorf("database unhealthy: %w",err)}rsp.Status="ready"returnnil}
Kubernetes Manifest
apiVersion:apps/v1kind:Deploymentmetadata:name:myservicespec:replicas:3template:spec:containers:- name:myserviceimage:myservice:latestports:- containerPort:8080livenessProbe:httpGet:path:/healthport:8080initialDelaySeconds:10periodSeconds:10readinessProbe:httpGet:path:/readyport:8080initialDelaySeconds:5periodSeconds:5lifecycle:preStop:exec:# Give service time to drain before SIGTERMcommand:["/bin/sh","-c","sleep 10"]terminationGracePeriodSeconds:40
Best Practices
Set timeouts: Don’t wait forever for shutdown
Stop accepting work early: Set readiness to false
Drain in-flight requests: Let current work finish
Close resources properly: Databases, file handles, etc.
Log shutdown progress: Help debugging
Handle SIGTERM and SIGINT: Kubernetes sends SIGTERM
Coordinate with load balancer: Use readiness probes
Test shutdown: Regularly test graceful shutdown works
Testing Shutdown
# Start servicego run main.go &PID=$!# Send some requestsfor i in {1..10};do curl http://localhost:8080/endpoint &done# Trigger graceful shutdownkill -TERM $PID# Verify all requests completedwait
You have a working go-micro service and want to make it accessible to AI agents via MCP. This guide covers the three approaches, from simplest to most flexible.
Option 1: One-Line Setup (Recommended)
Add a single option to your service constructor:
import"go-micro.dev/v6/gateway/mcp"funcmain(){service:=micro.NewService("myservice",mcp.WithMCP(":3001"),// Add this line)service.Init()// ... register handlers as beforeservice.Run()}
That’s it. Your service now exposes all registered handlers as MCP tools at http://localhost:3001/mcp/tools.
Option 2: Standalone MCP Gateway
If you want the MCP gateway to run separately from your services (e.g., in production with multiple services):
import"go-micro.dev/v6/gateway/mcp"// Start MCP gateway alongside your servicegomcp.ListenAndServe(":3001",mcp.Options{Registry:service.Options().Registry,})
This discovers all services in the registry and exposes them as tools.
Option 3: CLI (No Code Changes)
If you don’t want to modify your service code at all:
# Start your service normallygo run .
# In another terminal, start the MCP gatewaymicro mcp serve --address :3001
The CLI approach uses the same registry to discover running services.
Improving Agent Experience
Once MCP is enabled, improve how agents interact with your service by adding documentation.
// Get retrieves a user by their unique ID. Returns the full user profile// including email, display name, and account status.//// @example {"id": "user-123"}func(s*Users)Get(ctxcontext.Context,req*GetRequest,rsp*GetResponse)error{
The MCP gateway automatically extracts these comments and presents them to agents as tool descriptions.
Step 2: Add Struct Tag Descriptions
typeGetRequeststruct{IDstring`json:"id" description:"User ID in UUID format"`}typeGetResponsestruct{Namestring`json:"name" description:"Display name"`Emailstring`json:"email" description:"Primary email address"`Activebool`json:"active" description:"Whether the account is active"`}
# List all tools the MCP gateway exposescurl http://localhost:3001/mcp/tools | jq
# Test a specific toolcurl -X POST http://localhost:3001/mcp/call \
-H 'Content-Type: application/json'\
-d '{"tool": "myservice.Users.Get", "arguments": {"id": "user-123"}}'
What Doesn’t Need to Change
Handler signatures - No changes needed to your RPC handlers
Proto definitions - Existing protos work as-is
Client code - Services calling each other still use the normal RPC client
Tests - Existing tests continue to work
Deployment - Add a port for MCP, everything else stays the same
// Original gRPC serverpackagemainimport("context""log""net""google.golang.org/grpc"pb"myapp/proto")typeserverstruct{pb.UnimplementedGreeterServer}func(s*server)SayHello(ctxcontext.Context,req*pb.HelloRequest)(*pb.HelloReply,error){return&pb.HelloReply{Message:"Hello "+req.Name},nil}funcmain(){lis,_:=net.Listen("tcp",":50051")s:=grpc.NewServer()pb.RegisterGreeterServer(s,&server{})log.Fatal(s.Serve(lis))}
2. Generate Go Micro Code
Update your proto generation:
# Install protoc-gen-microgo install go-micro.dev/v6/cmd/protoc-gen-micro@latest
# Generate both gRPC and Go Micro codeprotoc --proto_path=. \
--go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
--micro_out=. --micro_opt=paths=source_relative \
proto/hello.proto
This generates:
hello.pb.go - Protocol Buffers types
hello_grpc.pb.go - gRPC client/server (keep for compatibility)
hello.pb.micro.go - Go Micro client/server (new)
3. Migrate Server to Go Micro
// Go Micro serverpackagemainimport("context""go-micro.dev/v6""go-micro.dev/v6/server"pb"myapp/proto")typeGreeterstruct{}func(s*Greeter)SayHello(ctxcontext.Context,req*pb.HelloRequest,rsp*pb.HelloReply)error{rsp.Message="Hello "+req.Namereturnnil}funcmain(){svc:=micro.NewService("greeter",)svc.Init()pb.RegisterGreeterHandler(svc.Server(),new(Greeter))iferr:=svc.Run();err!=nil{log.Fatal(err)}}
// Manually register with Consulconfig:=api.DefaultConfig()config.Address="consul:8500"client,_:=api.NewClient(config)reg:=&api.AgentServiceRegistration{ID:"greeter-1",Name:"greeter",Address:"localhost",Port:50051,}client.Agent().ServiceRegister(reg)// Cleanup on shutdowndeferclient.Agent().ServiceDeregister("greeter-1")
After (Go Micro)
import"go-micro.dev/v6/registry/consul"reg:=consul.NewConsulRegistry()svc:=micro.NewService("greeter",micro.Registry(reg),)// Registration automatic on Run()// Deregistration automatic on shutdownsvc.Run()
Load Balancing Migration
Before (gRPC with custom LB)
// Need external load balancer or custom implementation// Example: round-robin DNS, Envoy, nginx
New services use Go Micro, existing services stay on gRPC.
// New Go Micro service can call gRPC services// Configure gRPC endpoints directlygrpcConn,_:=grpc.Dial("old-service:50051",grpc.WithInsecure())oldClient:=pb.NewOldServiceClient(grpcConn)
2. Migrate Read-Heavy Services First
Services with many clients benefit most from service discovery.
3. Migrate Services with Fewest Dependencies
Leaf services are easier to migrate.
4. Add Adapters if Needed
// gRPC adapter for Go Micro servicetypeGRPCAdapterstruct{microClientpb.GreeterService}func(a*GRPCAdapter)SayHello(ctxcontext.Context,req*pb.HelloRequest)(*pb.HelloReply,error){returna.microClient.SayHello(ctx,req)}// Register adapter as gRPC servers:=grpc.NewServer()pb.RegisterGreeterServer(s,&GRPCAdapter{microClient:microClient})
Checklist
Update proto generation to include --micro_out
Convert handler signatures (response via pointer)
Replace grpc.Dial with Go Micro client
Configure service discovery (Consul, Etcd, etc)
Update deployment (remove hardcoded ports)
Update monitoring (Go Micro metrics)
Test service-to-service communication
Update documentation
Train team on Go Micro patterns
Common Issues
Port Already in Use
gRPC: Manual port management
lis,_:=net.Listen("tcp",":50051")
Go Micro: Automatic or explicit
// Let Go Micro choosesvc:=micro.NewService("greeter")// Or specifysvc:=micro.NewService("greeter",micro.Address(":50051"),)
Service Not Found
Check registry:
# Consulcurl http://localhost:8500/v1/catalog/services
# Or use micro CLImicro services
Different Serialization
gRPC uses protobuf by default. Go Micro supports multiple codecs.
grep -rl 'go-micro.dev/v5' --include='*.go' . \
| xargs sed -i 's|go-micro.dev/v5|go-micro.dev/v6|g'go mod tidy
Update the CLI too:
go install go-micro.dev/v6/cmd/micro@latest
2. TLS is verified by default
In v5, TLS certificate verification was off by default (you opted in with
MICRO_TLS_SECURE=true). In v6 it is on by default — the safe choice now
that an agent, not just a human on a trusted network, can reach an endpoint.
Production: nothing to do. Verification is on.
MICRO_TLS_SECURE is gone — remove it; it’s the default now.
Self-signed certs (local/dev): opt out with MICRO_TLS_INSECURE=true, or
call tls.InsecureConfig() directly.
3. NewService is the service constructor
The service constructor is now symmetric with NewAgent and NewFlow:
micro.New("greeter", ...) still works as a deprecated alias — no rush,
but prefer NewService.
The old name-less form micro.NewService(micro.Name("greeter"), ...) is
removed; pass the name positionally: micro.NewService("greeter", ...).
Generated services already use NewService — re-running micro new or
micro run --prompt emits the v6 form.
That’s it
No other API changed. Agents, services, flows, the registry/broker/store
interfaces, MCP, A2A, and x402 all work as they did — just under
go-micro.dev/v6 and secure by default.
4.2 - `micro loop` quickstart
micro loop scaffolds the autonomous improvement loop that Go Micro uses on
this repository: GitHub Actions workflows for planning, building, evaluation
feedback, coherence, security, and release. Use it when you want a repository to
continuously turn a ranked queue into small PRs while CI remains the merge gate.
1. Initialize the loop
Run the default loop from the repository root:
micro loop init
For every role used by Go Micro itself, scaffold all workflows:
micro loop init --roles all
The command writes:
.github/loop/NORTH_STAR.md — the direction every increment should optimize.
.github/loop/PRIORITIES.md — the ranked queue; the builder takes the top open issue.
.github/loop/prompts/*.md — editable policy for planner, builder, triage, coherence, and security roles.
Edit the files under .github/loop/ to steer the loop. Re-run
micro loop init --roles all --force only when you want to regenerate workflow
mechanics from the installed CLI.
2. Configure the dispatch token
The scheduled builder needs a repository secret containing a token from a user
account that the coding agent will answer. Go Micro names that secret
CODEX_TRIGGER_TOKEN by default. If you use another secret name, pass it when
you initialize the loop:
micro loop init --agent @codex --token-secret LOOP_TOKEN --roles all
The token needs enough repository permission to open issues, comment, push
branches, create pull requests, and enable auto-merge. Run gh auth setup-git in
the environment that will push branches so git push uses the same credentials
as gh.
Choosing an agent
The loop is agent-agnostic by design. Each run opens a fresh tracking issue
and summons the agent with an @mention comment; the prompt file
(.github/loop/prompts/<role>.md) is the instruction. Any coding agent that
(a) responds to an @mention on an issue and (b) can open a PR with gh works —
you select it with --agent.
Codex (--agent @codex, the default). Point --token-secret at a PAT for
the user account Codex follows, and make sure the Codex environment installs
gh and runs gh auth setup-git. This is the path Go Micro itself runs on.
Claude Code (--agent @claude). Install
anthropics/claude-code-action
in the repo so a workflow responds to @claude comments and runs Claude with a
repo-scoped token; then the loop’s dispatch triggers it like any other mention.
Any other mention-driven agent — pass its handle to --agent. The
mechanics don’t care which agent it is.
Not supported by the mention model: agents triggered by issue assignment
rather than a comment (e.g. GitHub Copilot’s coding agent, which you assign an
issue to). The dispatch would need an “assign” adapter for those; it isn’t wired
yet, so stick to mention-driven agents.
3. Make CI the gate
The loop should not be its own reviewer. Protect the default branch so PRs merge
only after the required checks pass. At minimum, require the same commands the
Go Micro loop verifies locally and in CI:
go build ./...
go test ./...
golangci-lint run ./...
If your repository has a harness or end-to-end grader, make that required too.
Keep human approval requirements out of the autonomous path unless you intend the
loop to pause for review.
4. Verify the wiring
After editing the North Star, queue, prompts, token secret, and branch
protection, run:
micro loop verify
micro loop verify checks that the loop direction, queue, prompts, role
workflows, and non-loop CI gate are present. Fix any reported missing items
before relying on scheduled increments.
5. Operate the queue
Keep one ranked list in .github/loop/PRIORITIES.md. Each item should link a
scoped issue and be small enough for one PR. The builder closes both the priority
issue and the per-run tracker issue in the PR body, for example:
Closes #1234
Closes #5678
Use the North Star to keep the queue honest: favor small improvements that move
developers through the services → agents → workflows lifecycle, and surface
breaking API or brand/positioning decisions for humans instead of auto-merging
them.
4.3 - 0→hero reference path
The 0→hero path is the maintained, no-secret reference for the Go Micro
services → agents → workflows lifecycle. It ties the CLI inner loop and the
runtime harness together so a contributor can prove the framework still works as
one system, not as separate demos.
Use it when you want to answer: “Can I scaffold a service, run it locally, talk
to an agent, inspect durable work, and reach the deployment boundary without
cloud credentials?”
What the contract covers
Boundary
Contract
CI check
Scaffold
micro new generates a runnable service with and without MCP support.
go test ./cmd/micro/cli/new -run TestZeroToOne -count=1
First-agent wayfinding
README, website index/quickstart, examples, and no-secret/0→hero docs keep the no-secret → first-agent → debugging → 0→hero links present and in order.
go test ./internal/harness/zero-to-hero-ci -run TestFirstAgentWayfinding -count=1
First agent
micro new, micro agent preflight, micro run, micro chat, and micro inspect agent <name> stay available for the documented first-agent walkthrough.
go test ./cmd/micro -run TestFirstAgentWalkthroughCLIBoundaries -count=1
Run
micro run remains the local development entry point.
go test ./cmd/micro -run TestZeroToHeroCLIBoundaries -count=1
Chat
micro chat remains the interactive agent entry point.
go test ./cmd/micro -run TestZeroToHeroCLIBoundaries -count=1
Inspect
micro inspect agent <name>, micro agent history <name>, micro inspect flow <flow>, and micro flow runs <flow> remain discoverable for run history; the no-secret debugging smoke seeds durable agent history and runs the documented inspect/history commands without provider keys.
go test ./internal/harness/zero-to-hero-ci -run TestNoSecretFirstAgentDebuggingSmoke -count=1
Deploy
micro deploy --dry-run prod resolves the documented deploy target without touching remote infrastructure.
go test ./internal/harness/zero-to-hero-ci -run TestZeroToHeroDeployDryRunCommandSmoke -count=1
Smallest first agent
examples/first-agent runs one service-backed agent with a deterministic mock model and no provider key.
go test ./examples/first-agent -run TestRunFirstAgent -count=1
Runtime reference app
examples/support runs typed services, an agent using those services as tools, an event-driven flow handoff, and an approval gate with only the model mocked.
`go test ./examples/support -run ‘TestRunSupportMockSmoke
Ordered 0→hero transcript
The maintained CI transcript walks scaffold → run/chat/inspect → support-agent chat → flow history → deploy dry-run without provider keys.
make zero-to-hero-transcript
Runtime harnesses
Real services, agents, durable flows, store-backed history, delegation, and A2A run with only the model mocked.
./internal/harness/zero-to-hero-ci/run.sh and make provider-conformance-mock
Find the one-command entrypoint
After installing the CLI, ask micro for the maintained no-secret lifecycle command:
micro zero-to-hero
The command prints the exact harness command below plus the smaller runnable examples, so a new developer can discover the 0→hero path from CLI help instead of translating this guide by hand.
Run the runnable example
From the repository root, start with the smallest service-backed agent when you want the fastest no-secret success path:
go run ./examples/first-agent
Then run the support-desk example when you want to see the full lifecycle in one terminal:
go run ./examples/support
It starts typed services, a support agent, an event-driven intake flow, and an approval gate with a deterministic mock model. Change one service method, agent prompt, or guardrail decision and run it again to learn the system by modifying a working path.
Run the whole no-secret path
From the repository root:
make harness
For the focused ordered transcript only, run:
make zero-to-hero-transcript
That target runs the scaffold contract, the CLI boundary smoke tests, the
0→hero runtime harnesses, the event-driven agent-flow harness, and mock provider
conformance. It is intentionally deterministic: no provider key, cloud account,
SSH access, or remote service is required.
Run focused checks while iterating
Use the dedicated inner-loop target when you need the provider-free CLI contract in one focused command:
make inner-loop
Use the smaller checks when you are working on one seam:
# Install script and first-run CLI boundary, with no network or provider keys.make install-smoke
# Scaffold → run/call contract.go test ./cmd/micro/cli/new -run TestZeroToOne -count=1# First-agent walkthrough boundary: scaffold, preflight, run, chat, inspect.go test ./cmd/micro -run TestFirstAgentWalkthroughCLIBoundaries -count=1# CLI inner-loop commands: run, chat, inspect, flow runs, deploy --dry-run.go test ./cmd/micro -run TestZeroToHeroCLIBoundaries -count=1go test ./cmd/micro/cli/deploy -run TestDeployDryRun -count=1go test ./internal/harness/zero-to-hero-ci -run TestZeroToHeroDeployDryRunCommandSmoke -count=1# Smallest no-secret service-backed first agent.go test ./examples/first-agent -run TestRunFirstAgent -count=1# Maintained 0→hero support-desk reference app.go test ./examples/support -run 'TestRunSupportMockSmoke|TestZeroToHeroReadmeDocumentsLifecycle|TestZeroToHeroInspectTranscript' -count=1# Durable services → agents → workflows reference scenarios../internal/harness/zero-to-hero-ci/run.sh
# Event-as-prompt agent flow.go run ./internal/harness/agent-flow
# Cross-provider semantics with the deterministic mock provider.make provider-conformance-mock
Reference scenarios
examples/first-agent
is the smallest no-secret service-backed agent: one notes service, one scoped
assistant agent, and a deterministic mock model.
examples/support
is the runnable support-desk story: customers, tickets, notify, a support
agent, an intake flow, and an approval gate in one no-secret example.
internal/harness/plan-delegate
is the compact 0→hero scenario: real task and notify services, a conductor
agent, a comms agent, plan persistence, delegation, and a workflow handoff.
internal/harness/universe
boots a larger mini-world: inventory, payment, order confirmation, a concierge
agent, durable checkpoint/resume, agent run history, flow run history, and A2A
reachability.
internal/harness/agent-flow
shows the event-driven path where a user.created event prompts an agent to
call services and complete onboarding.
Together these scenarios keep the North Star executable: services expose typed
capabilities, agents use those capabilities with memory and guardrails, and
workflows compose the work over time.
Keeping the guide honest
If you change the CLI inner loop, durable flow APIs, agent run history, or the
provider/tool semantics, update this guide and the harness in the same PR. The
point of 0→hero is not a polished sample app that drifts from reality; it is a
CI-verifiable contract that the documented lifecycle still works.
4.4 - Adding an AI Provider to Go Micro
This guide walks you through implementing a new AI model provider for
go-micro’s ai package. After following these steps your provider will
be available via ai.New("yourprovider") and automatically usable by the
MCP gateway, the agent playground, and any service that calls
service.Model().
Overview
The ai package uses the same plugin pattern as the rest of go-micro:
define an interface, register an implementation, and let users swap
providers with a single import. All providers live under ai/<name>/.
Go Micro exposes the provider interfaces registered in the current build, so
runtime tooling and docs can report what is actually available after blank
imports are linked in:
Convert req.Tools into the provider’s native tool format.
Send the request to the provider API.
Parse the response into ai.Response (text in Reply, tool calls in
ToolCalls).
If p.opts.ToolHandler is set and there are tool calls, execute
each tool and make a follow-up API call to get the final answer in
Answer.
func(p*Provider)Generate(ctxcontext.Context,req*ai.Request,opts...ai.GenerateOption)(*ai.Response,error){// 1. Build provider-specific tool definitionsvartools[]map[string]anyfor_,t:=rangereq.Tools{tools=append(tools,map[string]any{// Map to your provider's schema"name":t.Name,"description":t.Description,"parameters":map[string]any{"type":"object","properties":t.Properties,},})}// 2. Build the API request bodyapiReq:=map[string]any{"model":p.opts.Model,"messages":[]map[string]any{{"role":"system","content":req.SystemPrompt},{"role":"user","content":req.Prompt},},}iflen(tools)>0{apiReq["tools"]=tools}// 3. Call the APIresp,rawMsg,err:=p.callAPI(ctx,apiReq)iferr!=nil{returnnil,err}// 4. No tool calls → return immediatelyiflen(resp.ToolCalls)==0{returnresp,nil}// 5. Execute tools and follow upifp.opts.ToolHandler!=nil{// ... build follow-up messages with tool results ...followUpResp,_,err:=p.callAPI(ctx,followUpReq)iferr==nil&&followUpResp.Reply!=""{resp.Answer=followUpResp.Reply}}returnresp,nil}
Stream
If streaming is not supported yet, return a clear error:
func(p*Provider)Stream(ctxcontext.Context,req*ai.Request,opts...ai.GenerateOption)(ai.Stream,error){returnnil,fmt.Errorf("streaming not yet implemented for yourprovider")}
API Helper
Use net/http directly — no external SDK needed:
func(p*Provider)callAPI(ctxcontext.Context,reqmap[string]any)(*ai.Response,map[string]any,error){reqBody,err:=json.Marshal(req)iferr!=nil{returnnil,nil,fmt.Errorf("failed to marshal request: %w",err)}apiURL:=strings.TrimRight(p.opts.BaseURL,"/")+"/v1/chat/completions"httpReq,err:=http.NewRequestWithContext(ctx,"POST",apiURL,bytes.NewReader(reqBody))iferr!=nil{returnnil,nil,fmt.Errorf("failed to create request: %w",err)}httpReq.Header.Set("Content-Type","application/json")httpReq.Header.Set("Authorization","Bearer "+p.opts.APIKey)httpResp,err:=http.DefaultClient.Do(httpReq)iferr!=nil{returnnil,nil,fmt.Errorf("API request failed: %w",err)}deferhttpResp.Body.Close()respBody,_:=io.ReadAll(httpResp.Body)ifhttpResp.StatusCode!=200{returnnil,nil,fmt.Errorf("API error (%s): %s",httpResp.Status,string(respBody))}// Parse your provider's response format into ai.Response// ...}
Step 2: Write Tests
Create ai/yourprovider/yourprovider_test.go. At minimum test:
String() returns the correct name.
Init() applies options.
Default values are set when no options are provided.
Generate() without API key returns an error.
Stream() not implemented returns an error.
packageyourproviderimport("context""testing""go-micro.dev/v6/ai")funcTestProvider_String(t*testing.T){p:=NewProvider()ifp.String()!="yourprovider"{t.Errorf("got %q, want %q",p.String(),"yourprovider")}}funcTestProvider_Defaults(t*testing.T){p:=NewProvider()opts:=p.Options()ifopts.Model!="your-default-model"{t.Errorf("default model = %q, want %q",opts.Model,"your-default-model")}ifopts.BaseURL!="https://api.yourprovider.com"{t.Errorf("default base URL = %q",opts.BaseURL)}}funcTestProvider_Init(t*testing.T){p:=NewProvider()iferr:=p.Init(ai.WithModel("custom"),ai.WithAPIKey("key"));err!=nil{t.Fatalf("Init: %v",err)}ifp.Options().Model!="custom"{t.Errorf("model not updated")}}funcTestProvider_Generate_NoAPIKey(t*testing.T){p:=NewProvider()_,err:=p.Generate(context.Background(),&ai.Request{Prompt:"hi"})iferr==nil{t.Error("expected error without API key")}}funcTestProvider_Stream_NotImplemented(t*testing.T){p:=NewProvider()_,err:=p.Stream(context.Background(),&ai.Request{Prompt:"hi"})iferr==nil{t.Error("expected error for unimplemented streaming")}}
Run:
go test ./ai/yourprovider/...
Step 3: Register the Provider
The init() function in your package calls ai.Register. Users enable
your provider with a blank import:
import_"go-micro.dev/v6/ai/yourprovider"
Then use it:
m:=ai.New("yourprovider",ai.WithAPIKey("your-api-key"),ai.WithModel("your-model-name"),)resp,err:=m.Generate(ctx,&ai.Request{Prompt:"Hello!",SystemPrompt:"You are a helpful assistant",})
Step 4: Update the README
Add your provider to the Supported AI Providers section in the
project README.md. Follow the existing format:
### YourProvider
```go
m := ai.New("yourprovider",
ai.WithAPIKey("your-key"),
ai.WithModel("your-default-model"),
)
Default model: your-default-model
Default base URL: https://api.yourprovider.com
Also add an entry in `ai/README.md` under "Supported Providers".
## Checklist
Before submitting your PR:
- [ ] `ai/yourprovider/yourprovider.go` implements `ai.Model`
- [ ] `init()` calls `ai.Register("yourprovider", ...)`
- [ ] `Generate()` handles tool calls via `ToolHandler` when set
- [ ] `ai/yourprovider/yourprovider_test.go` covers basics
- [ ] `go test ./ai/yourprovider/...` passes
- [ ] `go vet ./ai/yourprovider/...` is clean
- [ ] Provider added to `ai/README.md` under "Supported Providers"
- [ ] Provider added to project README.md under "Supported AI Providers"
- [ ] No new dependencies beyond `go-micro.dev/v6/ai` and stdlib (use
`net/http` directly rather than an SDK)
## Design Notes
**Why `net/http` instead of an SDK?** Keeping providers dependency-free
means `go get go-micro.dev/v6` never pulls in heavy SDK trees. All
existing providers (Anthropic, OpenAI) use raw HTTP for the same reason.
**OpenAI-compatible APIs.** Many providers (Together, Groq, Fireworks,
Atlas Cloud, etc.) expose an OpenAI-compatible `/v1/chat/completions`
endpoint. In that case, users can often just use the `openai` provider
with `ai.WithBaseURL("https://api.yourprovider.com")`. A dedicated
provider package is only needed when the API differs or you want to set
provider-specific defaults.
**Tool call loop.** The current contract is one round of tool execution:
`Generate` calls tools via `ToolHandler`, feeds results back, and
returns the final answer. Multi-turn agentic loops are handled at a
higher level (e.g. the MCP gateway).
## Sponsorship
If you are an AI infrastructure company interested in becoming a
supported provider, we welcome both code contributions and sponsorships.
See the Supported AI Providers section in the project README for
current partners, and reach out via a GitHub issue or the Discord
community to discuss integration.
4.5 - Agent Guardrails
An autonomous agent decides its own actions at runtime, which is what makes it useful — and what makes it risky. The common failure modes are mundane: it loops, repeating the same call without making progress; it runs away, taking far more steps (and cost) than the task warrants; it takes an action that should have had a human or a policy in the way.
Go Micro separates orchestration (the model deciding what to do) from execution safety (whether a decided action is allowed to run). Every tool call an agent makes passes through one choke point, and that’s where the guardrails live — so they apply uniformly to service calls, custom tools, and delegate, without touching the model or your services.
The three agent guardrails
Stop on count — MaxSteps
Bounds the total number of tool executions in a single Ask. Once exceeded, further calls are refused and the model is told to stop and summarize. The blunt backstop against runaway cost.
micro.NewAgent("worker",micro.AgentMaxSteps(8))
Stop on repeat — LoopLimit
Bounds how many times the agent may call the same tool with the same arguments in one Ask. Identical repeated calls make no progress — MaxSteps only bounds them by total count, and a circuit breaker only catches failures, not a call that succeeds and is pointlessly repeated. When the limit is hit, the call is refused with a message that tells the model it’s looping, so it changes approach instead of spinning:
loop detected: you have already called “search.Search.Query” with the same arguments 3 times and the result will not change. Stop repeating it — try a different approach, or finish with what you have.
micro.NewAgent("worker",micro.AgentLoopLimit(3))
LoopLimit is on by default (a lenient 3) because identical repeated calls are never useful. Set AgentLoopLimit(0) to disable it.
Gate the action — ApproveTool
A hook called before each action runs. Return false to block it, with a reason that’s surfaced to the model. Use it for human-in-the-loop approval, spend limits, allow/deny lists, or any policy:
ApproveTool is also where an external policy engine plugs in. It sees every tool call before execution and can veto, so you can route decisions to your own rules, a budget service, or a third-party runtime-safety layer — without go-micro depending on it. Orchestration stays in the agent; execution safety stays in the hook. That separation is the whole point: you can swap the safety layer without touching the agent.
Wrap the whole execution — WrapTool
ApproveTool is a before gate. When you need the full lifecycle — timing, logging, metrics, retries, or inspecting the result — wrap the execution instead. WrapTool is the tool-side analogue of go-micro’s client.CallWrapper and server.HandlerWrapper: a wrapper takes the next handler and returns a new one, so code before the next(...) call runs before the tool, and code after runs after.
The handler signature is the same one every provider uses to execute a tool, and it mirrors a service handler — context first, the call in, a result out:
call.ID is a correlation ID carried through from the provider, so a wrapper can tie a tool call back to the request it came from. call.Scan(&v) decodes the arguments into a typed struct when you’d rather not work with the raw map.
Wrappers run outside the built-in guardrails, so they observe every call and its result — including a guardrail’s refusal. Multiple wrappers compose outermost-first (the first registered is the outer layer). A “before/after” hook is just the two halves of one wrapper, and retry is calling next again — so the wrapper is the single, composable seam for everything around execution, while MaxSteps, LoopLimit, and ApproveTool remain the named guardrails on top of it.
Reliability metadata
A wrapper has what it needs to build reliability tooling — loop handling, retry policies, auditing — without coupling to the agent:
What happened — a guardrail refusal is tagged with a structured reason on the result, so you switch on it rather than parse a message:
res:=next(ctx,call)switchres.Refused{caseai.RefusedLoop:// the agent repeated an identical callcaseai.RefusedMaxSteps:// the step budget was exhaustedcaseai.RefusedApproval:// ApproveTool blocked it}
Which run — ai.RunInfoFrom(ctx) returns a correlation id for the run, the agent’s name, and the parent run when the call came from a delegated sub-agent:
Per-call detail — call.ID (correlation), call.Name; duration is time.Since(start) around next, and step/attempt counts are naturally counted by the wrapper itself (it sees every call).
Execution safety at the gateway
When agents reach tools through the MCP gateway, the gateway adds its own per-tool policies, independent of the agent:
RateLimit — requests-per-second per tool.
CircuitBreaker — a tool that fails repeatedly is temporarily blocked, so a failing dependency doesn’t cascade.
Together with the agent-side guardrails, that’s a full set: bound the count, stop the spin, gate the action, rate-limit and circuit-break at the edge.
Why it matters for autonomous agents
These are most important when no human is in the loop. An agent triggered by an event runs unattended — there’s no one to notice it looping or to approve a risky call. The guardrails are what let it fail safely and recover on its own rather than quietly burning resources.
This guide covers common patterns for integrating AI agents with Go Micro services, from single-agent workflows to multi-agent architectures.
Pattern 1: Single Agent with Multiple Services
The simplest and most common pattern. One AI agent has access to multiple microservices as MCP tools.
User → AI Agent → MCP Gateway → [Service A, Service B, Service C]
Setup
Run multiple services and expose them all through one MCP gateway:
users:=micro.NewService("users",micro.Address(":8081"))tasks:=micro.NewService("tasks",micro.Address(":8082"))notifications:=micro.NewService("notifications",micro.Address(":8083"))// Run all together as a modular monolithg:=micro.NewGroup(users,tasks,notifications)g.Run()
With micro run, all services are discovered automatically via the registry, and the MCP tools endpoint at /mcp/tools exposes every endpoint from every service.
When to Use
Most applications start here
Agent needs to orchestrate across services (e.g., “create a task and notify the assignee”)
You want the agent to choose which service to call based on the user’s request
Pattern 2: Scoped Agents
Different agents have access to different subsets of tools via scopes.
Create tokens with different scopes for each agent:
// Gateway with scope enforcementmcp.ListenAndServe(":3000",mcp.Options{Registry:reg,Auth:authProvider,Scopes:map[string][]string{"billing.Billing.Charge":{"billing:admin"},"users.Users.Delete":{"users:admin"},"orders.Orders.List":{"orders:read"},"orders.Orders.Create":{"orders:write"},"support.Support.CreateTicket":{"support:write"},},})
Your Go Micro service itself calls an AI model to process data, using the ai package.
User → API → Your Service → AI Model (Claude/GPT)
→ Other Services
Setup
import("go-micro.dev/v6/ai"_"go-micro.dev/v6/ai/anthropic")typeSummaryServicestruct{aiai.Modeltasks*TaskClient}funcNewSummaryService()*SummaryService{return&SummaryService{ai:ai.New("anthropic",ai.WithAPIKey(os.Getenv("ANTHROPIC_API_KEY")),ai.WithModel("claude-sonnet-4-20250514"),),}}// Summarize generates an AI summary of a project's tasks.// Returns a natural language summary of task status, blockers, and progress.//// @example {"project_id": "proj-1"}func(s*SummaryService)Summarize(ctxcontext.Context,req*SummarizeRequest,rsp*SummarizeResponse)error{// Fetch tasks from another servicetasks,err:=s.tasks.List(ctx,req.ProjectID)iferr!=nil{returnerr}// Use AI to summarizeresp,err:=s.ai.Generate(ctx,&ai.Request{Prompt:fmt.Sprintf("Summarize these tasks:\n%s",formatTasks(tasks)),SystemPrompt:"You are a concise project manager. Summarize task status in 2-3 sentences.",})iferr!=nil{returnerr}rsp.Summary=resp.Replyreturnnil}
When to Use
Your service needs to process natural language
Generating summaries, classifications, or extractions
Enriching data with AI before returning to the caller
Pattern 4: Agent with Tool Calling
An AI model calls your services as tools, with automatic tool execution via the ai package.
User → Your App → AI Model ←→ MCP Tools (your services)
Setup
import("go-micro.dev/v6/ai"_"go-micro.dev/v6/ai/anthropic")// Define tools from your service endpointstools:=[]ai.Tool{{Name:"create_task",Description:"Create a new task with title and assignee",Properties:map[string]any{"title":map[string]any{"type":"string","description":"Task title"},"assignee":map[string]any{"type":"string","description":"Username"},},},{Name:"list_tasks",Description:"List tasks filtered by status",Properties:map[string]any{"status":map[string]any{"type":"string","description":"Filter: todo, in_progress, done"},},},}// Handle tool calls by routing to your services. The handler mirrors a// go-micro RPC handler: context first, the call in, a result out.toolHandler:=func(ctxcontext.Context,callai.ToolCall)ai.ToolResult{switchcall.Name{case"create_task":varrspCreateResponseerr:=client.Call(ctx,"tasks","TaskService.Create",call.Input,&rsp)iferr!=nil{returnai.ToolResult{ID:call.ID,Content:fmt.Sprintf(`{"error": "%s"}`,err)}}b,_:=json.Marshal(rsp)returnai.ToolResult{ID:call.ID,Value:rsp,Content:string(b)}case"list_tasks":varrspListResponseerr:=client.Call(ctx,"tasks","TaskService.List",call.Input,&rsp)iferr!=nil{returnai.ToolResult{ID:call.ID,Content:fmt.Sprintf(`{"error": "%s"}`,err)}}b,_:=json.Marshal(rsp)returnai.ToolResult{ID:call.ID,Value:rsp,Content:string(b)}}returnai.ToolResult{ID:call.ID,Content:`{"error": "unknown tool"}`}}m:=ai.New("anthropic",ai.WithAPIKey(os.Getenv("ANTHROPIC_API_KEY")),ai.WithToolHandler(toolHandler),)// The model will automatically call tools and return the final answerresp,err:=m.Generate(ctx,&ai.Request{Prompt:"Create a task for Alice to review the PR and tell me what tasks she has",SystemPrompt:"You are a helpful project management assistant",Tools:tools,})fmt.Println(resp.Answer)// "I've created a task for Alice to review the PR. She now has 3 tasks: ..."
When to Use
Building a chatbot or assistant that manages your services
The agent playground in micro run uses this pattern
You want the AI to decide which tools to call and in what order
Pattern 5: Event-Driven Agent Triggers
Services emit events that trigger agent actions via the broker.
Service → Broker Event → Agent Handler → AI Model → Action
Setup
// Publisher: emit events from your servicebroker.Publish("tasks.created",&broker.Message{Body:taskJSON,})// Subscriber: agent handler reacts to eventsbroker.Subscribe("tasks.created",func(pbroker.Event)error{vartaskTaskjson.Unmarshal(p.Message().Body,&task)// Use AI to auto-assign based on task contentresp,err:=aiModel.Generate(ctx,&ai.Request{Prompt:fmt.Sprintf("Who should handle this task? Title: %s, Description: %s. Team: alice (frontend), bob (backend), charlie (devops)",task.Title,task.Description),SystemPrompt:"Reply with just the username of the best person to handle this task.",})// Auto-assignclient.Call(ctx,"tasks","TaskService.Update",map[string]any{"id":task.ID,"assignee":strings.TrimSpace(resp.Reply),},nil)returnnil})
When to Use
Automated workflows triggered by service events
AI-powered routing, classification, or triage
Background processing without user interaction
Pattern 6: Claude Code Integration
Developers use Claude Code with your services as MCP tools for local development workflows.
Developer → Claude Code → stdio MCP → [local services]
Setup
# Start services locallymicro run
# In another terminal, use Claude Code with your services# Claude Code config (~/.claude/claude_desktop_config.json):
"List all tasks that are blocked"
"Create a user account for the new hire"
"Check the health of all services"
When to Use
Developer productivity workflows
Managing services during development
Testing and debugging with natural language
Pattern 7: LangChain / LlamaIndex Integration
Use the official Python SDKs to connect agent frameworks directly to your services.
LangChain
fromlangchain_go_microimportGoMicroToolkit# Connect to MCP gatewaytoolkit=GoMicroToolkit(base_url="http://localhost:3000",token="Bearer <token>",)# Get LangChain tools automaticallytools=toolkit.get_tools()# Use with any LangChain agentfromlangchain.agentsimportAgentExecutor,create_tool_calling_agentagent=create_tool_calling_agent(llm,tools,prompt)executor=AgentExecutor(agent=agent,tools=tools)executor.invoke({"input":"Create a task for Alice"})
LlamaIndex
fromgo_micro_llamaindeximportGoMicroToolkittoolkit=GoMicroToolkit(base_url="http://localhost:3000",token="Bearer <token>",)# Use as LlamaIndex toolstools=toolkit.to_tool_list()# Use with a LlamaIndex agentfromllama_index.core.agentimportReActAgentagent=ReActAgent.from_tools(tools,llm=llm)agent.chat("What tasks are assigned to Bob?")
When to Use
Python-based agent pipelines
RAG (Retrieval-Augmented Generation) workflows with LlamaIndex
Multi-step LangChain chains that orchestrate your services
Teams that prefer Python for AI/ML work
Pattern 8: Standalone Gateway for Production
Run the MCP gateway as a separate, horizontally scalable process.
┌──────────────────┐
Claude/GPT/Agent ──→│ micro-mcp-gateway │──→ Service A (consul)
│ (standalone) │──→ Service B (consul)
└──────────────────┘──→ Service C (consul)
docker run -p 3000:3000 ghcr.io/micro/micro-mcp-gateway \
--registry consul \
--registry-address consul:8500
When to Use
Production deployments where you want the gateway to scale independently
Multiple teams deploying services but sharing one MCP endpoint
Enterprise environments needing centralized auth and audit
Pattern 9: Planning and Delegation
Built into the Agent abstraction. Every agent gets two harness tools — plan and delegate — with no extra setup. They are plain tools, not a separate graph runtime.
Conductor ──plan──→ (records ordered steps in memory)
──delegate──→ registered agent (RPC) or ephemeral sub-agent
Setup
Nothing to wire — the tools are added to every agent automatically. Guide their use with the prompt:
conductor:=micro.NewAgent("conductor",micro.AgentServices("task"),micro.AgentPrompt("For multi-step requests, call the plan tool first to record your steps. "+"For notifications, delegate to the \"comms\" agent (to: \"comms\")."),micro.AgentProvider("anthropic"),)
plan records an ordered list of steps (task + status) in the agent’s store-backed memory, surfaced back on later turns so it stays oriented.
delegate hands a self-contained subtask to another agent. Delegate-first: if the target is a registered agent it’s reached over RPC; otherwise a focused, short-lived sub-agent is created with a fresh, isolated context. A sub-agent is just an agent — created with New, talked to with Ask; there’s no separate “spawn”/“fork” concept.
If agents don’t know what errors are possible, they can’t handle them gracefully. Always document error cases in your handler comments.
Don’t: Build Agent Logic into Services
Keep services as pure business logic. Let the agent harness handle orchestration, retries, and decision-making. Your service should just do one thing well.
Most agent work is one-shot: a prompt goes in, an answer comes out. The next
step in agentic systems is the loop — run a step over and over, letting the
agent keep working until the goal is met instead of stopping after one pass. One
agent improves an architecture while another removes duplicated abstractions,
both opening pull requests continuously; a draft is refined until it’s good
enough; a build is fixed and re-run until it’s green.
The catch is cost and runaway risk: a loop “burns through tokens a lot faster
than a simple Q&A chatbot,” and a non-deterministic stop (“keep going until
you’re done”) has no natural ceiling. So a usable loop needs two things:
a stop condition — how it decides it’s done, and
a hard cap — a guardrail that guarantees it always terminates.
Go Micro gives you both as a flow step: micro.FlowLoop.
The shape
micro.FlowLoop is a StepFunc, so it drops into a flow’s ordered, checkpointed
step list like any other step. It runs a body step repeatedly, carrying the
flow State from one pass to the next, until a stop condition fires or the
iteration cap is hit — whichever comes first.
f:=micro.NewFlow("refactor",micro.FlowProvider("anthropic"),micro.FlowSteps(micro.FlowStep{Name:"improve",Run:micro.FlowLoop(micro.FlowDispatch("coder"),// the body: an agent does one passmicro.FlowUntilLLM("Is the refactor complete with no duplicated abstractions left?"),micro.FlowLoopMax(5),// the ceiling: never more than 5 passes)},),)
Stop conditions
Code-defined — FlowUntil stops when your predicate returns true. Use it
when “done” is something you can measure (tests pass, a score clears a
threshold, a queue is empty):
Model-judged — FlowUntilLLM asks the flow’s model, after each pass,
whether the goal is met, and stops on an affirmative answer. This is the
supervised (“Ralph”) loop: the agent decides when it’s done, while the cap
still guarantees it stops. It requires a flow model (FlowProvider/FlowAPIKey).
micro.FlowUntilLLM("Have all the failing tests been fixed?")
You can combine both — either firing stops the loop.
The guardrail
FlowLoopMax(n) is the ceiling. The body never runs more than n times, so the
loop always terminates even if the stop condition never fires. When the cap is
hit, the loop returns the latest state rather than erroring — the guardrail did
its job. Always set it. For tighter budgets, keep the cap low and pair the
loop with agent guardrails (e.g. token/spend limits)
and paid tools (per-call metering) so a background loop
can’t run up an unbounded bill.
Watching progress
FlowOnIteration runs after each pass — log it, or persist a summary so you can
see how a long-running loop is doing:
A loop runs as a single flow step. The flow checkpoints the loop’s outcome
(before and after the step) through its Checkpoint, and a
resume re-enters the step — so keep loop bodies safe to repeat. For long loops,
use FlowOnIteration to persist per-pass progress.
Run it
A complete, offline example (no API key — the body and stop condition are plain
Go) is in examples/flow-loop:
go run ./examples/flow-loop/
# refining until quality >= 90# pass 1 → quality 30# pass 2 → quality 60# pass 3 → quality 90# done: {"text":"draft refined (quality 90)","quality":90}
Swap the body for micro.FlowDispatch("agent") or micro.FlowLLM(...) and the
stop check for micro.FlowUntilLLM(...) to turn it into a real agent loop.
Go Micro speaks the Agent2Agent (A2A) protocol — the open standard for agents on different frameworks to discover and call each other over HTTP. The A2A gateway is the agent-side analogue of the MCP gateway: MCP exposes your services as tools, A2A exposes your agents as agents.
There is nothing to add to an agent. An agent already registers in the registry with type=agent metadata; the gateway discovers it, generates an Agent Card from that metadata, and translates incoming A2A tasks to the agent’s existing Agent.Chat RPC — the same call delegate and flows use.
Run it
micro a2a serve --address :4000 --base_url https://agents.example.com
micro a2a list # agents and their Agent Card URLs
A2A is JSON-RPC over HTTP — a different wire protocol from go-micro’s RPC — so something always translates between the two. That something doesn’t have to be a separate process. There are two ways to run it:
A gateway (above) fronts every agent in the registry behind one endpoint. Use it for a single front door, centralized discovery, and shared policy.
Directly on the agent.AgentA2A(addr) makes the agent serve its own A2A endpoint when it runs — no separate gateway, and the task is handled in-process (no extra RPC hop):
agent:=micro.NewAgent("task-mgr",micro.AgentServices("task"),micro.AgentProvider("anthropic"),micro.AgentA2A(":4000"),// also reachable at http://host:4000 over A2A)agent.Run()
The agent stays a normal go-micro service; this adds a second, A2A-native HTTP endpoint. Now any A2A client can curl it directly. Use it when each agent should be independently addressable without a gateway.
Both reuse the same handler; the only difference is whether the agent is reached over RPC (gateway) or in-process (embedded).
Discovery: cards from the registry
Every registered agent gets an Agent Card, generated from its registry metadata (name, the services it manages). Cards are not published by the agent — they are derived, the same way MCP tools are derived from service endpoints.
Endpoint
Returns
GET /agents
a directory of all Agent Cards
GET /agents/{name}
one agent’s card
GET /agents/{name}/.well-known/agent.json
one agent’s card (well-known path)
POST /agents/{name}
the agent’s JSON-RPC endpoint
GET /.well-known/agent.json
the single agent’s card, when exactly one is registered
Each managed service is advertised as its own typed skill. Clients can call the
whole agent at /agents/task-mgr, or address one skill directly at
/agents/task-mgr/skills/task; the skill endpoint serves a focused card and
routes the request to the same agent with that skill selected.
Calling an agent
A2A uses JSON-RPC 2.0 over HTTP. Send a message with message/send; the gateway runs the agent and returns a completed Task:
Retrieve a task later with tasks/get (params: { "id": "…" }). To continue
the same piece of work, send another message/send with the previous taskId
and contextId. The gateway preserves the task id, context id, and prior
history, then appends the new user turn and agent reply. That makes a remote
A2A task fit the Go Micro lifecycle: services are still invoked through the
agent’s normal tools, the agent keeps task context across turns, and a workflow
can poll one task id as the conversation progresses.
Push notifications
Operators can register a task callback with
tasks/pushNotificationConfig/set:
The gateway stores one callback per retained task and POSTs the latest task
snapshot to that URL whenever the task changes. Delivery is best effort: failures
do not fail the agent turn, and there is no retry queue in the in-memory gateway.
Use tasks/get as the source of truth after a missed callback or receiver
outage. If a token is configured, it is sent as Authorization: Bearer <token>.
Calling out to other agents
The gateway makes your agents reachable from the A2A ecosystem. The
client (a2a.Client) is the other direction: it lets a Go Micro agent or
flow call an agent on any framework, by URL.
reply,err:=a2a.NewClient("https://other.example.com/agents/research").Send(ctx,"Summarize the latest on X")
It’s wired into the two places that hand off work:
A flow step — flow.A2A(url) is the cross-framework counterpart to
flow.Dispatch(name) (which dispatches to a local agent):
Agent delegate — when an agent’s delegate target is an http(s)
URL, the subtask is sent to that external agent over A2A instead of to a
locally registered one. Nothing else changes; the model just delegates
to a URL.
Send handles the task lifecycle: if the remote returns a task that isn’t
yet terminal, it polls tasks/get until it completes.
Scope
This is the JSON-RPC binding for task execution:
message/send runs the agent and returns a completed Task.
message/stream streams the completed Task as an SSE data: event, giving A2A clients a streaming-compatible path while the underlying agent call remains synchronous.
tasks/get returns a recent task by id.
Multi-turn continuation keeps task state when a new message includes the previous taskId.
tasks/pushNotificationConfig/set / get stores and reads a task callback for best-effort update delivery.
tasks/resubscribe reconnects to an existing task stream, immediately emits the current task snapshot, then streams subsequent updates until the task reaches a terminal state.
input-required task state carries human-input handoffs (for example checkpointed approval pauses) in task status, artifacts, and history; continue the task by sending a follow-up message with the same taskId and contextId.
Agent Card discovery, generated from the registry.
Both directions work: the gateway exposes your agents, and a2a.Client (via flow.A2A or delegate to a URL) calls external ones. The task binding is what makes a Go Micro agent both reachable from, and able to reach, the A2A ecosystem today.
AP2 mandate layer (opt-in)
AP2 sits above A2A as a verifiable-intent and audit layer. Go Micro keeps the
A2A envelope separate from payment settlement: an A2A message can carry signed
AP2 checkout or payment mandates, and the resulting task can retain the stable
mandate reference plus verification result. Payment settlement state remains in
the payment rail. For x402, use an AP2 payment mandate with an x402 rail
reference to name the payment requirement; the existing x402 facilitator still
performs verification and settlement.
Go Micro’s AI primitives map directly onto the taxonomy in Anthropic’s Building Effective Agents. That post draws one distinction that matters:
Workflows — “LLMs and tools orchestrated through predefined code paths.” Deterministic.
Agents — “LLMs dynamically direct their own processes and tool usage.” Model-driven.
Go Micro has both, plus the harness they run inside — and expresses them as plain services and tools, with no graph DSL. That’s deliberate: the same post advises finding “the simplest solution possible” and being “cautious with frameworks… they obscure the underlying mechanics.”
The building block: the augmented LLM
Anthropic’s foundational unit is the augmented LLM — a model with tools, retrieval, and memory. In Go Micro:
Augmented LLM
Go Micro
the model
ai package (7 providers, one interface)
tools
every service endpoint, discovered from the registry
memory
the store (file, Postgres, NATS KV)
Every endpoint is automatically a tool, so the augmented LLM is the default, not something you assemble.
Workflow ↔ flow
A Flow is a workflow in Anthropic’s exact sense: a predefined path — an event on a broker topic triggers a prompt with a fixed set of tools, deterministically. Use it when the task is well-defined and you want predictability.
f:=micro.NewFlow("onboard-user",micro.FlowTrigger("events.user.created"),micro.FlowPrompt("New user {{.Data}} — create a workspace and send a welcome email."),micro.FlowProvider("anthropic"),)
Flow triggers, Agent reasons
A flow doesn’t have to do the reasoning itself. Point it at an agent and it becomes a pure trigger — the event fires, the flow renders the prompt, and a registered agent handles it over RPC with its full capabilities (plan, delegate, memory, guardrails):
f:=micro.NewFlow("onboard-user",micro.FlowTrigger("events.user.created"),micro.FlowPrompt("New user {{.Data}} — get them set up."),micro.FlowAgent("conductor"),// the conductor agent reasons; the flow only triggers)
This is the clean seam between the two halves of the taxonomy: the workflow (deterministic, event-driven) hands off to the agent (dynamic). One engine, two front doors — an event (flow) or a conversation (agent.Ask).
Ordered, durable steps
A flow can be a task made of ordered steps rather than a single turn — the predefined path made explicit. Each step is checkpointed before and after, so if the process dies mid-run the run resumes at the step it stopped on, without re-running the steps that already completed (and already had their side effects). This is durable execution, store-backed by default, with no separate workflow engine.
f:=micro.NewFlow("checkout",micro.FlowTrigger("events.order.placed"),micro.FlowRetry(2),// retry each step; per-step override availablemicro.FlowSteps(micro.FlowStep{Name:"reserve",Run:micro.FlowCall("inventory","Inventory.Reserve")},micro.FlowStep{Name:"charge",Run:micro.FlowCall("payment","Payment.Charge")},micro.FlowStep{Name:"welcome",Run:micro.FlowDispatch("comms")},// hand a step to an agent),// Durable by default; point the default store at Postgres/NATS KV to// survive a real restart, or plug in Temporal/Restate via Checkpoint.)
A step’s action is an RPC (FlowCall), an agent hand-off (FlowDispatch), one model turn (FlowLLM), or any function. State carries a typed payload (Set/Scan) plus a Stage marker — the resume point. Runs are retained for success and failure (audit) unless you set FlowDeleteOnSuccess. On restart, f.Pending(ctx) lists incomplete runs and f.Resume(ctx, runID) continues one. See examples/flow-durable.
The pluggability is the usual go-micro shape: the built-in Checkpoint is store-backed (swap the store backend freely); implement the Checkpoint interface to delegate durability to an external engine. Most teams need neither — the default is durable.
Agent ↔ agent
An Agent is an agent in Anthropic’s exact sense: it directs itself — plans, calls tools, evaluates results, and decides the next step over many turns, with memory across them. Use it when you want flexibility and model-driven decisions.
a:=micro.NewAgent("conductor",micro.AgentServices("task"),micro.AgentProvider("anthropic"),)a.Ask(ctx,"Plan the launch, create the tasks, and have comms notify the owner.")
Long-running memory
Agents use store-backed conversation memory by default, scoped under the agent’s
name. That makes short restarts boring: the next Ask reloads the retained
history from the same store backend you already use for services and flows.
Long-running agents can also keep model context bounded without losing useful
prior context. If you want retrieval without summaries, enable bounded active
context plus a durable archive of every turn:
a:=micro.NewAgent("conductor",micro.AgentServices("task"),micro.AgentProvider("anthropic"),micro.AgentRetrievalMemory(40),// active messages kept in prompt contextmicro.AgentMemoryRecallLimit(5),// archived turns recalled per Ask)
AgentRetrievalMemory(activeLimit) switches the default memory to a store-backed
retriever. The active conversation is capped at activeLimit, every turn is
archived in the same scoped store used by the agent, and future asks inject
matching archived turns ahead of active context. The built-in ranking is
deterministic and credential-free for CI.
When you also want a rolling summary in active context, use compacting memory:
a:=micro.NewAgent("conductor",micro.AgentServices("task"),micro.AgentProvider("anthropic"),micro.AgentCompactMemory(40,12),// max active messages, recent messages kept verbatimmicro.AgentMemoryRecallLimit(5),// compacted turns recalled per Ask)
AgentCompactMemory(maxMessages, keepRecent) switches the default memory to a
deterministic compactor. Once active history grows past maxMessages, older
turns move into the durable archive, a provider-neutral summary is injected into
active context, and the newest keepRecent messages stay verbatim. On future
asks, archived turns whose text matches the current request are recalled ahead of
the active context. Teams that need embeddings or a vector database can still
provide their own AgentMemory implementation.
This is harness memory, not prompt-layer orchestration: services remain the
capabilities, agents remain the dynamic decision makers, and flows remain the
durable predefined paths. Compaction only keeps a scheduled or looping agent from
turning every past turn into model context while still letting it remember facts
that matter to the current service → agent → workflow run.
Checkpointed agent runs and compacted memory share the same store-backed shape.
If a provider call fails after the prompt has been recorded, agent.Resume uses
the checkpointed run id and does not append that same user turn a second time;
completed tool results and recalled archived memory remain available for the
retry.
The patterns — most are already here
Anthropic lists five workflow patterns. Go Micro implements the two richest ones natively, as services and tools, and the rest are ordinary compositions:
Pattern
Go Micro
Routing — classify input, dispatch to a specialist
Orchestrator-workers — a central LLM breaks down a task, delegates to workers, synthesizes
the agent with plan (break down) + delegate (hand to workers) + reply (synthesize) — see Plan & Delegate
Prompt chaining — sequential steps
chain flows, or steps in an agent’s plan
Parallelization — independent subtasks at once
Go concurrency + multiple services/agents; fan out with delegate
Evaluator-optimizer — one LLM generates, another critiques in a loop
two agents over RPC (generator + evaluator)
The orchestrator-workers example is worth calling out: the conductor agent that plans, creates tasks, and delegates the notification to a comms agent is orchestrator-workers — built without a graph engine. See examples/agent-plan-delegate.
Choosing
Follow Anthropic’s guidance:
Start with the augmented LLM (a single service call through a model). Most tasks need nothing more.
Reach for a workflow (flow) when the path is well-defined and you want predictability.
Reach for an agent (agent) when the task needs flexibility and model-driven decisions — and accept the higher cost and the need for guardrails.
Guardrails
Anthropic is emphatic that autonomous agents need stopping conditions, human checkpoints, and sandboxed testing. Go Micro’s agent has two built-in guardrails, both as plain options:
Stopping condition — MaxSteps bounds the number of actions an agent may take per Ask. Once exceeded, further tool calls are refused and the model is told to stop and summarize.
micro.NewAgent("conductor",micro.AgentServices("task"),micro.AgentMaxSteps(8),// at most 8 tool calls per request)
Human-in-the-loop — ApproveTool gates each action before it runs. Return false to block it; the reason is shown to the model so it can adapt. The internal plan tool is never gated (it’s bookkeeping, not an action).
micro.NewAgent("conductor",micro.AgentServices("task"),micro.AgentApproveTool(func(toolstring,inputmap[string]any)(bool,string){ifstrings.HasPrefix(tool,"billing_"){returnfalse,"billing actions require human sign-off"}returntrue,""}),)
These are harness guardrails, not a separate policy engine — a counter and a callback on the path every tool call already takes. For anything that must be predictable, still prefer a workflow, and test agents against the integration harness.
Why no graph DSL
Anthropic: “be cautious with frameworks… understand the underlying code.” Go Micro’s answer is that there is no separate framework to understand — the harness is the service runtime. Workflows and agents are services, and tool use is RPC. plan and delegate are tools, not a graph DSL. The patterns above are code you can read, not a DSL you have to learn. That’s the direction we took going all in on AI.
Atlas Cloud is an enterprise AI infrastructure platform offering 300+ models across text, image, and video through a unified, OpenAI-compatible API. It is an official Go Micro sponsor and a first-class provider in the ai package.
Quick Start
Install or update Go Micro:
go get go-micro.dev/v6@latest
Import the Atlas Cloud provider and use it:
packagemainimport("context""fmt""log""go-micro.dev/v6/ai"_"go-micro.dev/v6/ai/atlascloud")funcmain(){m:=ai.New("atlascloud",ai.WithAPIKey("your-atlas-cloud-key"),)resp,err:=m.Generate(context.Background(),&ai.Request{Prompt:"What is Go Micro?",SystemPrompt:"You are a helpful assistant.",})iferr!=nil{log.Fatal(err)}fmt.Println(resp.Reply)}
Atlas Cloud supports text-to-image generation through the ai.ImageModel interface. This uses the same OpenAI-compatible /v1/images/generations endpoint.
import("context""fmt""go-micro.dev/v6/ai"_"go-micro.dev/v6/ai/atlascloud")funcmain(){ig:=ai.NewImage("atlascloud",ai.WithAPIKey("your-key"),)resp,err:=ig.GenerateImage(context.Background(),&ai.ImageRequest{Prompt:"A Go gopher building microservices, digital art",Size:"1024x1024",})iferr!=nil{log.Fatal(err)}// Image returned as URL or base64, depending on the modelfmt.Println(resp.Images[0].URL)}
ImageRequest Options
Field
Default
Description
Prompt
required
Text description of the image
Model
gpt-image-1
Image model to use
Size
provider default
Image dimensions (e.g. "1024x1024")
N
1
Number of images to generate
Available Image Models
Atlas Cloud offers image models including gpt-image-1, flux-2, nano-banana-pro, and more. Check atlascloud.ai for the full catalog.
The ai.ImageModel interface is also implemented by the OpenAI provider, so switching between providers is a one-line change.
Using with Services (Tool Calling)
Atlas Cloud supports OpenAI-compatible function calling. Combined with Go Micro’s ai.Tools, your services become tools that the model can call:
packagemainimport("context""fmt""log""go-micro.dev/v6""go-micro.dev/v6/ai"_"go-micro.dev/v6/ai/atlascloud")funcmain(){service:=micro.NewService("my-agent")service.Init()// Discover all services as toolstools:=ai.NewTools(service.Registry())discovered,err:=tools.Discover()iferr!=nil{log.Fatal(err)}// Create a model with tool executionm:=ai.New("atlascloud",ai.WithAPIKey("your-key"),ai.WithTools(tools),)// The model can now call your servicesresp,err:=m.Generate(context.Background(),&ai.Request{Prompt:"List all users and send each a welcome email",SystemPrompt:"You are a service orchestrator.",Tools:discovered,})iferr!=nil{log.Fatal(err)}fmt.Println(resp.Answer)}
How it works
ai.NewTools(registry) creates a tool set bound to the service registry
tools.Discover() walks the registry and returns every endpoint as an ai.Tool
ai.WithTools(tools) wires execution into the model — tool calls are routed via RPC
When the model decides to call a tool, it routes to the correct service
This works identically across all providers. Swap "atlascloud" for "anthropic" or "openai" and the same services, tools, and handlers work without changes.
Using with micro chat
micro chat is an interactive terminal agent. Start your services, then chat:
# Terminal 1: start servicesmicro run
# Terminal 2: chat with Atlas CloudATLASCLOUD_API_KEY=your-key micro chat --provider atlascloud
> what services are running?
> get user alice@example.com
> create a new order for product-42
For a single prompt (non-interactive):
micro chat --provider atlascloud --prompt "list all services"
Using with micro run
The agent playground at /agent uses whatever AI provider is configured. To use Atlas Cloud:
exportMICRO_AI_API_KEY=your-atlas-cloud-key
exportMICRO_AI_BASE_URL=https://api.atlascloud.ai
micro run
Open http://localhost:8080/agent and chat with your services through Atlas Cloud.
Using with MCP
The MCP gateway (micro mcp serve) exposes services as tools for external AI agents. Atlas Cloud’s models can be used by any MCP-compatible agent that connects to the gateway. The gateway itself doesn’t depend on a specific AI provider — it serves tools over MCP, and the agent on the other end chooses which model to use.
Swapping Providers
All Go Micro AI providers implement the same ai.Model interface. To switch from Atlas Cloud to another provider, change the import and the provider name:
Your Go doc comments become the documentation that AI agents read when deciding how to call your service. Better descriptions lead to fewer errors, faster task completion, and a better user experience.
How Agents Use Your Docs
When an AI agent receives a user request like “create a task for Alice”, it:
Queries the MCP tools endpoint for available tools
Reads each tool’s description to understand what it does
Reads the parameter schema and descriptions to build the input
References the example to verify the format
Makes the call
If any of these are missing or unclear, the agent guesses — and often guesses wrong.
The Three Essentials
Every handler method needs three things:
1. A Clear Description (Doc Comment)
// Create creates a new task with the given title and description.// Returns the created task with a generated ID and initial status of "todo".// The assignee field is optional; if omitted, the task is unassigned.
Rules:
First sentence: what the method does (imperative mood)
Second sentence: what it returns
Additional sentences: important behavior, constraints, edge cases
2. An Example Input (@example)
// @example {"title": "Fix login bug", "description": "Users can't log in with SSO", "assignee": "alice"}
Rules:
Use realistic values, not placeholders like "string" or "test"
Include all required fields
Include at least one optional field to show the format
Keep it on one line (the parser reads until end of line)
3. Field Descriptions (description tag)
typeCreateRequeststruct{Titlestring`json:"title" description:"Task title (required, max 100 chars)"`Assigneestring`json:"assignee,omitempty" description:"Username to assign (optional)"`}
Rules:
State the type constraint if not obvious (e.g., “UUID format”, “ISO 8601 date”)
List valid values for enums (e.g., “todo, in_progress, or done”)
Note if optional (matches omitempty)
Good vs Bad Examples
Describing What a Method Does
Good:
// GetUser retrieves a user by their unique ID from the database.// Returns the full profile including name, email, and preferences.// Returns an error if the user does not exist.//// @example {"id": "user-123"}func(s*UserService)GetUser(ctxcontext.Context,req*GetRequest,rsp*GetResponse)error{
// Create creates a new [resource].// Returns the created [resource] with a generated ID.//// @example {realistic create payload}// Get retrieves a [resource] by ID.// Returns an error if the [resource] does not exist.//// @example {"id": "realistic-id"}// List returns all [resources], optionally filtered by [criteria].// Returns an empty list if no [resources] match.//// @example {"status": "active"}// Update modifies an existing [resource].// Only the provided fields are updated; omitted fields are unchanged.// Returns an error if the [resource] does not exist.//// @example {"id": "realistic-id", "field": "new-value"}// Delete removes a [resource] by ID. This action is irreversible.// Returns an error if the [resource] does not exist.//// @example {"id": "realistic-id"}
Search Endpoints
// Search finds [resources] matching the query string.// Supports full-text search across [fields].// Results are paginated; use page and per_page to control pagination.// Returns results sorted by relevance by default.//// @example {"query": "realistic search term", "page": 1, "per_page": 20}
Actions with Side Effects
// SendEmail sends an email notification to the specified recipient.// This triggers an actual email delivery — use with caution.// Returns an error if the email address is invalid or the mail server is unavailable.//// @example {"to": "alice@example.com", "subject": "Task assigned", "body": "You have a new task."}
Methods with Complex Inputs
// CreateReport generates a report for the specified date range and metrics.// Processing may take up to 30 seconds for large date ranges.// Valid metrics: cpu_usage, memory_usage, request_count, error_rate.// Date format: YYYY-MM-DD (e.g., "2026-01-15").//// @example {"start_date": "2026-01-01", "end_date": "2026-01-31", "metrics": ["cpu_usage", "error_rate"]}
Impact on Agent Performance
Documentation Quality
First-Call Success Rate
Avg Calls to Complete
No docs
~25%
3-4 calls
Basic (name only)
~50%
2-3 calls
Good (description + types)
~80%
1-2 calls
Excellent (description + types + example)
~95%
1 call
Testing Your Descriptions
1. Use micro mcp list
Check what agents will see:
micro mcp list
Verify each tool has a description and the schema looks correct.
2. Use micro mcp docs
Generate the full documentation:
micro mcp docs
Read through it as if you were an AI agent. Does it make sense without seeing the code?
3. Test with Claude Code
The ultimate test — add your service to Claude Code and try natural language commands:
"Create a task for Alice to fix the login bug"
"What tasks are assigned to Bob?"
"Mark task-1 as done"
If Claude gets it right on the first try, your docs are good.
4. Use micro mcp test
Test individual tools with specific inputs:
micro mcp test tasks.TaskService.Create
Manual Overrides
If you can’t modify the source code (e.g., third-party services), override descriptions at handler registration:
handler:=service.Server().NewHandler(new(LegacyService),server.WithEndpointDocs("LegacyService.Process",server.EndpointDocs{Description:"Process a payment transaction. Charges the specified amount to the customer's payment method on file.",Example:`{"customer_id": "cust-123", "amount_cents": 4999, "currency": "USD"}`,}),)
Manual docs take precedence over auto-extracted comments. This is useful for:
Third-party or generated code where you can’t add comments
Overriding auto-extracted descriptions that aren’t agent-friendly
Adding examples to legacy endpoints
Export Formats
You can export tool descriptions in different formats for use with agent frameworks:
This guide walks you through building a Go Micro service that is AI-native from the start — meaning AI agents can discover, understand, and call your service automatically via the Model Context Protocol (MCP).
What You’ll Build
A task management service with full CRUD operations that:
Exposes every endpoint as an MCP tool automatically
Has rich documentation that agents can read
Includes auth scopes for write operations
Works with Claude Code, the agent playground, and any MCP client
Prerequisites
go install go-micro.dev/v6/cmd/micro@latest
Step 1: Create the Service
micro new tasks
cd tasks
Step 2: Define Your Types
Design your request/response types with description tags. These tags become parameter descriptions that agents read:
packagemainimport"context"// Request types with description tags for AI agentstypeTaskstruct{IDstring`json:"id" description:"Unique task identifier"`Titlestring`json:"title" description:"Short task title (max 100 chars)"`Descriptionstring`json:"description" description:"Detailed task description"`Statusstring`json:"status" description:"Task status: todo, in_progress, or done"`Assigneestring`json:"assignee,omitempty" description:"Username of assigned person"`}typeCreateRequeststruct{Titlestring`json:"title" description:"Task title (required, max 100 chars)"`Descriptionstring`json:"description" description:"Detailed description of the task"`Assigneestring`json:"assignee,omitempty" description:"Username to assign the task to"`}typeCreateResponsestruct{Task*Task`json:"task" description:"The newly created task"`}typeGetRequeststruct{IDstring`json:"id" description:"Task ID to retrieve"`}typeGetResponsestruct{Task*Task`json:"task" description:"The requested task"`}typeListRequeststruct{Statusstring`json:"status,omitempty" description:"Filter by status: todo, in_progress, done (optional)"`}typeListResponsestruct{Tasks[]*Task`json:"tasks" description:"List of matching tasks"`}typeUpdateRequeststruct{IDstring`json:"id" description:"Task ID to update"`Statusstring`json:"status" description:"New status: todo, in_progress, or done"`}typeUpdateResponsestruct{Task*Task`json:"task" description:"The updated task"`}typeDeleteRequeststruct{IDstring`json:"id" description:"Task ID to delete"`}typeDeleteResponsestruct{Deletedbool`json:"deleted" description:"True if the task was deleted"`}
Key point: The description tags are parsed by the MCP gateway and shown to agents as parameter documentation. Be specific about formats, constraints, and valid values.
Step 3: Write the Handler with Doc Comments
Write standard Go doc comments on every handler method. The MCP gateway extracts these automatically at registration time.
typeTaskServicestruct{tasksmap[string]*TasknextIDint}// Create creates a new task with the given title and description.// Returns the created task with a generated ID and initial status of "todo".//// @example {"title": "Fix login bug", "description": "Users can't log in with SSO", "assignee": "alice"}func(t*TaskService)Create(ctxcontext.Context,req*CreateRequest,rsp*CreateResponse)error{t.nextID++task:=&Task{ID:fmt.Sprintf("task-%d",t.nextID),Title:req.Title,Description:req.Description,Status:"todo",Assignee:req.Assignee,}t.tasks[task.ID]=taskrsp.Task=taskreturnnil}// Get retrieves a task by its unique ID.// Returns an error if the task does not exist.//// @example {"id": "task-1"}func(t*TaskService)Get(ctxcontext.Context,req*GetRequest,rsp*GetResponse)error{task,ok:=t.tasks[req.ID]if!ok{returnfmt.Errorf("task %s not found",req.ID)}rsp.Task=taskreturnnil}// List returns all tasks, optionally filtered by status.// If no status filter is provided, returns all tasks.// Valid status values: "todo", "in_progress", "done".//// @example {"status": "todo"}func(t*TaskService)List(ctxcontext.Context,req*ListRequest,rsp*ListResponse)error{for_,task:=ranget.tasks{ifreq.Status==""||task.Status==req.Status{rsp.Tasks=append(rsp.Tasks,task)}}returnnil}// Update changes the status of an existing task.// Valid status transitions: todo -> in_progress -> done.// Returns an error if the task does not exist.//// @example {"id": "task-1", "status": "in_progress"}func(t*TaskService)Update(ctxcontext.Context,req*UpdateRequest,rsp*UpdateResponse)error{task,ok:=t.tasks[req.ID]if!ok{returnfmt.Errorf("task %s not found",req.ID)}task.Status=req.Statusrsp.Task=taskreturnnil}// Delete removes a task by ID. This action is irreversible.// Returns an error if the task does not exist.//// @example {"id": "task-1"}func(t*TaskService)Delete(ctxcontext.Context,req*DeleteRequest,rsp*DeleteResponse)error{if_,ok:=t.tasks[req.ID];!ok{returnfmt.Errorf("task %s not found",req.ID)}delete(t.tasks,req.ID)rsp.Deleted=truereturnnil}
What agents see: Each method’s doc comment becomes the tool description. The @example tag provides a valid JSON input that agents can reference.
Step 4: Register with Scopes
Use server.WithEndpointScopes() to control which agents can call which endpoints:
packagemainimport("context""fmt""go-micro.dev/v6""go-micro.dev/v6/server")funcmain(){service:=micro.NewService("tasks",micro.Address(":8081"))service.Init()service.Handle(&TaskService{tasks:make(map[string]*Task)},// Read operations: any authenticated agentserver.WithEndpointScopes("TaskService.Get","tasks:read"),server.WithEndpointScopes("TaskService.List","tasks:read"),// Write operations: agents with write scopeserver.WithEndpointScopes("TaskService.Create","tasks:write"),server.WithEndpointScopes("TaskService.Update","tasks:write"),// Delete: admin onlyserver.WithEndpointScopes("TaskService.Delete","tasks:admin"),)service.Run()}
Step 5: Run with MCP
There are three ways to run your service with MCP enabled.
Option A: micro run (Recommended for Development)
micro run
Your service is now available at:
Web Dashboard: http://localhost:8080/
Agent Playground: http://localhost:8080/agent
MCP Tools: http://localhost:8080/mcp/tools
WebSocket: ws://localhost:3000/mcp/ws
API Gateway: http://localhost:8080/api/tasks/TaskService/Create
You: "Create a task to fix the login bug and assign it to alice"
Claude: [calls tasks.TaskService.Create with {"title": "Fix login bug", ...}]
Created task-1: "Fix login bug" assigned to alice.
You: "What tasks does alice have?"
Claude: [calls tasks.TaskService.List]
Alice has 1 task: "Fix login bug" (status: todo)
You: "Mark it as in progress"
Claude: [calls tasks.TaskService.Update with {"id": "task-1", "status": "in_progress"}]
Updated task-1 to "in_progress".
Use with WebSocket Clients
For real-time bidirectional communication (e.g., streaming agent frameworks):
constws=newWebSocket("ws://localhost:3000/mcp/ws",{headers:{"Authorization":"Bearer <token>"}});// JSON-RPC 2.0 over WebSocket
ws.send(JSON.stringify({jsonrpc:"2.0",id:1,method:"tools/list",params:{}}));
Step 6: Test Your Tools
Use the CLI to verify tools work:
# List all available toolsmicro mcp list
# Test a specific toolmicro mcp test tasks.TaskService.Create
# Generate documentationmicro mcp docs
# Export for LangChainmicro mcp export --format langchain
Step 7: Add Observability (Optional)
Enable OpenTelemetry tracing to see every agent tool call as a distributed trace:
Trace context is propagated downstream via metadata headers (Mcp-Trace-Id, Mcp-Tool-Name, Mcp-Account-Id), so you get full distributed traces from agent through gateway to service.
Step 8: Use the AI Package (Optional)
If your service needs to call AI models directly:
import("go-micro.dev/v6/ai"_"go-micro.dev/v6/ai/anthropic")m:=ai.New("anthropic",ai.WithAPIKey(os.Getenv("ANTHROPIC_API_KEY")),)resp,err:=m.Generate(ctx,&ai.Request{Prompt:"Summarize these tasks: "+taskJSON,SystemPrompt:"You are a project manager assistant",})
Checklist
Before shipping an AI-native service:
Every handler method has a doc comment explaining what it does
Every method has an @example tag with realistic JSON input
Request struct fields have description tags
Write/delete operations have auth scopes
You’ve tested with micro mcp test to verify tools work
You’ve tested with Claude Code or the agent playground
What Happens Under the Hood
1. You write Go comments on handler methods
2. micro registers the handler and extracts docs via go/ast
3. Docs are stored in the service registry as endpoint metadata
4. MCP gateway discovers services via the registry
5. Gateway generates JSON Schema tools with descriptions
6. AI agents query the tools endpoint and see rich descriptions
7. Agents call tools via JSON-RPC, gateway routes to your handler
The Go Micro CLI provides two gateway modes for accessing your microservices: development (micro run) and production (micro server). Both use the same underlying gateway architecture, ensuring consistent behavior across environments.
# Create and run a servicemicro new myservice
cd myservice
micro run
Open http://localhost:8080 - no login required!
What You Get
Instant Gateway: HTTP API at /api/{service}/{method}
Web Dashboard: Browse and test services at /
Hot Reload: Code changes trigger automatic rebuild
Authentication: JWT auth with default credentials (admin/micro)
Scopes: Endpoint access control via /auth/scopes
Example Usage
# Start with hot reloadmicro run
# Log in at http://localhost:8080 with admin/micro# Or use a token for API calls:curl -X POST http://localhost:8080/api/myservice/Handler.Call \
-H "Authorization: Bearer <token>"\
-d '{"name": "World"}'
# Start your services separately (e.g., via systemd, docker)./myservice &# Start the gatewaymicro server --address :8080
Open http://localhost:8080 and log in with admin/micro.
What You Get
API Gateway: Secure HTTP endpoint for all services
JWT Authentication: Token-based access control
Web Dashboard: Service management UI with login
User Management: Create users and API tokens
Endpoint Scopes: Fine-grained access control per endpoint
Production Ready: Designed for deployed environments
Authentication
All API calls require an Authorization header:
# Get a token (via web UI or login endpoint)TOKEN="eyJhbGc..."# Call a service with authcurl -X POST http://localhost:8080/api/myservice/Handler.Call \
-H "Authorization: Bearer $TOKEN"\
-d '{"name": "World"}'
Managing Users, Tokens & Scopes
Log in: Visit http://localhost:8080 → Enter admin/micro
Create API Token: Go to /auth/tokens → Generate token with scopes
Set Endpoint Scopes: Go to /auth/scopes → Restrict which endpoints require which scopes
Use Token: Copy and use in Authorization: Bearer <token> header
When to Use
Production deployments
Staging environments
Multi-team access (with auth)
Public-facing APIs (with security)
Gateway Features (Both Modes)
Both commands provide the same core gateway capabilities:
1. HTTP to RPC Translation
The gateway automatically converts HTTP requests to RPC calls:
POST /api/{service}/{method}Content-Type: application/json
{"field": "value"}
Becomes an RPC call to:
Service: {service}
Method: {method}
Payload: {"field": "value"}
2. Service Discovery
The gateway queries the registry (mdns, consul, etcd) to find services:
# List all servicescurl http://localhost:8080/services
# Returns:[{"name": "myservice", "endpoints": ["Handler.Call", "Handler.List"]},
{"name": "users", "endpoints": ["Users.Create", "Users.Get"]}]
Services register automatically when they start - no manual configuration needed!
3. Web Dashboard
Visit / in your browser to:
Browse all registered services
See available endpoints with request/response schemas
Test endpoints with auto-generated forms
View service health and status
Read API documentation
4. Health Checks
# Aggregate health of all servicescurl http://localhost:8080/health
# Kubernetes-style probescurl http://localhost:8080/health/live # Is gateway alive?curl http://localhost:8080/health/ready # Are services ready?
5. Dynamic Updates
The gateway automatically picks up:
New services registering
Services going offline
Endpoint changes
Version updates
No gateway restart needed!
6. Endpoint Scopes
Scopes provide fine-grained access control over which tokens can call which endpoints. Both micro run and micro server support scopes.
Set up endpoint scopes:
Visit /auth/scopes to see all discovered endpoints
Set required scopes for endpoints (e.g., billing on payments.Payments.Charge)
Use Bulk Set to apply scopes to all endpoints matching a pattern (e.g., greeter.*)
Create scoped tokens:
Visit /auth/tokens and create a token with matching scopes
A token with scope billing can call endpoints that require billing
A token with scope * bypasses all scope checks
Endpoints with no scopes set are open to any authenticated token
Scopes are enforced on all call paths:
Direct API calls (/api/{service}/{endpoint})
MCP tool calls (/mcp/call)
Agent playground tool invocations
The gateway uses auth.Account from the go-micro framework. The account’s Scopes field carries the same []string used by the framework’s wrapper/auth package for service-level auth.
Architecture Benefits
Why Unified?
Previously, micro run and micro server had separate gateway implementations. This caused:
❌ Duplicated code (hard to maintain)
❌ Feature lag (improvements didn’t benefit both)
❌ Inconsistent behavior between dev and prod
The unified gateway means:
✅ Single codebase for both commands
✅ Identical HTTP API in dev and production
✅ New features benefit both modes automatically
✅ Easier testing and maintenance
What Changed for Users?
From a user perspective:
micro run and micro server both have auth enabled
Both use the same JWT authentication and scopes system
API endpoints are unchanged
Web UI is identical
The unification is internal - your code keeps working.
Common Patterns
Local Development → Production
# 1. Develop locally without authmicro run
# Test: curl http://localhost:8080/api/...# 2. Build for productiongo build -o myservice
# 3. Deploy services./myservice &# or via systemd, docker, k8s# 4. Start gateway with authmicro server
# 5. Generate API token (via web UI)# Use token in production API calls
Multi-Service Development
# micro.muservice api
path ./api
port 8081service worker
path ./worker
port 8082 depends api
service web
path ./web
port 8090 depends api worker
# Start all with gatewaymicro run
Only micro server needs public access - services can be internal.
Programmatic Usage
You can also use the gateway in your own Go code:
packagemainimport("context""log""go-micro.dev/v6/cmd/micro/server""go-micro.dev/v6/store")funcmain(){// Start gateway with custom optionsgw,err:=server.StartGateway(server.GatewayOptions{Address:":9000",AuthEnabled:true,// Enable authenticationStore:store.DefaultStore,Context:context.Background(),})iferr!=nil{log.Fatal(err)}log.Printf("Gateway running on %s",gw.Addr())// Block until context is cancelledgw.Wait()}
This gives you full control over gateway configuration in custom deployments.
Troubleshooting
Gateway starts but no services show
Problem: http://localhost:8080 shows empty service list
Solution:
Check services are running: ps aux | grep myservice
Verify registry: services must register via mdns/consul/etcd
Check logs: ~/micro/logs/ for service startup errors
Use this guide when an agent surprises you: it answered without using a service,
called the wrong endpoint, looped, lost memory, refused a tool, or behaved
differently when a flow handed work to it. The local inner loop is:
micro run # start services, agents, gateway, dashboardmicro chat # reproduce one turnmicro inspect ... # read the recorded run or workflow history
Debug the lifecycle in the same order Go Micro runs it: first prove the service is
registered and callable, then inspect the agent run that chose tools, then inspect
any workflow that handed off to the agent.
Use the recovery command that matches where you are in the first-agent journey:
Checkpoint
When to use it
Command
Install troubleshooting
micro is not installed, not on PATH, or the shell cannot run it.
The first-agent loop stalled and you want the short scaffold → run → chat → inspect checklist before reading this full guide.
micro agent quickcheck (alias: micro agent debug)
Preflight before micro run
You have not started the local runtime yet and want to verify Go, CLI, provider-key, and gateway-port prerequisites.
micro agent preflight
Doctor after micro run
micro run is active, but chat, the /agent gateway, agent registration, provider settings, or inspect/run history is not behaving.
micro agent doctor
micro agent quickcheck is the quickest breadcrumb when you are unsure where the first-agent path failed: it prints the preflight, run, doctor, inspect, and no-secret fallback commands in one place. micro agent preflight is read-only and runs before the first local run; failed
checks include Fix: and Next: lines for Go, CLI installation, provider-key
setup, and the local gateway port. Once micro run is already up, switch to
micro agent doctor so the recovery output follows the live gateway, chat
settings, registered agents, provider configuration, and inspectable run history.
1. Reproduce one small turn
Start from the application directory and keep the prompt narrow enough that you
can tell which tool should have run:
micro run
micro chat --prompt "Create a ticket for Pat, then list open tickets."
For a live provider, make the provider choice explicit so a later retry uses the
same model boundary:
MICRO_AI_PROVIDER=anthropic \
ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY"\
micro chat --prompt "Create a ticket for Pat, then list open tickets."
If the provider supports streaming, turn it on while you reproduce the issue:
micro chat --provider anthropic --stream
Streaming shows the final answer as it arrives. Tool execution still goes through
the same agent run and is visible through inspection after the turn completes.
2. Prove the service side before blaming the model
Agents only call tools that the runtime can discover and describe. Check the
service boundary first:
If the service is missing, restart the service under micro run and verify it is
using the same registry as the agent. If the direct micro call fails, fix the
handler, request shape, or auth error there before debugging prompts.
When the agent calls the wrong tool or sends the wrong fields, improve the tool
description at the service source:
// Create opens a customer support ticket and returns its stable ticket ID.// @example {"customer":"Pat","subject":"Cannot log in"}func(s*TicketService)Create(ctxcontext.Context,req*CreateRequest,rsp*CreateResponse)error{
Endpoint comments, request field names, description tags, and @example blocks
are the model’s map of your service. A vague handler comment often looks like a
reasoning failure from the outside.
3. Inspect agent run history
After a chat turn, list recent runs for that agent:
micro inspect agent support
The output shows the run id, status, number of recorded events, the last event,
errors, and a short trace id when tracing is configured. Narrow the list while you
iterate:
micro inspect agent support --limit 5micro inspect agent support --status timeout
micro inspect agent support --trace abc123
micro inspect agent support --json
Useful statuses include done, refused, timeout, rate_limited, canceled,
and error. Use --json when you want exact timestamps, trace/span ids, and error
kinds for a bug report.
When a run is paused at stage=input-required, continue it from the CLI and then
inspect the completed checkpoint without writing a Go helper:
micro agent resume-input support <run-id> --input "Approve deploy to us-east-1"micro inspect agent support --limit 1
Run timelines are stored in the agent’s state store under that agent’s scoped
state (agent/<name>/runs/...). The persisted timeline is recorded even without
an OpenTelemetry exporter, so micro inspect agent remains useful in local
no-secret development.
Provider-free quickcheck: if you want to verify the documented inspect path
before involving a live model, run the same smoke check CI uses:
go test ./internal/harness/zero-to-hero-ci -run TestNoSecretFirstAgentDebuggingSmoke -count=1
That test seeds a local assistant run history and memory transcript, then runs
micro inspect agent assistant --limit 1, micro inspect agent --status done --json assistant, and micro agent history assistant with provider credentials
cleared.
4. See tool calls as they happen
When you are embedding an agent in Go and need live tool visibility, use the
streaming API instead of waiting for the final answer:
stream,err:=agent.StreamAsk(ctx,ag,"Create a ticket for Pat")iferr!=nil{returnerr}for{ev,err:=stream.Recv()iferr!=nil{break}switchev.Type{caseagent.StreamEventToolStart:log.Printf("tool start: %s %#v",ev.ToolCall.Name,ev.ToolCall.Input)caseagent.StreamEventToolEnd:log.Printf("tool end: %s %#v",ev.ToolCall.Name,ev.Result)caseagent.StreamEventToken:fmt.Print(ev.Token)}}
For custom audit logging, wrap the tool execution boundary. Wrappers observe every
call and result, including guardrail refusals:
Use this when you need request/response payloads in your own logs. By default,
Go Micro records safe run metadata; raw prompt input is not persisted unless the
agent is configured with agent.TraceInputs(true).
5. Inspect memory and plans
Default agent memory is store-backed and scoped to the agent name. A restarted
agent with the same micro.WithStore(...) and name reloads conversation history
from the history key in agent/<name> state. If you pass micro.WithMemory(...),
you own that backend; if you pass agent.NewInMemory(...), memory disappears on
restart.
The built-in plan tool also saves the current plan to the same scoped agent
state, so a later turn can pick up the saved plan. When memory does not persist,
check that all of these are stable across restarts:
the agent name (micro.NewAgent("support", ...)),
the configured store backend (micro.WithStore(...) or the process default),
whether a custom in-memory Memory implementation replaced the default,
whether compaction/retrieval limits are intentionally hiding older turns from
the active model context.
6. Inspect workflow handoffs
If a flow triggered the agent, inspect the flow too. The flow history tells you
which durable stage dispatched to the agent and whether a run is still pending:
The older flow-specific command remains available for listing runs:
micro flow runs intake
Use the flow run id and the agent run id together when debugging handoffs: the
flow explains why work started and where it checkpointed; the agent run explains
which model/tool steps happened after the handoff.
7. Add traces when metadata is not enough
For local CLI debugging, micro inspect is the fastest path. For production or
multi-service debugging, configure an OpenTelemetry tracer provider on the agent:
Trace ids flow into the recorded run summaries, so you can pivot between
micro inspect agent support --trace <prefix> and your trace backend. Keep
agent.TraceInputs(true) off unless your observability backend is approved to
store prompt content.
Troubleshooting table
Symptom
What to inspect
Common fix
Agent answers without calling a service
micro services, direct micro call, then micro inspect agent <name>
Register the service, include it in micro.AgentServices(...), or improve endpoint comments and examples.
Agent loops or burns steps
micro inspect agent <name> --status error and wrapper logs
Add or lower micro.AgentMaxSteps(...) / micro.AgentLoopLimit(...); move predictable work into a flow.
Redact secrets and user data. If you enabled agent.TraceInputs(true), inspect the
JSON before sharing it because prompts may be present.
4.16 - Deployment Guide
This is a quick reference for deploying go-micro services. For the full guide, see the Deployment documentation.
Workflow
micro run → Develop locally with hot reload
micro build → Compile production binaries
micro deploy → Push to a remote Linux server via SSH + systemd
micro server → Optional: production web dashboard with auth
Quick Start
# Build binaries for Linuxmicro build --os linux
# Deploy to server (builds automatically if needed)micro deploy user@your-server
First-Time Server Setup
On your server (any Linux with systemd):
curl -fsSL https://go-micro.dev/install.sh | sh
sudo micro init --server
This creates /opt/micro/{bin,data,config} and a systemd template for managing services.
Deploy
micro deploy user@your-server
This builds for linux/amd64, copies binaries to /opt/micro/bin/, configures systemd services, and verifies they’re running.
See the Deployment documentation for complete details including SSH setup, environment variables, security best practices, and troubleshooting.
4.17 - Error Handling for AI Agents
Error Handling for AI Agents
When AI agents call your services through MCP, they need to understand errors well enough to recover or inform the user. This guide covers how to write services that give agents useful error information.
Use Typed Errors
Go Micro’s errors package provides structured errors that the MCP gateway forwards to agents with status codes and detail messages.
import"go-micro.dev/v6/errors"func(s*Users)Get(ctxcontext.Context,req*GetRequest,rsp*GetResponse)error{ifreq.ID==""{returnerrors.BadRequest("users.Get","id is required")}user,err:=s.db.FindUser(req.ID)iferr!=nil{returnerrors.NotFound("users.Get","user %s not found",req.ID)}rsp.User=userreturnnil}
Agents receive structured error responses like:
{"error":{"id":"users.Get","code":404,"detail":"user abc-123 not found","status":"Not Found"}}
This gives the agent enough context to decide: retry with a different ID, ask the user, or report the problem.
Error Types and When to Use Them
Error
Code
Use When
errors.BadRequest
400
Missing or invalid input — agent should fix the request
errors.Unauthorized
401
Missing auth — agent needs credentials
errors.Forbidden
403
Insufficient permissions — agent can’t do this
errors.NotFound
404
Resource doesn’t exist — agent should try something else
errors.Conflict
409
Duplicate or version conflict — agent should retry or adjust
errors.InternalServerError
500
Server bug — agent should report to user, don’t retry
Write Error Messages for Agents
Error messages should tell the agent what went wrong and what to do about it.
Agents can’t recover from these — they don’t know what’s wrong.
Good: Actionable Errors
returnerrors.BadRequest("users.Create","email is required — provide a valid email address")returnerrors.BadRequest("users.Create","email '%s' is already registered — use a different email",req.Email)returnerrors.NotFound("users.Get","no user with id '%s' — use users.List to find valid IDs",req.ID)
The agent now knows exactly what to fix or which tool to call next.
Validation Patterns
Validate inputs at the top of your handler before doing any work:
// CreateOrder places a new order for a user. The user must exist// and at least one item is required.//// @example {"user_id": "u-1", "items": [{"product_id": "p-1", "quantity": 1}]}func(s*Orders)CreateOrder(ctxcontext.Context,req*CreateRequest,rsp*CreateResponse)error{// Validate required fieldsifreq.UserID==""{returnerrors.BadRequest("orders.CreateOrder","user_id is required")}iflen(req.Items)==0{returnerrors.BadRequest("orders.CreateOrder","at least one item is required")}// Validate each itemfori,item:=rangereq.Items{ifitem.ProductID==""{returnerrors.BadRequest("orders.CreateOrder","item[%d].product_id is required",i)}ifitem.Quantity<=0{returnerrors.BadRequest("orders.CreateOrder","item[%d].quantity must be positive, got %d",i,item.Quantity)}}// All validations passed — do the work// ...}
Document Error Cases
Tell agents what errors to expect in your doc comments:
// Transfer moves funds between two accounts. Both accounts must exist// and the source account must have sufficient balance.// Returns an error if the source balance is too low.//// @example {"from": "acc-1", "to": "acc-2", "amount": 100}func(s*Accounts)Transfer(ctxcontext.Context,req*TransferRequest,rsp*TransferResponse)error{
The description “returns an error if the source balance is too low” helps agents anticipate failure modes and plan accordingly.
Don’t Expose Internal Details
Agents (and the users they serve) shouldn’t see stack traces, database errors, or internal paths.
// Bad — leaks internalsreturnfmt.Errorf("pq: duplicate key value violates unique constraint \"users_email_key\"")// Good — clear message, no internalsreturnerrors.Conflict("users.Create","a user with email '%s' already exists",req.Email)
Idempotency for Retries
Agents may retry failed operations. Design critical operations to be idempotent:
// CreateOrUpdate upserts a config value. Safe to call multiple times// with the same key — it will create on first call, update on subsequent calls.//// @example {"key": "theme", "value": "dark"}func(s*Config)CreateOrUpdate(ctxcontext.Context,req*SetRequest,rsp*SetResponse)error{
When an operation is naturally idempotent, say so in the doc comment. Agents will learn they can safely retry.
Dapr is a distributed application runtime. Its building
blocks cover service invocation, state, pub/sub, bindings, secrets,
configuration, distributed locks, actors, jobs, and workflow, usually accessed
through a sidecar from many languages. Dapr Agents
adds an agent framework on top of those runtime capabilities.
Go Micro overlaps with Dapr on distributed-systems primitives, but the product
shape is different: Go Micro is a Go framework where services, agents, tools,
and flows are built from the same runtime. A service endpoint can become an
AI-callable tool, and an agent is itself a registered service with memory,
guardrails, planning, delegation, MCP, and A2A around it.
Decision table
Need
Prefer Go Micro
Prefer Dapr
Use both
Primary language
Your core runtime is Go and you want library-native APIs
You run a polyglot estate and want one sidecar API across languages
Go services use Go Micro while non-Go services expose Dapr APIs
Agent model
Agents should be ordinary services: registered, discoverable, callable by RPC, MCP, and A2A
Agents are primarily Python applications using Dapr Agents
Dapr-hosted agents call Go Micro MCP tools, or Go Micro agents call Dapr-backed services
Tools
Existing service endpoints should become tools with minimal extra code
Tools are modeled through Dapr components, bindings, or agent framework code
Use Dapr components behind Go Micro services that expose a stable tool surface
Workflows
Deterministic steps should live beside Go services and agents in the same codebase
You want Dapr Workflow’s sidecar-backed orchestration model across languages
Let Dapr own cross-language workflows and let Go Micro own Go-native agent/tool execution
State and pub/sub
You want Go interfaces and pluggable packages directly in-process
You want component YAML and sidecar portability across backing services
Put portable infrastructure behind Dapr and domain/tool logic in Go Micro
Deployment
You want a simple Go binary/runtime first, with Kubernetes support as an explicit deployment target
You are already standardized on Dapr sidecars in Kubernetes
Run Go Micro services in clusters that already have Dapr for shared infrastructure
Interop
MCP and A2A are first-class requirements for exposing services and agents
Dapr’s app APIs and agent framework are the integration boundary
Bridge through MCP/A2A at the agent edge and Dapr APIs at the infrastructure edge
When to choose Dapr
You need a polyglot runtime contract for Node, Python, Java, .NET, Go, and
other services.
Your platform team already operates sidecars and component configuration across
Kubernetes clusters.
You want Dapr’s standard building blocks for state, pub/sub, bindings, secrets,
actors, jobs, and workflow more than you want a Go-native service framework.
You are adopting Dapr Agents and want to stay in its Python-first agent stack.
When to choose Go Micro
You are building mostly in Go and want the agent harness to be the same runtime
as your services.
You want service methods and their comments/examples to become AI-callable tools
without maintaining a separate tool layer.
You want agents to be deployed, discovered, called, load-balanced, and inspected
like ordinary services.
You need MCP and A2A at the agent/service boundary, not only an internal
application API.
You prefer library-native composition and direct Go interfaces over sidecar
component wiring.
Where Go Micro still needs to prove itself
Dapr has a mature platform narrative and broad deployment footprint. Go Micro’s
agent-harness story is sharper for Go teams, but production adoption depends on
keeping the no-secret getting-started path green, documenting durability
semantics clearly, proving MCP/A2A conformance with external clients, and making
Kubernetes deployment first-class.
Practical migration path
Start with one Go Micro service that wraps a real domain capability.
Add doc comments and examples so the endpoint is useful as an agent tool.
Expose it through MCP for external agents or through A2A if the capability is
itself an agent.
If your platform already uses Dapr, keep Dapr components behind the service
boundary and let Go Micro present the agent/tool contract.
Move deterministic multi-step work into flows only after the service/tool
boundary is stable.
vs Agent Frameworks (Google ADK)
ADK (Agent Development Kit) is Google’s open-source, code-first
framework for building AI agents. It spans several languages (Python, TypeScript,
Go, Java, Kotlin); adk-go is the Go
implementation. It’s model-agnostic (optimized for Gemini), speaks MCP and A2A,
and supports multi-agent systems, evaluation, and deployment to Cloud Run / GKE.
They overlap on agents but solve different problems. ADK is a library for building
an agent process — you define an agent, its tools, and a model, then run and deploy
it. Go Micro is the harness around agents once they operate real systems: service
discovery, inter-service RPC, pub/sub, durable flows, tool execution, and deployment.
Those pieces are out of scope for ADK, and you bring your own.
In Go Micro an agent is built as an ordinary service: it registers in the registry,
is callable by RPC (Agent.Chat) and over A2A, and other services and agents
discover and call it the same way they call anything else. Its endpoints are exposed
as MCP tools automatically. So once you have more than one agent or service, Go Micro
also gives you the discovery, RPC, pub/sub, config, and deployment around them.
Go Micro
Google ADK
Primary unit
A harnessed service (an agent is a service with an LLM inside)
An agent
Service discovery / registry
Built-in (mDNS, Consul, etcd)
Not in scope
Inter-service RPC, load balancing, pub/sub
Built-in
Not in scope
MCP
Every service endpoint is automatically an MCP tool (no extra code)
MCP tools, wired explicitly
A2A
Agents are A2A-reachable services
Supported
Deterministic orchestration
Flows
Graph workflows
Multi-agent
Agents discover & call each other via the registry; plan/delegate built in
Composition, routing, workflow patterns
Evaluation suite
Harnesses/conformance today; first-class evaluation is a gap
Yes (criteria, user/env simulation, metrics)
Context engineering
Store-backed memory
“Context as source code” (auto filter/summarize/token tracking)
Languages
Go
Python, TypeScript, Go, Java, Kotlin
Backing
Community
Google
When to choose ADK
You want an agent framework with first-class evaluation and context tooling
You’re polyglot, or invested in the Google Cloud / Gemini ecosystem
You want a cross-language A2A ecosystem with Google’s backing
When to choose Go Micro
You want an agent harness where agents and services are the same thing —
registered, discoverable, load-balanced, and deployed the same way
You want your existing services to become agent tools with zero extra code
(every endpoint is an MCP tool automatically)
You’re building in Go and want one set of primitives for services, agents, and flows
They interoperate
Both speak MCP and A2A, so this isn’t strictly either/or: a Go Micro agent
and an ADK agent (in any language) can call each other over A2A, and either can
consume the other’s MCP tools. A common pattern is to run Go Micro as the service
mesh / runtime and let ADK (or any A2A agent) plug into it.
vs tRPC-Agent-Go
tRPC-Agent-Go (maintained by tRPC-Group,
validated inside Tencent) is a production-grade Go framework for agent systems:
LLM / Chain / Parallel / Cycle / Graph agents, function tools, MCP, A2A, AG-UI, Redis
memory and RAG, evaluation, agent self-evolution, and OpenTelemetry. It’s a serious,
well-resourced project.
They overlap heavily on agents but take a different approach. tRPC-Agent-Go is an agent
SDK you run alongside your services — you compose agents and tools into graphs and
conditional workflows, and your microservices (tRPC) live separately and are called
into. Go Micro starts from the premise that an agent is a service — one runtime
where every endpoint is automatically a tool, an agent registers and is discovered and
load-balanced like anything else, and workflows are durable code paths rather than a
graph DSL. The premise is that the line between “your services” and “your agents” is
accidental complexity; remove it and there’s less to wire and keep in sync.
Go Micro
tRPC-Agent-Go
Primary unit
A harnessed service (an agent is a service with an LLM inside)
Built in — agents are services (registry, RPC, load balancing, pub/sub)
Runs alongside your existing service stack (tRPC)
MCP / A2A
Both, generated from the registry
Both
Evaluation / self-evolution
Verification loop on the roadmap; not yet first-class
First-class today
Memory / RAG
Store-backed memory (Postgres, NATS KV, file); RAG on the roadmap
In-memory / Redis memory; RAG today
Observability
OpenTelemetry run timelines, micro runs
OpenTelemetry, Langfuse examples
Backing
Independent, community
tRPC-Group / Tencent
When to choose tRPC-Agent-Go
You want a graph/workflow DSL for composing agents and tools
You’re on tRPC, or want to add agents alongside an existing service stack
You want first-class evaluation and self-evolution today, with a large team behind it
When to choose Go Micro
You want one runtime where services, agents, and flows are the same primitives —
registered, discoverable, and deployed the same way
You want your existing services to become agent tools with zero extra code
You prefer durable flows and plain code paths over a graph DSL, in a small,
independent framework you can hold in your head
They interoperate
Both speak MCP and A2A, so a Go Micro agent and a tRPC-Agent-Go agent can call
each other over A2A, and either can consume the other’s MCP tools. You can run Go Micro
as the service-and-agent runtime and still reach an agent built on tRPC-Agent-Go.
Feature Deep Dive
Service Discovery
Go Micro: Built-in with plugins
// Zero-config for devsvc:=micro.NewService("myservice")// Consul for productionreg:=consul.NewRegistry()svc:=micro.NewService("myservice",micro.Registry(reg))
go-kit: Bring your own
// You implement service discovery// Can be 100+ lines of code
gRPC: No built-in discovery
// Use external solution like Consul// or service mesh like Istio
health.Register("disk",health.CustomCheck(func()error{varstatsyscall.Statfs_tiferr:=syscall.Statfs("/",&stat);err!=nil{returnerr}freeGB:=stat.Bavail*uint64(stat.Bsize)/1e9iffreeGB<1{returnfmt.Errorf("low disk space: %dGB free",freeGB)}returnnil}))
RegistryCheck
Verifies the service registry is still reachable. A go-micro service can keep running while it has silently lost its connection to the registry (etcd, Consul, …) — the process looks healthy, but other services can no longer discover it. RegistryCheck surfaces that state so a readiness probe can take the pod out of rotation.
The check lists services under the configured probe timeout, so an unreachable registry is reported as down rather than hanging the probe. It works with any registry implementation — the connectivity is exercised through the standard ListServices call.
Critical vs Non-Critical Checks
By default, all checks are critical. A critical check failure marks the service as not ready.
When using micro run with a micro.mu config that specifies ports, the runner waits for /health to return 200 before starting dependent services:
service database
path ./database
port 8081
service api
path ./api
port 8080
depends database
The api service won’t start until database’s /health endpoint is ready.
Programmatic Usage
// Check readiness in codeifhealth.IsReady(ctx){// Service is healthy}// Get full health statusresp:=health.Run(ctx)fmt.Printf("Status: %s\n",resp.Status)for_,check:=rangeresp.Checks{fmt.Printf(" %s: %s (%v)\n",check.Name,check.Status,check.Duration)}
Best Practices
Keep checks fast - Health endpoints are called frequently
Use timeouts - Don’t let slow dependencies block health checks
Non-critical for optional deps - External APIs, caches that have fallbacks
Critical for required deps - Databases, message queues
Include version info - Helps debugging in production
4.20 - Install troubleshooting
Install troubleshooting
Use this page before micro new or micro agent demo when the CLI install is
unclear. The goal is to prove three boundaries in order: the micro binary is on
PATH, it is the version you expected, and the no-secret first-run path works
without provider keys.
1. Choose one install path
Binary installer (no Go required to install)
curl -fsSL https://go-micro.dev/install.sh | sh
Use this when you want the released micro binary without building it yourself.
The generated services still need a Go toolchain when you run micro run, but the
installer itself does not require Go.
Go install (build from source)
go install go-micro.dev/v6/cmd/micro@latest
Use this when Go is already installed and you want the binary in your Go bin
directory. If the command succeeds but micro is not found, your Go bin directory
is probably not on PATH.
2. Verify PATH and version
Check which binary your shell will run:
command -v micro
micro --version
If command -v micro prints nothing, add the install directory to PATH, then
open a new terminal and retry. Common locations are:
exportPATH="$HOME/.micro/bin:$PATH"# binary installerexportPATH="$(go env GOPATH)/bin:$PATH"# go install
If micro --version shows an older binary than expected, remove the stale copy or
put the intended install directory earlier in PATH.
3. Run the no-secret smoke path
Once micro resolves, prove the local service runtime before adding LLM provider
keys:
micro new helloworld
cd helloworld
micro run
In another terminal:
curl -X POST http://localhost:8080/api/helloworld/Helloworld.Call \
-H 'Content-Type: application/json' -d '{"name":"World"}'
This checks the scaffold, local build, gateway, and service registration without
calling a model provider.
4. Recover common failures
Symptom
Check
Fix
micro: command not found
command -v micro
Add the installer bin directory or $(go env GOPATH)/bin to PATH, then open a new terminal.
Prevent abuse with per-tool rate limiting using a token bucket algorithm:
mcp.ListenAndServe(":3000",mcp.Options{Registry:reg,RateLimit:&mcp.RateLimitConfig{RequestsPerSecond:10,// Sustained rateBurst:20,// Allow bursts up to 20},})
When the rate limit is exceeded, calls return 429 Too Many Requests.
Choosing Rate Limits
Service Type
Requests/sec
Burst
Rationale
Read-heavy API
50
100
High throughput, low cost
Write API
10
20
Moderate, prevents spam
Expensive operation
2
5
Protect downstream resources
Internal tool
100
200
Trusted callers, higher limits
Audit Logging
Record every tool call for compliance, debugging, and analytics:
Each tool call creates a span (mcp.tool.call) with these attributes:
Attribute
Example
mcp.tool.name
tasks.TaskService.Create
mcp.transport
http, websocket, stdio
mcp.account.id
user-123
mcp.trace.id
a1b2c3d4-...
mcp.auth.allowed
true
mcp.auth.denied_reason
insufficient_scope
mcp.scopes.required
tasks:write
mcp.rate_limited
false
The gateway propagates W3C trace context downstream, so you get end-to-end traces from agent → gateway → service in Jaeger, Zipkin, or any OTel-compatible backend.
WebSocket Authentication
The WebSocket transport supports two authentication methods:
Common issues when using the MCP gateway and AI agents with Go Micro services.
Agent Can’t Find My Tools
Symptom: Agent says “no tools available” or doesn’t list your service endpoints.
Check 1: Is the service registered?
# List registered servicesmicro services
If your service isn’t listed, it hasn’t registered with the registry. Make sure your service is running and using the same registry as the MCP gateway.
Check 2: Is the MCP gateway discovering services?
# List tools the gateway seescurl http://localhost:3001/mcp/tools | jq
If empty, the gateway can’t reach the registry. Verify both use the same registry address.
Check 3: Are you using the right port?
The MCP gateway runs on its own port (default :3001 with WithMCP), separate from the service RPC port. Make sure you’re querying the MCP port, not the service port.
Tool Calls Return Errors
Symptom: Agent calls a tool but gets an error response.
“service not found”
The MCP gateway found the tool definition but can’t reach the service. The service may have stopped since the gateway cached its tools. Restart the service and try again.
“method not found”
The handler method name doesn’t match what the gateway expects. Ensure your handler is properly registered:
// Correct - registers all methods on the handlerservice.Handle(new(MyHandler))// Or with proto-generated codepb.RegisterMyServiceHandler(service.Server(),handler.New())
“unauthorized” or “forbidden”
Auth scopes are configured but the agent’s token doesn’t have the required scope. Check your scope configuration:
Verify the agent’s bearer token includes the required scopes.
“rate limited”
The agent is making too many requests. Adjust rate limits:
mcp.Options{RateLimit:&mcp.RateLimitConfig{RequestsPerSecond:100,// Increase if neededBurst:200,},}
Agent Makes Bad Tool Calls
Symptom: Agent calls tools with wrong parameters or misunderstands what a tool does.
This is almost always a documentation problem. Improve your handler doc comments:
// Bad - agent doesn't know what this doesfunc(s*Users)Get(ctxcontext.Context,req*GetRequest,rsp*GetResponse)error{// Good - agent understands purpose, parameters, and format// Get retrieves a user by their unique ID. Returns the full user profile// including email, display name, and account status.//// @example {"id": "user-123"}func(s*Users)Get(ctxcontext.Context,req*GetRequest,rsp*GetResponse)error{
Add description struct tags to your request/response types:
typeGetRequeststruct{IDstring`json:"id" description:"User ID in UUID format"`}
Check 3: Check for connection limits. Each WebSocket connection is persistent. If you have many agents, you may need to increase file descriptor limits.
Claude Code Can’t Connect
Symptom: Claude Code doesn’t see your MCP tools after configuring the server.
Check 1: Test stdio transport manually
# This should start and wait for JSON-RPC inputmicro mcp serve
If it errors, check that your services are running and the registry is accessible.
If AuditFunc is nil, no audit records are generated.
Performance Issues
Symptom: MCP tool calls are slow.
Check 1: Network round-trips
Each MCP tool call makes an RPC call to the underlying service. If the service is on a different host, network latency applies. Use micro mcp test to measure raw latency.
Check 2: Service discovery caching
The gateway caches service/tool metadata. If you’re seeing stale data, it’s because of caching. The cache refreshes periodically based on registry TTL.
Check 3: Rate limiting
If rate limits are too low, requests queue up. Check your rate limit configuration.
MCP Gateway - Optional dedicated MCP protocol listener via --mcp-address
Features
API Gateway
The gateway converts HTTP requests to RPC calls. All API calls require authentication:
# Log in at http://localhost:8080 with admin/micro to get a session# Or use a token for programmatic access:curl -X POST http://localhost:8080/api/helloworld/Say.Hello \
-H "Authorization: Bearer <token>"\
-d '{"name": "World"}'# Response{"message": "Hello World"}
Create tokens at /auth/tokens. The default admin token has * scope (full access).
Agent Playground
The agent playground at /agent lets you interact with your services using AI. Your services are automatically exposed as MCP (Model Context Protocol) tools — no configuration needed.
Open http://localhost:8080/agent
Configure your API key in Agent Settings (supports OpenAI and Anthropic)
Chat with the AI agent — it can discover and call your services as tools
The MCP tools API is available at:
/mcp/tools — list all services as AI-callable tools
/mcp/call — invoke a tool (service endpoint) by name
For a dedicated MCP protocol listener (for external AI clients), use:
micro run --mcp-address :3000
Hot Reload
By default, micro run watches for .go file changes and automatically rebuilds and restarts affected services.
micro run # Hot reload enabled (default)micro run --no-watch # Disable hot reload
Changes are debounced (300ms) to handle rapid saves from editors.
Configuration File
For multi-service projects, create a micro.mu file to define services, dependencies, and environments.
micro.mu (Recommended)
# Service definitions
service users
path ./users
port 8081
service posts
path ./posts
port 8082
depends users
service web
path ./web
port 8089
depends users posts
# Environment configurations
env development
STORE_ADDRESS file://./data
DEBUG true
env production
STORE_ADDRESS postgres://localhost/db
DEBUG false
Port the service listens on (enables health check waiting)
depends
No
Services that must start first (space-separated in .mu, array in .json)
Dependency Ordering
When depends is specified, services start in topological order:
Services with no dependencies start first
Each service waits for its dependencies to be ready
If a service has a port, we wait for /health to return 200
Circular dependencies are detected and reported as errors
Environment Management
micro run # Uses 'development' (default)micro run --env production # Uses 'production'micro run --env staging # Uses 'staging'MICRO_ENV=test micro run # Environment variable override
Environment variables from the config are injected into each service’s environment.
Graceful Shutdown
On SIGINT (Ctrl+C) or SIGTERM:
Services stop in reverse dependency order
SIGTERM is sent first (graceful)
After 5 seconds, SIGKILL if still running
PID files are cleaned up
Without Configuration
If no micro.mu or micro.json exists:
All main.go files are discovered recursively
Each is built and run
No dependency ordering
Hot reload still works
Logs
Service logs are written to:
Terminal: Colorized with service name prefix
File: ~/micro/logs/{service}-{hash}.log
View logs:
micro logs # List available logsmicro logs users # Show logs for 'users' service
Process Management
micro status # Show running servicesmicro stop users # Stop a specific service
Example: micro/blog
The micro/blog project demonstrates a multi-service setup:
# micro.mu
service users
path ./users
port 8081
service posts
path ./posts
port 8082
depends users
service comments
path ./comments
port 8083
depends users posts
service web
path ./web
port 8089
depends users posts comments
Run it:
micro run github.com/micro/blog
Options
micro run # Gateway on :8080, hot reloadmicro run --address :3000 # Custom gateway portmicro run --no-gateway # Services only, no HTTP gatewaymicro run --no-watch # Disable hot reloadmicro run --env production # Use production environmentmicro run --mcp-address :3000 # Enable MCP protocol gateway for AI clients
Tips
Browse First: Open http://localhost:8080 to explore your services
Try the Agent: Open http://localhost:8080/agent to chat with your services via AI
Port Configuration: Set port for services to enable health check waiting
Health Endpoint: Implement /health returning 200 for reliable startup sequencing
Environment Separation: Keep secrets in production env, use file:// paths for development
Hot Reload Scope: Only .go files trigger rebuilds; static assets don’t
4.24 - Native gRPC Compatibility
This guide explains how to make your Go Micro services compatible with native gRPC clients like grpcurl, grpcui, or clients generated by the standard protoc gRPC plugin in any language.
Understanding Transport vs Server
Go Micro has two different gRPC-related concepts that are often confused:
gRPC Transport (go-micro.dev/v6/transport/grpc)
The gRPC transport uses the gRPC protocol as a communication layer, similar to how you might use NATS, RabbitMQ, or HTTP. It does not guarantee compatibility with native gRPC clients.
// This uses gRPC as transport but is NOT compatible with native gRPC clientsimport"go-micro.dev/v6/transport/grpc"t:=grpc.NewTransport()service:=micro.NewService("helloworld",micro.Transport(t),)
When using the gRPC transport:
Communication between Go Micro services works fine
Native gRPC clients (grpcurl, etc.) will fail with “Unimplemented” errors
The protocol is used like a message bus, not as a standard gRPC server
gRPC Server/Client (go-micro.dev/v6/server/grpc and go-micro.dev/v6/client/grpc)
The gRPC server and client provide native gRPC compatibility. These implement a proper gRPC server that any gRPC client can communicate with.
// This IS compatible with native gRPC clientsimport("go-micro.dev/v6"grpcServer"go-micro.dev/v6/server/grpc"grpcClient"go-micro.dev/v6/client/grpc")service:=micro.NewService("helloworld",micro.Server(grpcServer.NewServer()),micro.Client(grpcClient.NewClient()),)
packagemainimport("context""log""go-micro.dev/v6"grpcServer"go-micro.dev/v6/server/grpc"pb"example.com/helloworld/proto")typeSaystruct{}func(s*Say)Hello(ctxcontext.Context,req*pb.Request,rsp*pb.Response)error{rsp.Message="Hello "+req.Namereturnnil}funcmain(){// Create service with gRPC server for native gRPC compatibility// Note: Server must be set before Name to ensure the name is applied to the gRPC serverservice:=micro.NewService("helloworld",micro.Server(grpcServer.NewServer()),micro.Address(":8080"),)service.Init()// Register handlerpb.RegisterSayHandler(service.Server(),&Say{})// Run serviceiferr:=service.Run();err!=nil{log.Fatal(err)}}
Client Implementation (Go Micro)
packagemainimport("context""fmt""log""go-micro.dev/v6"grpcClient"go-micro.dev/v6/client/grpc"pb"example.com/helloworld/proto")funcmain(){// Create service with gRPC clientservice:=micro.NewService("helloworld.client",micro.Client(grpcClient.NewClient()),)service.Init()// Create client - use the service name "helloworld" (not the proto package name)// Go Micro uses this name for registry lookup, which may differ from the package namesayService:=pb.NewSayService("helloworld",service.Client())// Call servicersp,err:=sayService.Hello(context.Background(),&pb.Request{Name:"Alice"})iferr!=nil{log.Fatal(err)}fmt.Println(rsp.Message)// Output: Hello Alice}
Testing with grpcurl
Once your service is running with the gRPC server, you can use grpcurl:
# List available servicesgrpcurl -plaintext localhost:8080 list
# Call the Hello methodgrpcurl -proto ./proto/helloworld.proto \
-plaintext \
-d '{"name":"Alice"}'\
localhost:8080 helloworld.Say.Hello
Using Both gRPC Server and Client Together
For full native gRPC compatibility (both inbound and outbound), use both:
// Transport (NOT native gRPC compatible)import"go-micro.dev/v6/transport/grpc"// Server (native gRPC compatible)import"go-micro.dev/v6/server/grpc"// Client (native gRPC compatible)import"go-micro.dev/v6/client/grpc"
Service Name vs Package Name
When creating a client to call another service, use the service name passed to micro.NewService, not the proto package name:
// If the server was started with micro.NewService("helloworld", ...)sayService:=pb.NewSayService("helloworld",service.Client())// Use service name// NOT the package name from the proto file// sayService := pb.NewSayService("helloworld.Say", service.Client()) // Wrong!
Go Micro uses the service name for registry lookup, which may differ from the proto package name.
Environment Variable Configuration
You can also configure the server and client via environment variables:
# Use gRPC serverMICRO_SERVER=grpc go run main.go
# Use gRPC clientMICRO_CLIENT=grpc go run main.go
Summary
Component
Import Path
Native gRPC Compatible
Transport
go-micro.dev/v6/transport/grpc
❌ No
Server
go-micro.dev/v6/server/grpc
✅ Yes
Client
go-micro.dev/v6/client/grpc
✅ Yes
For native gRPC compatibility with tools like grpcurl or polyglot clients, always use the gRPC server and client packages, not the transport.
This is the fastest first-agent success path when you do not have a provider key
handy. It starts from the maintained examples/support app and uses the
repository harness that CI already runs: real Go Micro services, registry,
broker, client, store, agent loop, flow handoff, and guardrail code with only the
LLM provider mocked.
Use it before the live-provider Your First Agent
walkthrough when you want to see the services → agents → workflows lifecycle run
end to end with no secrets.
What this proves
Services expose typed customers, tickets, and notify endpoints.
The support agent discovers those endpoints as tools and uses them to
triage a ticket.
The intake flow turns a ticket.created event into an agent run.
The approval gate intercepts the customer email action before the tool
executes.
Transcript
If you installed the CLI first, ask it for the no-secret path:
micro agent demo
From a fresh clone of the repository, first run the smallest service-backed agent:
git clone https://github.com/micro/go-micro.git
cd go-micro
go run ./examples/first-agent
Then run the maintained support-agent transcript that exercises the full lifecycle:
go run ./examples/support
The default provider is mock, so the command does not need ANTHROPIC_API_KEY,
OPENAI_API_KEY, or any other secret. A healthy run prints the event, service
calls, guardrail decision, and final support-agent reply in one terminal:
> event: events.ticket.created {"id":"ticket-1","customer":"alice@acme.com",...}
[customers] looked up Alice (pro plan)
[tickets] ticket-1 → priority=high status=in_progress
▣ approval gate notify_NotifyService_Send(alice@acme.com) — approved
[notify] 📨 to=alice@acme.com: "Hi Alice — thanks for reaching out..."
support agent: Hi Alice — thanks for reaching out...
✓ ticket triaged and the customer was replied to — triggered by an event
That single run is the no-secret version of the first-agent loop: a service
capability exists, an agent calls it as a tool, and workflow infrastructure can
trigger and inspect the work.
CI-backed check
Run the same deterministic paths as focused tests:
go test ./examples/first-agent -run TestRunFirstAgent -count=1go test ./examples/support -run TestRunSupportMockSmoke -count=1
For the broader no-secret contract that also checks scaffold, chat/inspect CLI
boundaries, flow history, deploy dry-run, and mock provider conformance, run:
make harness
Equivalent scaffold → run → chat → inspect path
When you are ready to build the smaller live-agent version yourself, follow
Your First Agent. The command shape is the same, but a
live micro chat turn needs a provider key because the model is no longer
mocked:
go test ./cmd/micro -run TestFirstAgentWalkthroughCLIBoundaries -count=1go test ./internal/harness/zero-to-hero-ci -run TestNoSecretFirstAgentDebuggingSmoke -count=1
Debug transcript checkpoint
A successful first chat turn should always leave an inspectable trail. After the
chat command finishes, continue the same terminal transcript with the inspection
and history commands before changing prompts or provider settings:
micro chat assistant --prompt "Triage ticket-1 for Alice"micro inspect agent assistant --limit 1micro agent history assistant
The inspection output is the checkpoint that the runnable loop did not stop at
chat: it should show a recent agent run with a status, event count, last event,
and trace breadcrumb when tracing is configured. micro agent history assistant
then confirms the conversation memory that future turns will reuse. If either
command is empty after a successful chat turn, keep the failing transcript and
use Debugging your agent to check provider failures, run
history, memory, and tool-call inspection before changing application code.
If micro agent preflight reports a missing provider key, you can still use this no-secret path because it runs against the mock model; the command now prints this guide as the next step for that failure. If chat behaves unexpectedly, continue to
Debugging your agent for provider checks, run history,
memory, and tool-call inspection.
4.26 - Payments (x402)
Go Micro can require a payment before a tool runs, using x402 — the open HTTP 402 Payment Required standard for stablecoin payments, designed for AI agents and onchain APIs. It lets every Go Micro endpoint, already exposed as an AI-callable tool, become a paid tool: a service answers a call with 402 and payment requirements, the client pays and retries, and the gateway verifies the payment before serving.
Payments are opt-in and dependency-light. Go Micro carries no chain or crypto code — it speaks the protocol and delegates verification and settlement to a pluggable facilitator (Coinbase CDP, Alchemy, or self-hosted), so Base and Solana are just different facilitators behind one interface.
The wrapper
The core is HTTP middleware in go-micro.dev/v6/wrapper/x402:
import"go-micro.dev/v6/wrapper/x402"pay:=x402.Middleware(x402.Config{PayTo:"0xYourAddress",// where payments go (required)Network:"base",// or "solana", ...Amount:"10000",// smallest units, e.g. 0.01 USDCFacilitatorURL:"https://facilitator.example",})mux.Handle("/paid",pay(handler))
A request with no X-PAYMENT header gets a 402 with the requirements; once a payment verifies through the facilitator, the request is served (with settlement details on the X-PAYMENT-RESPONSE header).
Pluggable facilitator
Config.Facilitator is an interface; the default is an HTTPFacilitator pointed at FacilitatorURL. Implement your own to target any chain or hosted service:
Because every endpoint is already an MCP tool, the gateway is where you charge. Payments are wired into both micro mcp serve and the standalone micro-mcp-gateway, gated on /mcp/call (listing tools and health stay free), and off unless you set a pay-to address.
When payments are enabled, /mcp/tools advertises each priced tool’s payment requirements, so an agent can see the cost before calling and choose by price — the catalog is shoppable, not just discoverable:
Free tools carry no payment block. This is the foundation for a tool marketplace: offering a tool is registering a priced service; using it is list → choose → call → pay.
Per-tool amounts
Different tools can cost different amounts. Pricing is an operator concern — the payTo address is the operator’s, and amounts change without redeploying anyone’s service — so it’s configured at the gateway with a file, the same way per-tool scopes and rate limits are. Point the gateway at an x402 config:
amount is the default (here "0" — free unless priced), and amounts sets per-tool overrides keyed by tool name. There is no “pricing” abstraction; it’s the x402 amount, resolved per tool, in the protocol’s own vocabulary. micro mcp serve accepts the file via --x402_config; the standalone gateway accepts the same file via --x402-config or the X402_CONFIG environment variable.
Paying for tools (the consumer side)
The counterpart to the server middleware is x402.Client — an HTTP client that settles 402 challenges automatically, up to a spend budget. This is the safety piece for an autonomous caller: it pays what a tool requires, but refuses (before paying) once a call would exceed the budget.
c:=&x402.Client{Payer:myWallet,// constructs the payment payload (signs with a wallet)Budget:1_000_000,// max total spend in the asset's smallest unit (0 = unlimited)}resp,err:=c.Do(req)// a 402 is paid and retried; over-budget calls error instead
Payer is an interface (Pay(ctx, Requirements) (payment string, error)) — the consumer counterpart to Facilitator. The budget accumulates across calls, so a long-running agent can be handed a fixed allowance for a task. Budget is reserved before payment is created, which means parallel paid calls cannot race past the cap; if payment creation or verification fails, the reservation is released.
Agent-level spend guardrail
For unattended agents, set the same cap at the agent tool-execution layer so paid tools are refused before their handler — and therefore before a payer — can run:
agent:=micro.NewAgent("buyer",micro.AgentMaxSteps(8),micro.AgentMaxSpend(20_000),// per Ask, smallest unitsmicro.AgentToolSpend("weather.Weather.Forecast",10_000),)
AgentMaxSpend is disabled by default (0). AgentToolSpend records the price discovered from your shoppable MCP/x402 catalog for the tools this agent may call. When a call would exceed the per-run allowance, the result is a normal structured guardrail refusal with Refused: "spend_budget" and an explanatory error in the run timeline/inspect output, distinct from provider/model failures.
Live facilitator conformance
The regular test suite uses in-process facilitators and does not need network credentials. To smoke-test a hosted facilitator, run the opt-in live conformance test with a real payment payload and matching requirements:
GO_MICRO_X402_LIVE_FACILITATOR_URL=https://facilitator.example \
GO_MICRO_X402_LIVE_PAYMENT='...'\
GO_MICRO_X402_LIVE_PAY_TO=0xYourAddress \
GO_MICRO_X402_LIVE_NETWORK=base \
GO_MICRO_X402_LIVE_AMOUNT=1\
go test ./wrapper/x402 -run TestLiveFacilitatorConformance -count=1
Leave those variables unset in normal CI; the live test skips unless the facilitator URL, payment payload, and pay-to address are all provided.
Notes
Opt-in. No pay-to address (and no config), no payments — nothing changes.
No crypto in the framework. The facilitator does verification and settlement on-chain; Go Micro speaks HTTP.
A paying agent needs a budget. Use AgentMaxSpend plus AgentToolSpend next to MaxSteps and ApproveTool so a run has an explicit allowance before any paid tool can execute.
AP2 can authorize an x402 payment without making A2A carry settlement state. A
payment mandate records the buyer intent and names an x402 rail reference; the
existing x402 facilitator remains responsible for payment verification and
settlement. This keeps AP2 as the signed mandate/audit layer while x402 stays the
pluggable payment rail.
4.27 - Plan & Delegate
Every Go Micro agent has two built-in capabilities, on top of the service tools it discovers:
plan — record an ordered plan in memory before doing multi-step work.
delegate — hand a self-contained subtask to another agent.
They are exposed to the model as ordinary tools. There is no separate graph runtime to configure — these harness capabilities are tools, and the agent calls them the same way it calls a service endpoint. They are added automatically to every agent, so you don’t wire anything up. micro chat exposes them too, so you get planning and delegation even when talking to your services directly.
Prerequisites
Go 1.24+
An API key for any supported provider (Anthropic, OpenAI, Gemini, Groq, Mistral, Together, Atlas Cloud)
exportANTHROPIC_API_KEY=sk-ant-...
Smallest possible agent
An agent doesn’t need any services to plan — plan and delegate are always available.
packagemainimport("context""fmt""os""go-micro.dev/v6")funcmain(){a:=micro.NewAgent("assistant",micro.AgentProvider("anthropic"),micro.AgentAPIKey(os.Getenv("ANTHROPIC_API_KEY")),)resp,err:=a.Ask(context.Background(),"Plan how to launch a product, then carry out what you can.")iferr!=nil{panic(err)}fmt.Println(resp.Reply)}
Save it in a fresh module and run:
mkdir my-agent &&cd my-agent
go mod init my-agent
go get go-micro.dev/v6
# save the code above as main.goexportANTHROPIC_API_KEY=sk-ant-...
go run main.go
The agent records its plan with the plan tool, then works through it. The plan is saved to the agent’s store-backed memory and shown back to it on later turns, so it stays oriented across a long task.
plan
The model calls plan with an ordered list of steps, each with a task and a status (pending, in_progress, done):
{"steps":[{"task":"draft the announcement","status":"in_progress"},{"task":"schedule the email","status":"pending"},{"task":"publish the blog post","status":"pending"}]}
The plan is persisted under agent/{name}/plan in the store — file-backed by default, Postgres or NATS KV in production — and re-injected into the system prompt on subsequent turns. Memory survives restarts.
You don’t have to do anything to enable this. Nudge the agent to use it from the prompt when you want disciplined multi-step behaviour:
micro.AgentPrompt("For multi-step requests, call the plan tool first to record your steps, then carry them out.")
delegate
delegate hands a self-contained subtask to another agent. It resolves delegate-first:
If to names a registered agent that owns the relevant services, the subtask is sent to it over RPC (Agent.Chat). The domain expert handles its own services.
Otherwise a focused, short-lived sub-agent is created for the subtask with a fresh, isolated context, asked the task, and torn down.
A sub-agent is just an agent — created with New, talked to with Ask. There is no separate “spawn” or “fork” concept to learn. Ephemeral sub-agents load and persist no history and have no built-in tools, so they can’t plan or re-delegate — which keeps delegation from recursing.
{"task":"Notify owner@acme.com that the launch plan is ready","to":"comms"}
This is how intelligence stays distributed: an agent doesn’t need to know how to do everything, only who does. It mirrors how Go Micro already works — agents are services, and services call each other over RPC.
A multi-agent example
Two services (task, notify) and two agents. The conductor owns task; comms owns notify. Asked to create tasks and notify someone, the conductor plans the work, creates the tasks with its own tools, then delegates the notification to comms — which, being a registered agent, receives the hand-off over RPC.
comms:=micro.NewAgent("comms",micro.AgentServices("notify"),micro.AgentPrompt("You handle outbound notifications."),micro.AgentProvider("anthropic"),micro.AgentAPIKey(key),)gocomms.Run()conductor:=micro.NewAgent("conductor",micro.AgentServices("task"),micro.AgentPrompt("For multi-step requests, call the plan tool first. "+"For notifications, delegate to the \"comms\" agent (to: \"comms\")."),micro.AgentProvider("anthropic"),micro.AgentAPIKey(key),)resp,_:=conductor.Ask(ctx,"Create three launch tasks: Design, Build, and Ship. "+"Then make sure owner@acme.com is notified that the launch plan is ready.")
A typical run:
→ plan({"steps":[{"task":"create Design task","status":"pending"}, ...]})
→ task_TaskService_Add({"title":"Design"})
→ task_TaskService_Add({"title":"Build"})
→ task_TaskService_Add({"title":"Ship"})
→ delegate({"task":"Notify owner@acme.com that the launch plan is ready","to":"comms"})
📨 notify: to=owner@acme.com message="The launch plan is ready"
The agent to stay on track over a long, multi-step task
plan
One domain expert to handle its own services
delegate with to set to that agent
A focused helper for a one-off subtask, with its own clean context
delegate with no matching agent (ephemeral sub-agent)
How it fits
plan and delegate don’t add a new layer to the framework — they’re tools, the same primitive everything else uses. That’s deliberate: services are the only abstraction, the LLM calls them as tools, and an agent’s own capabilities are no exception.
Go Micro treats model providers as interchangeable pieces of the same agent
harness: services expose tools, agents reason over them, and workflows stitch the
work together. The conformance harness keeps that promise honest by running the
same deterministic services → agents → workflows scenarios against every
configured provider.
The live harness is in internal/harness/provider-conformance. It skips
providers without API keys by default, so it is safe to run locally, and it fails
when any configured provider breaks the shared contract.
go run ./internal/harness/provider-conformance
For a no-key smoke test of the same harness wiring, run the mock provider:
go run ./internal/harness/provider-conformance -providers mock
Status legend
Status
Meaning
✅ Verified
Covered by the provider-conformance harness for configured live providers.
⚠️ Unverified
Implemented in the public API, but not yet exercised by provider conformance.
— Unsupported
Not exposed by that provider integration today.
Harness coverage by capability
These rows describe what the conformance harness verifies today. A provider is
considered conformant when the configured-key run passes all selected harnesses.
Capability
Harness coverage
Notes
Simple generation
✅ Verified
Each harness asks the provider to produce an agent response through ai.Model.
Service tool calls
✅ Verified
Harness services are discovered and invoked as model-selected tools.
Multi-step tool use
✅ Verified
The universe and plan-delegate harnesses require more than one service/tool action.
plan
✅ Verified
plan-delegate verifies that the conductor agent stores a plan in scoped state.
delegate
✅ Verified
plan-delegate verifies agent-to-agent delegation over real RPC.
Guardrail/stop behavior
✅ Verified
universe runs with guardrails enabled and asserts the guarded path completes.
Streaming
⚠️ Unverified
ai.Model.Stream exists on the interface, but end-to-end streaming conformance is a roadmap item.
Structured errors
⚠️ Unverified
Error handling is covered by normal test suites, but provider conformance does not yet compare structured provider errors.
Provider capability matrix
This matrix combines the registered provider interfaces with the conformance
coverage above. The chat/text column is the harness path: when the provider has a
configured key, the conformance command exercises the verified rows in the
previous section.
Provider
Chat/text agent harness
Image
Video
Streaming
Structured errors
anthropic
✅ Verified when configured
— Unsupported
— Unsupported
✅ Verified when configured
⚠️ Unverified
openai
✅ Verified when configured
✅ Registered
— Unsupported
⚠️ Unverified
⚠️ Unverified
gemini
✅ Verified when configured
— Unsupported
— Unsupported
✅ Verified when configured
⚠️ Unverified
groq
✅ Verified when configured
— Unsupported
— Unsupported
⚠️ Unverified
⚠️ Unverified
mistral
✅ Verified when configured
— Unsupported
— Unsupported
⚠️ Unverified
⚠️ Unverified
together
✅ Verified when configured
— Unsupported
— Unsupported
⚠️ Unverified
⚠️ Unverified
atlascloud
✅ Verified when configured
✅ Registered
✅ Registered
⚠️ Unverified
⚠️ Unverified
Running a focused check
Use -providers to select a provider and -harnesses to narrow the scenario:
go run ./internal/harness/provider-conformance \
-providers openai,anthropic \
-harnesses agent-flow,plan-delegate
By default missing live-provider keys are reported as skips. Add
-require-configured in CI when a selected provider must be present:
go run ./internal/harness/provider-conformance \
-providers openai \
-require-configured
The command also prints the registered model, image, and video provider
capabilities before running conformance. Disable that with -capabilities=false
when you only want pass/fail output.
For automation, add -summary-json to capture the selected providers,
harnesses, registered capability rows, and pass/skip/fail results in a stable
machine-readable file. Add -capabilities-markdown when you also want a
ready-to-publish Markdown support table for release notes, docs, or issue
updates:
go run ./internal/harness/provider-conformance \
-providers mock \
-summary-json provider-conformance-summary.json \
-capabilities-markdown provider-capabilities.md
Get up and running with go-micro in under 5 minutes.
Install
go install go-micro.dev/v5/cmd/micro@v5.16.0
Note: Use a specific version instead of @latest to avoid module path conflicts. See releases for the latest version.
Create Your First Service
# Create a new servicemicro new helloworld
cd helloworld
# Review the generated codels -la
# Run locally with hot reloadmicro run
# Test itcurl -X POST http://localhost:8080/api/helloworld/Helloworld.Call \
-H "Content-Type: application/json"\
-d '{"name": "World"}'
Registry - In-memory registry for service discovery
Transport - HTTP transport for RPC
Broker - In-memory broker for events
This allows your service to run without affecting or being affected by other services.
API
Creating a Harness
h:=test.NewHarness(t)deferh.Stop()// Always stop to clean up
Configuring
h.Name("myservice")// Set service name (default: "test")h.Register(handler)// Set the handlerh.Start()// Start the service
Making Calls
// Simple callerr:=h.Call("Handler.Method",&request,&response)// With contexterr:=h.CallContext(ctx,"Handler.Method",&request,&response)
Assertions
// Check service is runningh.AssertServiceRunning()// Check call succeedsh.AssertCallSucceeds("Handler.Method",&req,&rsp)// Check call failsh.AssertCallFails("Handler.Method",&req,&rsp)
Advanced Access
// Get the client for custom callsclient:=h.Client()// Get the serverserver:=h.Server()// Get the registryreg:=h.Registry()
Example: Testing a User Service
packageusersimport("context""testing""go-micro.dev/v6/test")typeUsersHandlerstruct{usersmap[string]*User}typeUserstruct{IDstringNamestring}typeCreateRequeststruct{Namestring}typeCreateResponsestruct{User*User}func(h*UsersHandler)Create(ctxcontext.Context,req*CreateRequest,rsp*CreateResponse)error{user:=&User{ID:"123",Name:req.Name}h.users[user.ID]=userrsp.User=userreturnnil}funcTestUsersCreate(t*testing.T){h:=test.NewHarness(t)deferh.Stop()handler:=&UsersHandler{users:make(map[string]*User)}h.Name("users").Register(handler)h.Start()varrspCreateResponseh.AssertCallSucceeds("UsersHandler.Create",&CreateRequest{Name:"Alice"},&rsp)ifrsp.User==nil{t.Fatal("user is nil")}ifrsp.User.Name!="Alice"{t.Errorf("expected Alice, got %s",rsp.User.Name)}// Verify the user was storedif_,ok:=handler.users["123"];!ok{t.Error("user not stored in handler")}}
Limitations
Due to go-micro’s global defaults, each harness should test one service. If you need to test service-to-service communication, consider:
Integration tests - Run services as separate processes
Mock clients - Mock the client calls to dependent services
Contract tests - Test service interfaces separately
Tips
Always defer Stop() - Ensures cleanup even if test fails
Use meaningful names - h.Name("users") makes logs clearer
Test edge cases - Use AssertCallFails for error paths
Keep handlers simple - Complex handlers are harder to test
4.31 - The Agent Harness
The first wave of agent frameworks solved one problem: put a model in a loop with
some tools. The harder problem is operating that loop — and that’s what a
harness is.
A harness is the runtime around an agent:
the tools it can call,
the memory it keeps,
the guardrails that bound it,
the workflows that trigger and structure it,
the state that survives a restart,
the observability to see what it did,
the services it depends on,
and the protocols other agents use to reach it.
Go Micro’s bet is that this runtime is the one you already deploy. An agent is a
service with a model inside; the harness is the distributed-systems machinery
services already have. So you don’t bolt a separate orchestration product onto
your stack — the harness is the stack.
The pieces, and what they map to
Harness concern
In Go Micro
Status
Tools
Every service endpoint is an MCP-callable tool from registry metadata — no extra code
Shipped
Memory
Store-backed agent memory (AgentMemory), durable across restarts
Shipped
Guardrails
MaxSteps, LoopLimit, ApproveTool, tool wrappers — enforced at the call site
Shipped
Workflows
Durable flows; micro.FlowLoop for run-until-done
Shipped
Planning / delegation
Built-in plan and delegate tools on every agent
Shipped
Discovery & RPC
Registry + client; agents and services find and call each other
Shipped
Interop
MCP (tools), A2A (agents), x402 (paid tools)
Shipped
Resilience
Per-call timeout with context propagation; opt-in retry/backoff (ModelRetry) across the loop
Shipped
Durable runs
Checkpoint and resume an agent run with the same checkpoint backend flows use
Shipped
Observability
RunInfo → OpenTelemetry spans for runs, model calls, tools, delegation, and failures; persisted run history
Shipped
Streaming
ai.Stream through chat, agent, and A2A
In progress
The “in progress” rows are exactly the roadmap’s Now and Next,
and the work is happening in the open.
Durable agent runs
Agents can persist their execution history to the same Checkpoint backend as
flows. A checkpointed Ask records the run id, original prompt, model result,
and completed tool calls. If the process restarts after a tool succeeds but
before the model finishes, AgentResume continues the same run and returns the
recorded tool result instead of re-running the side effect. If a run already
completed, resume returns the persisted response without calling the model.
agent:=micro.NewAgent("conductor",micro.AgentProvider("anthropic"),micro.AgentWithCheckpoint(checkpoint),)resp,err:=agent.Ask(ctx,"charge order 42 and send a receipt")iferr!=nil{// On startup, or after a transient failure, discover unfinished work:pending,_:=micro.AgentPending(ctx,agent)for_,run:=rangepending{_,_=micro.AgentResume(ctx,agent,run.ID)}}_=resp
Choose the boundary deliberately: use a durable flow when the steps are known
(reserve, charge, confirm) and each step has deterministic retry/resume
semantics. Use a checkpointed agent run when the model is deciding which tools to
call or how many turns it needs, but the side effects of completed tool calls
still need crash-safe resume. Flows and agents share the same Checkpoint
interface, so a flow can safely dispatch to a checkpointed agent for the
open-ended part.
For human-in-the-loop runs that pause through the built-in request_input tool,
resume with the operator’s response:
_,err:=micro.AgentResumeInput(ctx,agent,runID,"Deploy to us-east-1")
Observing agent runs
Pass an OpenTelemetry tracer provider when you construct an agent to turn the
agent’s RunInfo into spans:
A traced Ask emits a parent agent.run span plus child spans for
agent.model.call and agent.tool.call. Delegate tool calls are marked with
agent.delegate=true; ephemeral sub-agents start their own agent.run span with
agent.run.parent_id set to the delegating run, so a trace shows the hand-off
from service-like agent to sub-agent. Failure and refusal outcomes set error
status on the relevant span and are also recorded in the persisted run timeline.
Important span attributes include:
Attribute
Meaning
agent.run.id
Stable run correlation ID surfaced as ai.RunInfo.RunID
agent.run.parent_id
Parent run for delegated sub-agent work
agent.name
Agent that owns the run or call
agent.model.provider / agent.model.name
Provider and configured model for model calls
agent.tool.name
Tool invoked by the model
agent.delegate
Whether the tool call is a delegation boundary
agent.latency_ms
Elapsed time for the run/call
agent.tokens.*
Token usage when the provider reports it
Why services are the right substrate
An agent that does real work needs typed, discoverable, callable capabilities —
which is what a service is. The harness is credible because of the service
layer, not in spite of it:
Tools are services — endpoint metadata becomes the tool schema; an RPC
executes the call.
Agents are services — they register, load-balance, expose Agent.Chat, and
are reachable by other agents.
Workflows are code paths — use a flow when the path is known; hand off to an
agent when it isn’t.
Safety lives at execution — guardrails run on the one path every tool call
takes.
When to reach for it
Use Go Micro when the agent has to operate a system, not just answer a prompt
— when it needs real tools, state that survives, limits you can enforce, and a way
to be seen and called. If you only need a model in a loop, you don’t need a
harness. When that loop has to touch production, you do.
This walkthrough builds the smallest useful Go Micro agent path: one service
with typed endpoints, one agent scoped to that service, and one CLI conversation
that proves the agent can use the service as a tool. It is the 0→1 version of
the services → agents → workflows lifecycle: build capability first, add
intelligence on top, then keep a clear path toward flows when the work needs to
run on events or schedules.
Runnable reference first
If you want to run the lifecycle before copying code, start with the no-secret first-agent transcript or run the maintained support-desk example from the repository root:
go run ./examples/support
It uses a deterministic mock model by default, so it needs no provider key, and it exercises the same shape this guide teaches: services become tools, an agent uses them, and a flow can trigger the work. Use the transcript for expected output, then use this guide when you are ready to build the smaller 0→1 version yourself.
What you’ll build
A tiny task assistant:
A task service exposes Create and List endpoints.
An assistant agent is scoped to the task service.
micro run starts both in the local harness.
micro chat asks the agent to create and list tasks.
The same service endpoints are normal RPC methods, dashboard/API actions, MCP
tools, and agent tools. You do not write a second integration layer for the
agent.
Prerequisites
Go 1.24 or newer.
The micro CLI installed.
An LLM provider key for live agent calls. For example:
exportANTHROPIC_API_KEY=sk-ant-...
Plain service calls work without a model key; the key is only needed when the
agent reasons over tools.
Run the read-only first-agent preflight before starting the walkthrough. The same CLI boundary is covered by CI with go test ./cmd/micro -run TestFirstAgentWalkthroughCLIBoundaries -count=1, and the copy/paste tutorial code is built from a clean temporary workspace with go test ./internal/harness/zero-to-hero-ci -run TestYourFirstAgentTutorialSmoke -count=1, so the documented scaffold → run → chat → inspect path stays visible in the local harness:
micro agent preflight
It checks Go 1.24+, the micro binary, provider-key setup, and the default local gateway port without contacting a provider. Failed checks include a Fix: line and a Next: line that points back to this guide, the no-secret walkthrough, or the debugging guide. Use it before micro run; if micro run is already active but micro chat, the /agent gateway, registration, provider settings, or inspect history is failing, run the after-run recovery check instead:
micro agent doctor
1. Create a workspace
mkdir first-agent
cd first-agent
go mod init example.com/first-agent
go get go-micro.dev/v6@v6
Add main.go:
packagemainimport("context""fmt""os""sync"micro"go-micro.dev/v6")typeCreateRequeststruct{Titlestring`json:"title"`}typeCreateResponsestruct{IDstring`json:"id"`Titlestring`json:"title"`}typeListRequeststruct{}typeListResponsestruct{Tasks[]CreateResponse`json:"tasks"`}typeTaskServicestruct{musync.Mutexnextinttasks[]CreateResponse}// Create adds a task to the list.// @example {"title":"Write first agent guide"}func(t*TaskService)Create(ctxcontext.Context,req*CreateRequest,rsp*CreateResponse)error{t.mu.Lock()defert.mu.Unlock()t.next++*rsp=CreateResponse{ID:fmt.Sprintf("task-%d",t.next),Title:req.Title}t.tasks=append(t.tasks,*rsp)returnnil}// List returns all known tasks.// @example {}func(t*TaskService)List(ctxcontext.Context,req*ListRequest,rsp*ListResponse)error{t.mu.Lock()defert.mu.Unlock()rsp.Tasks=append([]CreateResponse(nil),t.tasks...)returnnil}funcmain(){service:=micro.NewService("task")service.Handle(new(TaskService))agent:=micro.NewAgent("assistant",micro.AgentServices("task"),micro.AgentPrompt("You help manage tasks. Use the task service before answering."),micro.AgentProvider("anthropic"),micro.AgentAPIKey(os.Getenv("ANTHROPIC_API_KEY")),)goagent.Run()service.Run()}
Why the comments matter: endpoint comments and @example tags become tool
descriptions, so the agent has enough context to choose task.Create and
task.List correctly.
2. Run the service and agent
From the same directory:
micro run
The local harness starts the service, gateway, dashboard, MCP tool surface, and
agent playground. You can also verify the service directly before involving the
agent:
In another terminal, ask the agent to use the service:
micro chat assistant
Try:
Create a task called "Review the first-agent walkthrough", then show me all tasks.
A healthy run shows the agent calling the task service and then summarizing the
result. Inspect the recorded run when you want to see the tool calls, memory,
and timing behind the answer:
micro inspect agent assistant
If inspect shows stage=input-required, provide the missing value and inspect the
completed run from the same local store:
micro agent resume-input assistant <run-id> --input "Approve the next step"micro inspect agent assistant --limit 1
If the model refuses to call tools, tighten the prompt so it explicitly
uses the task service before answering.
4. Know what just happened
The service registered typed RPC endpoints.
Go Micro derived tool descriptions from the endpoint names, comments, request
fields, and examples.
The agent registered as another service with an Agent.Chat endpoint.
micro chat sent your message to the agent.
The agent selected the scoped task tools, called them over the same runtime,
and stored conversation history in memory.
That is the core lifecycle: services provide capability, agents use the
capability, and the same runtime can later put the interaction behind a flow.
5. Make it a workflow when the path is event-driven
Once the prompt should run because something happened rather than because a
human typed a message, move the handoff into a flow:
flow:=micro.NewFlow("task-triage",micro.FlowTrigger("tasks.created"),micro.FlowPrompt("Review this new task and decide the next action: {{.Data}}"),micro.FlowProvider("anthropic"),micro.FlowAPIKey(os.Getenv("ANTHROPIC_API_KEY")),)
Use flows for deterministic triggers and long-running orchestration; keep the
agent for judgment, tool use, and handoffs when the path is not known up front.
Troubleshooting
Symptom
Check
The agent says it cannot access tasks.
Confirm the agent was created with micro.AgentServices("task") and that micro agent list shows assistant.
Tool calls use the wrong fields.
Add or improve doc comments and @example tags on the service methods.
Plain service calls work but chat fails.
Check that your provider key is exported in the shell that runs micro run.
You need a no-secret reference path.
Run make harness from the Go Micro repository; it exercises the services → agents → workflows lifecycle with a mock provider.
Go Micro is a framework for building agents and services in Go. An agent is a distributed system — it discovers services, calls them, holds state, and recovers from failure — so building an agent is b
Go Micro is a framework for building agents and services in Go. An agent is a distributed system — it discovers services, calls them, holds state, and recovers from failure — so building an agent is building a service. The roadmap has two jobs: make agentic development excellent, and make the developer experience around it excellent. Nothing else.
Where we are (v6)
The foundation is in place:
Services — register, discover, RPC, events; every endpoint is automatically an MCP tool.
Agents — a model with memory and tools that manages services, with plan, delegate, and guardrails (MaxSteps, LoopLimit, ApproveTool) built in, plus tool-execution middleware (WrapTool) and run metadata.
Flows — durable, event-driven workflows: ordered steps that checkpoint and resume after a crash.
Interop — the MCP gateway (services as tools) and the A2A gateway (agents as agents, both directions, including A2A streaming, push notifications, and multi-turn continuation), both generated from the registry; x402 for paid tools.
Secure by default — TLS verification on, state scoped per component.
Principles
These constrain everything below:
Build into what people run, never a separate product. No hosted platform, no enterprise edition. Improvements go deeper into the framework, not beside it.
CLI-first. The CLI is the experience. Any UI must be genuinely good and earn its place; bloat gets trimmed, not maintained.
The getting-started flow is a contract.0→1 (scaffold → run → call) and 0→hero (the ~10 steps to a working multi-agent system) must always work, and every change is checked against them.
Interaction is as important as running. Talking to an agent, inspecting runs and history — end to end, not just “it starts.”
Battle-tested. Works across every provider, fails safely, and is observable.
Now — hardening (“functional on every level”)
The priority is that what exists works everywhere, under real conditions.
Cross-provider conformance. Each of the seven providers implements its own tool-call loop; today only the mock and one live provider are exercised. A suite that runs the same agent scenario (tool-calling, multi-step, plan/delegate, guardrails) across every provider, gated on keys, on a schedule.
Failure & resilience. Provider timeouts, rate limits, and cancellation mid-run; deadline/context propagation through the agent loop; retry and backoff at the model call.
The getting-started contract. Define and CI-verify the 0→1 and 0→hero flows so they can’t silently break.
Next — agentic depth
Streaming. Broaden provider-backed ai.Stream coverage and keep chat plus A2A message/stream working end to end for real chat and long-task UX.
Agent observability. Wire the new RunInfo into OpenTelemetry spans so a run — steps, tool calls, delegation — is traceable. This is also what anyone running it in production will need.
Later
Memory management — summarization and retrieval (RAG) beyond a fixed buffer.
Human-in-the-loop — broaden pause/resume UX around input-required runs and approvals.
A2A — richer live-stream reconnection (tasks/resubscribe) and input-required handoffs.
Developer experience (ongoing)
The CLI inner loop — scaffold → run → chat → inspect (runs/history) → deploy, made seamless. This is the main lever for “dramatically improve the experience.”
UI discipline — keep only high-value, well-built surfaces; trim or cut the rest. The web UI should never be a worse version of the CLI.
Examples & a real-world build — a maintained example that builds something real with the framework, doubling as the 0→hero reference and continuous battle-testing.
Docs in lockstep — the getting-started guide tracks the code on every change.
How it’s sustained
The framework is the product. It’s funded by sponsorship from the people and companies who run it — not a hosted service, not an enterprise tier, not venture funding. The model is deliberate: keep refining the framework, aligned users adopt and depend on it, and that dependence funds the work. (See Bringing an Open Source Project Back from the Dead
for why.)
5.3 - Powered by Go Micro Badge
Documentation for Get Badge. Show your support and let others know your project is built with Go Micro!
Markdown Badges
Dark Badge
[](https://go-micro.dev)
Light Badge
[](https://go-micro.dev)
<ahref="https://go-micro.dev"target="_blank"><imgsrc="https://img.shields.io/badge/Powered%20by-Go%20Micro-0366d6?style=for-the-badge&logo=go&logoColor=white"alt="Powered by Go Micro"></a>
Custom SVG Badge
<ahref="https://go-micro.dev"style="display:inline-block;text-decoration:none;"><svgwidth="160"height="32"xmlns="http://www.w3.org/2000/svg"><rectwidth="160"height="32"rx="6"fill="#0366d6"/><textx="16"y="21"font-family="system-ui,-apple-system,sans-serif"font-size="13"font-weight="600"fill="white"> Powered by Go Micro
</text></svg></a>
Usage
Add one of these badges to your README.md, documentation, or website footer to show that your project uses Go Micro.
Example README
# My Awesome Project

[](https://go-micro.dev)
My project does amazing things using Go Micro microservices framework.
## Features
- Fast and scalable
- Built with Go Micro
- Production ready
Badge Guidelines
Link the badge to https://go-micro.dev to help others discover Go Micro
Use the badge prominently in your README
Consider adding it to your project website footer
Feel free to customize the colors to match your brand
Showcase Your Project
Built something cool with Go Micro? Open an issue to get featured on our homepage!
5.4 - Server (optional)
The Micro server is an optional web dashboard and authenticated API gateway for production environments. It provides a secure entrypoint for discovering and interacting with services that are already
Micro Server (Optional)
The Micro server is an optional web dashboard and authenticated API gateway for production environments. It provides a secure entrypoint for discovering and interacting with services that are already running (e.g., managed by systemd via micro deploy).
micro server does not build, run, or watch services. It only discovers services via the registry and provides a UI/API to interact with them.
micro server vs micro run
micro run
micro server
Purpose
Local development
Production dashboard
Builds services
Yes
No
Runs services
Yes (as child processes)
No (discovers already-running services)
Hot reload
Yes
No
Authentication
Yes (default admin/micro)
Yes (default admin/micro)
Scopes
Yes (/auth/scopes)
Yes (/auth/scopes)
Dashboard
Full gateway UI with auth, scopes, agent
Full dashboard with API explorer, logs, user/token management
Services managed by systemd are discovered via the registry and appear in the dashboard automatically. The server provides the authenticated API and web UI for interacting with them.
When to use it
You have services running in production (via systemd or otherwise) and want a web UI
You need authenticated API access with JWT tokens
You want user management and token revocation
You’re running a shared environment where multiple people interact with services
Documentation of architectural decisions made in Go Micro, following the ADR pattern.
What are ADRs?
Architecture Decision Records (ADRs) capture important architectural decisions along with their context and consequences. They help understand why certain design choices were made.
ADRs are immutable once accepted. To change a decision, create a new ADR that supersedes the old one.
5.5.1 - Adr 001 Plugin Architecture
Microservices frameworks need to support multiple infrastructure backends (registries, brokers, transports, stores). Different teams have different preferences and existing infrastructure.
Status
Accepted
Context
Microservices frameworks need to support multiple infrastructure backends (registries, brokers, transports, stores). Different teams have different preferences and existing infrastructure.
No version hell: Plugins versioned with core framework
Discovery: Users browse available plugins in same repo
Consistency: All plugins follow same patterns
Testing: Plugins tested together
Zero config: Default implementations require no setup
Negative
Repo size: More code in one repository
Plugin maintenance: Core team responsible for plugin quality
Breaking changes: Harder to evolve individual plugins independently
Neutral
Plugins can be extracted to separate repos if they grow complex
Community can contribute plugins via PR
Plugin-specific issues easier to triage
Alternatives Considered
Separate Plugin Repositories
Used by go-kit and other frameworks. Rejected because:
Version compatibility becomes user’s problem
Discovery requires documentation
Testing integration harder
Splitting community
Single Implementation
Like standard net/http. Rejected because:
Forces infrastructure choices
Limits adoption
Can’t leverage existing infrastructure
Dynamic Plugin Loading
Using Go plugins or external processes. Rejected because:
Complexity for users
Compatibility issues
Performance overhead
Debugging difficulty
Related
ADR-002: Interface-First Design (planned)
ADR-005: Registry Plugin Scope (planned)
5.5.2 - Adr 004 Mdns Default Registry
Service discovery is critical for microservices. Common approaches:
Status
Accepted
Context
Service discovery is critical for microservices. Common approaches:
Central registry (Consul, Etcd) - Requires infrastructure
DNS-based (Kubernetes DNS) - Platform-specific
Static configuration - Doesn’t scale
Multicast DNS (mDNS) - Zero-config, local network
For local development and getting started, requiring infrastructure setup is a barrier. Production deployments typically have existing service discovery infrastructure.
Decision
Use mDNS as the default registry for service discovery.
Works immediately on local networks
No external dependencies
Suitable for development and simple deployments
Easily swapped for production registries (Consul, Etcd, Kubernetes)
Implementation
// Default - uses mDNS automaticallysvc:=micro.NewService("myservice")// Production - swap to Consulreg:=consul.NewConsulRegistry()svc:=micro.NewService("myservice",micro.Registry(reg),)
Consequences
Positive
Zero setup: go run main.go just works
Fast iteration: No infrastructure for local dev
Learning curve: Newcomers start immediately
Progressive complexity: Add infrastructure as needed
Negative
Local network only: mDNS doesn’t cross subnets/VLANs
Not for production: Needs proper registry in production
Port 5353: May conflict with existing mDNS services
Discovery delay: Can take 1-2 seconds
Mitigations
Clear documentation on production alternatives
Environment variables for easy swapping (MICRO_REGISTRY=consul)
Examples for all major registries
Health checks and readiness probes for production
Use Cases
Good for mDNS
Local development
Testing
Simple internal services on same network
Learning and prototyping
Need Production Registry
Cross-datacenter communication
Cloud deployments
Large service mesh (100+ services)
Require advanced features (health checks, metadata filtering)
Alternatives Considered
No Default (Force Configuration)
Rejected because:
Poor first-run experience
Increases barrier to entry
Users must setup infrastructure before trying framework
Static Configuration
Rejected because:
Doesn’t support dynamic service discovery
Manual configuration doesn’t scale
Doesn’t reflect real microservices usage
Consul as Default
Rejected because:
Requires running Consul for “Hello World”
Platform-specific
Adds complexity for beginners
Migration Path
Start with mDNS, migrate to production registry:
# Developmentgo run main.go
# StagingMICRO_REGISTRY=consul MICRO_REGISTRY_ADDRESS=consul:8500 go run main.go
# Production (Kubernetes)MICRO_REGISTRY=nats MICRO_REGISTRY_ADDRESS=nats://nats:4222 ./service
Full control over initialization and configuration.
Level 4: External Config (Enterprise)
cfg:=config.NewConfig(config.Source(file.NewSource("config.yaml")),config.Source(env.NewSource()),config.Source(vault.NewSource()),)// Use cfg to initialize plugins with complex configs
func(s*service)Init()error{ifs.opts.Name==""{returnerrors.New("service name required")}// Warn about development defaults in productionifisProduction()&&usingDefaults(){log.Warn("Using development defaults in production")}returnnil}
Now calls server.StartGateway() with AuthEnabled: true
Retains process management and hot reload functionality
Same auth, scopes, and token management as micro server
Architecture
┌─────────────────────────────────────────────────────────────┐
│ Unified Gateway │
│ (cmd/micro/server/gateway.go) │
│ │
│ • HTTP → RPC translation │
│ • Service discovery via registry │
│ • Web UI (dashboard, logs, API docs) │
│ • Health checks │
│ • Configurable authentication │
│ • Endpoint scopes for access control │
│ • MCP tool integration with scope enforcement │
└─────────────────────────────────────────────────────────────┘
▲ ▲
│ │
┌──────┴──────┐ ┌────────┴────────┐
│ micro run │ │ micro server │
│ │ │ │
│ + Process │ │ + Auth enabled │
│ mgmt │ │ + JWT tokens │
│ + Hot │ │ + Scopes │
│ reload │ │ + Production │
│ + Auth │ │ │
│ + Scopes │ │ │
└─────────────┘ └─────────────────┘
Usage
Development Mode (micro run)
# Start services with gateway (auth enabled, default admin/micro)micro run
# Gateway provides:# - HTTP API at /api/{service}/{endpoint}# - Web dashboard at /# - JWT authentication (admin/micro default)# - Endpoint scopes at /auth/scopes
Production Mode (micro server)
# Start gateway with authenticationmicro server --address :8080
# Gateway provides:# - HTTP API at /api/{service}/{endpoint} (auth required)# - Web dashboard with login# - JWT-based authentication# - User/token management UI# - Endpoint scopes at /auth/scopes
Benefits
Single Source of Truth: Gateway logic lives in one place
Automatic Feature Propagation: New features (like MCP) added to the unified gateway benefit both commands
Simplified Testing: Test gateway once, works everywhere
Reduced Code Size: Eliminated ~300 lines of duplicate code
Clear Separation:
micro server = API gateway (HTTP + future MCP)
micro run = Development tool (gateway + process management + hot reload)
Go Micro is an agent harness and service framework for Go. Every service you build can become an AI-callable tool, every agent runs as a service with model/memory/guardrails around it, and flows orchestrate the deterministic parts. This page explains how the services → agents → workflows lifecycle fits together.
The Stack
Services → write Go handlers, register with the framework
↓
Registry → automatic discovery for services, agents, and flows
↓
Gateways → micro api (HTTP→RPC), micro mcp (tools), micro a2a (agents)
↓
ai.Tools → discovers services + executes RPCs programmatically
↓
ai.Model → calls LLMs (Anthropic, OpenAI, Gemini, Atlas Cloud, ...)
↓
Agents → service-backed model loop with memory, guardrails, plan/delegate
↓
Flows → durable deterministic steps that can dispatch to agents
Every layer is optional. You can use Go Micro as a service framework without AI. You can use the ai package without MCP. But when you stack them, you get one runtime where services become tools, agents are reachable services, and workflows coordinate the predictable parts.
Layer by Layer
1. Services (your code)
Write normal Go handlers. Add doc comments for AI tool descriptions:
// CreateUser creates a new user account.// @example {"name": "Alice", "email": "alice@example.com"}func(h*Users)CreateUser(ctxcontext.Context,req*pb.CreateRequest,rsp*pb.CreateResponse)error{// your business logic}
The doc comment becomes the tool description. The @example tag gives the LLM a usage hint. No AI-specific code in your handler.
2. Registry (service discovery)
Services register automatically. The registry is the source of truth for what’s running:
service:=micro.NewService("users")service.Handle(handler.New())service.Run()// registers with the registry
Pluggable: mDNS (default, zero config), Consul, etcd, NATS.
3. MCP Gateway (services → tools)
The MCP gateway walks the registry and exposes every endpoint as a tool via the Model Context Protocol:
// One line to expose all services as AI toolsservice:=micro.NewService("myservice",mcp.WithMCP(":3001"))
Or run it standalone:
micro mcp serve # stdio for Claude Codemicro mcp serve --address :3000 # HTTP for web agents
Any MCP-compatible agent (Claude Code, ChatGPT, custom agents) can discover and call your services.
4. ai.Tools (discover + execute)
ai.Tools turns registered services into LLM-callable tools — discovery plus RPC execution in one type:
tools:=ai.NewTools(service.Registry())discovered,_:=tools.Discover()// []ai.Tool from all registered services// Wire execution into a model with one option:m:=ai.New("anthropic",ai.WithAPIKey(key),ai.WithTools(tools))
This is what powers micro chat and the agent playground. You can use it directly in your own services to build agentic workflows.
5. ai.Model (LLM providers)
The ai package provides a pluggable interface for calling LLMs:
import("go-micro.dev/v6/ai"_"go-micro.dev/v6/ai/anthropic")m:=ai.New("anthropic",ai.WithAPIKey(key))resp,_:=m.Generate(ctx,&ai.Request{Prompt:"What users are in the system?",Tools:discovered,// from ai.Tools})
Seven text providers, two image providers, one video provider. Same interface, swap with an import.
Provider
Text
Image
Video
Anthropic
yes
OpenAI
yes
yes
Google Gemini
yes
Atlas Cloud
yes
yes
yes
Groq
yes
Mistral
yes
Together AI
yes
6. micro chat (orchestration)
The CLI ties it all together — discovers services, builds the tool list, and lets you talk to your services:
ANTHROPIC_API_KEY=sk-ant-... micro chat --provider anthropic
> list all users
> send a welcome email to alice@example.com
> create an order for product-42
Multi-turn conversation with ai.History — the model remembers context across turns. Type reset to clear history.
7. micro flow (event-driven orchestration)
Subscribe to broker events and let an LLM orchestrate the response:
import"go-micro.dev/v6/flow"f:=flow.New("onboard",flow.Trigger("events.user.created"),flow.Prompt("New user: {{.Data}}. Send welcome email and create workspace."),flow.Provider("anthropic"),flow.APIKey(key),)f.Register(service.Registry(),service.Options().Broker,service.Client())
Or from the CLI:
micro flow run --trigger events.user.created \
--prompt "New user: {{.Data}}. Send welcome email."\
--provider anthropic
micro flow exec --prompt "List all users" --provider anthropic
8. micro api (HTTP gateway)
A standalone HTTP-to-RPC gateway for exposing services over HTTP without the full dashboard:
micro api # listen on :8080micro api --address :3000 # custom port# Call services through the gatewaycurl -XPOST -d '{"name":"Alice"}' http://localhost:8080/greeter/Greeter.Hello
What You Don’t Need
No agent framework — the building blocks compose; you don’t need a LangChain or CrewAI equivalent
No special handler code — your services are normal Go handlers with doc comments
No API key to use MCP — external agents bring their own models; your services just expose tools
No vendor lock-in — every provider implements the same interface; swap with one import
Getting Started
The fastest path:
# Create a service with MCP enabledmicro new myservice --template crud
cd myservice
# Run itmicro run
# Chat with itANTHROPIC_API_KEY=sk-ant-... micro chat --provider anthropic
> list all records
Date: 2026-02-03 Author: GitHub Copilot Status: RECOMMENDATION - DO NOT PROCEED
Executive Summary
After comprehensive analysis of the go-micro codebase and comparison with livekit/psrpc (referenced as an example of a reflection-free approach), we recommend AGAINST removing reflection from go-micro. The architectural differences make this change infeasible without a complete redesign that would:
Break backward compatibility - Fundamentally change the API
// Current go-micro approach - accepts ANY structtypeGreeterServicestruct{}func(g*GreeterService)SayHello(ctxcontext.Context,req*Request,rsp*Response)error{rsp.Message="Hello "+req.Namereturnnil}server.Handle(server.NewHandler(&GreeterService{}))
How it works:
Uses reflect.TypeOf() to inspect the struct
Uses typ.NumMethod() to iterate all public methods
Uses reflect.Method.Type to validate signatures
Uses reflect.Value.Call() to invoke methods dynamically
2. Method Signature Validation
funcprepareMethod(methodreflect.Method,loggerlog.Logger)*methodType{mtype:=method.Type// Validate: func(receiver, context.Context, *Request, *Response) errorswitchmtype.NumIn(){case4:// Standard RPCargType=mtype.In(2)replyType=mtype.In(3)case3:// Streaming RPCargType=mtype.In(2)// Must implement Stream interface}ifmtype.NumOut()!=1||mtype.Out(0)!=typeOfError{returnnil// Invalid method}}
3. Dynamic Method Invocation
function:=mtype.method.FuncreturnValues=function.Call([]reflect.Value{s.rcvr,// Receiver (the handler struct)mtype.prepareContext(ctx),// context.Contextreflect.ValueOf(argv.Interface()),// Request argumentreflect.ValueOf(rsp),// Response pointer})iferr:=returnValues[0].Interface();err!=nil{returnerr.(error)}
Performance Impact: Each Call() allocates a slice of reflect.Value and has ~10-20% overhead vs direct function calls.
4. Dynamic Type Construction
// Create request value based on method signatureifmtype.ArgType.Kind()==reflect.Ptr{argv=reflect.New(mtype.ArgType.Elem())}else{argv=reflect.New(mtype.ArgType)argIsValue=true}// Unmarshal into the dynamically created valuecc.ReadBody(argv.Interface())
livekit/psrpc Approach
Architecture
PSRPC completely avoids reflection by using code generation from Protocol Buffer definitions:
// my_service.psrpc.go (auto-generated)typeMyServiceClientinterface{SayHello(ctxcontext.Context,req*Request,opts...psrpc.RequestOpt)(*Response,error)}typemyServiceClientstruct{buspsrpc.MessageBus}func(c*myServiceClient)SayHello(ctxcontext.Context,req*Request,opts...psrpc.RequestOpt)(*Response,error){// Type-safe, no reflection neededdata,err:=proto.Marshal(req)iferr!=nil{returnnil,err}respData,err:=c.bus.Request(ctx,"MyService.SayHello",data,opts...)iferr!=nil{returnnil,err}resp:=&Response{}iferr:=proto.Unmarshal(respData,resp);err!=nil{returnnil,err}returnresp,nil}typeMyServiceServerinterface{SayHello(ctxcontext.Context,req*Request)(*Response,error)}funcRegisterMyServiceServer(srvMyServiceServer,buspsrpc.MessageBus)error{// Register type-safe handlerbus.Subscribe("MyService.SayHello",func(ctxcontext.Context,data[]byte)([]byte,error){req:=&Request{}iferr:=proto.Unmarshal(data,req);err!=nil{returnnil,err}resp,err:=srv.SayHello(ctx,req)iferr!=nil{returnnil,err}returnproto.Marshal(resp)})returnnil}
Key Differences
Aspect
go-micro (Reflection)
psrpc (Code Generation)
Handler Definition
Any Go struct with methods
Must implement generated interface
Type Safety
Runtime validation
Compile-time enforcement
Setup
Import library
Protoc + code generation
Flexibility
Register any struct
Only proto-defined services
Boilerplate
Minimal
Significant (generated)
Performance
~10-20% overhead
Zero reflection overhead
Maintainability
Simple codebase
Generated code + proto files
Feasibility Analysis
Why Removing Reflection is NOT Feasible
1. Fundamental Architecture Mismatch
go-micro’s core value proposition is:
“Register any Go struct as a service handler without boilerplate”
// This is go-micro's strengthtypeEmailServicestruct{mailer*smtp.Client}func(e*EmailService)Send(ctxcontext.Context,req*Email,rsp*Status)error{returne.mailer.Send(req)}// Simple registration - no interfaces to implementserver.Handle(server.NewHandler(&EmailService{}))
With code generation (psrpc-style):
// Would require proto file
serviceEmailService{rpcSend(Email)returns(Status);}
// Must implement generated interfacetypeemailServiceServerstruct{mailer*smtp.Client}func(e*emailServiceServer)Send(ctxcontext.Context,req*Email)(*Status,error){// Different signature - no *rsp parameterreturn&Status{},e.mailer.Send(req)}// Different registrationRegisterEmailServiceServer(&emailServiceServer{...},bus)
Impact: Complete API redesign, breaking change for all users.
2. Go Generics Cannot Replace Runtime Type Discovery
Go generics (as of Go 1.24) require compile-time type knowledge:
// IMPOSSIBLE: You can't iterate methods of T at runtimefuncRegisterHandler[Tany](handlerT){// Go generics can't do:// - Iterate methods// - Check method signatures// - Call methods by name string// - Create instances from types}
Why: Generics are a compile-time feature. go-micro needs runtime introspection of arbitrary user-defined types.
3. Loss of Key Features
Features that require reflection and would be lost:
Dynamic endpoint discovery - Building service registry metadata
API documentation generation - Extracting request/response types
Migration path - Help users migrate existing services
Estimated effort: 6-12 months, complete rewrite
Comparison with Similar Frameworks
Framework
Approach
Reflection
go-micro
Dynamic registration
Heavy use
gRPC-Go
Proto + codegen
Protobuf reflection only
psrpc
Proto + codegen
None
Twirp
Proto + codegen
None
go-kit
Manual interfaces
Minimal
Gin/Echo
Manual routing
None (HTTP only)
Insight: RPC frameworks that avoid reflection all require code generation. There’s no middle ground.
Performance Analysis
Benchmarks (Hypothetical)
Based on reflection overhead patterns:
Metric
Current (Reflection)
After Removal (Hypothetical)
Improvement
Method dispatch
10-50μs
1-5μs
5-10x
Type construction
5-20μs
1-2μs
5-10x
Total per-RPC overhead
~50μs
~10μs
5x faster
But in context:
Component
Time
Network I/O
1-10ms
Protobuf marshal/unmarshal
100-500μs
Business logic
Variable (often milliseconds)
Reflection overhead
50μs (0.5-5% of total)
When Reflection Matters
Reflection overhead is significant ONLY when:
Extremely high request rates (>100k RPS)
Minimal business logic (<100μs)
Local/loopback communication (<100μs network)
Example use case: In-process microservices with <1ms SLA.
For most users: Database queries, external API calls, and business logic dominate.
Recommendations
Primary Recommendation: DO NOT REMOVE REFLECTION
Rationale:
Architectural fit - Reflection enables go-micro’s core value proposition
Negligible impact - Performance overhead is <5% in typical scenarios
High risk - Would break all existing code
High cost - 6-12 month rewrite with ongoing maintenance burden
User experience - Current API is simpler and more Go-idiomatic
Alternative Approaches
If performance is critical for specific use cases:
Option 1: Hybrid Approach
Add optional code generation path:
// Option A: Current reflection-based (simple)server.Handle(server.NewHandler(&MyService{}))// Option B: New codegen-based (fast)server.Handle(NewGeneratedMyServiceHandler(&MyService{}))
Benefits:
Backward compatible
Users opt-in for performance
Best of both worlds
Cost: Maintain both paths
Option 2: Optimize Hot Paths
Keep reflection but optimize critical paths:
// Cache reflect.Value to avoid repeated lookupstypemethodCachestruct{functionreflect.ValueargTypereflect.Type// Pre-allocate call argumentscallArgs[4]reflect.Value}
Benefits:
~2-3x faster reflection
No API changes
Lower risk
Cost: Internal refactoring only
Option 3: Document Performance Characteristics
Add documentation for users who need maximum performance:
## Performance Considerations
go-micro uses reflection for dynamic handler registration, which adds
~50μs overhead per RPC call. For most applications this is negligible.
If you need <100μs latency:
- Consider gRPC with protocol buffers
- Use direct client/server without service discovery
- Benchmark your specific use case
Benefits:
Set correct expectations
Guide high-performance users
Zero implementation cost
Conclusion
Removing reflection from go-micro is technically infeasible without a fundamental redesign that would:
Eliminate the framework’s primary value proposition (simplicity)
Break all existing code
Require 6-12 months of development
Provide <5% performance improvement for 99% of users
Recommendation: Close this issue with explanation that reflection is a deliberate architectural choice that enables go-micro’s ease of use. For performance-critical applications, recommend:
Profile first - ensure reflection is actually the bottleneck
Consider gRPC or psrpc if code generation is acceptable
Use go-micro’s strengths for rapid development, then optimize specific services if needed
The comparison with livekit/psrpc shows that avoiding reflection requires code generation and proto-first design, which is a completely different architecture incompatible with go-micro’s goals.
After thorough analysis comparing go-micro with livekit/psrpc and evaluating the feasibility of removing reflection, we’ve determined this would require a fundamental architectural redesign incompatible with go-micro’s goals.
Key findings:
psrpc avoids reflection through code generation from proto files - a completely different architecture
go-micro’s strength is “register any struct” without boilerplate - this requires reflection
Reflection overhead is ~50μs per RPC, typically <5% of total latency
Removing reflection would be a breaking change requiring 6-12 months of development
Recommendation: Keep reflection as a deliberate design choice. For users needing maximum performance, recommend profiling first and considering gRPC/psrpc if code generation is acceptable.
Closing as “won’t fix” - reflection is an intentional architectural decision that enables go-micro’s simplicity and flexibility.
8 - Architecture Decision Records
Documentation of architectural decisions made in Go Micro, following the ADR pattern.
What are ADRs?
Architecture Decision Records (ADRs) capture important architectural decisions along with their context and consequences. They help understand why certain design choices were made.
ADRs are immutable once accepted. To change a decision, create a new ADR that supersedes the old one.
9 - Broker
The broker provides pub/sub messaging for Go Micro services.
Features
Publish messages to topics
Subscribe to topics
Multiple broker implementations
Implementations
Supported brokers include:
HTTP (default)
NATS (go-micro.dev/v6/broker/nats)
RabbitMQ (go-micro.dev/v6/broker/rabbitmq)
Memory (go-micro.dev/v6/broker/memory)
Plugins are scoped under go-micro.dev/v6/broker/<plugin>.
Configure the broker in code or via environment variables.
Example Usage
Here’s how to use the broker in your Go Micro service:
packagemainimport("go-micro.dev/v6""go-micro.dev/v6/broker""log")funcmain(){service:=micro.NewService("publisher")service.Init()// Publish a messageiferr:=broker.Publish("topic",&broker.Message{Body:[]byte("hello world")});err!=nil{log.Fatal(err)}// Subscribe to a topic_,err:=broker.Subscribe("topic",func(pbroker.Event)error{log.Printf("Received message: %s",string(p.Message().Body))returnnil})iferr!=nil{log.Fatal(err)}// Run the serviceiferr:=service.Run();err!=nil{log.Fatal(err)}}
Community support is best-effort, from maintainers and contributors, with no response-time guarantees.
Commercial support
If you’re running Go Micro in production — or building agents and services on it and want a hand — paid support and consulting are available directly from the maintainer. This is what keeps the project maintained.
Tier
For
What you get
How
Community
Everyone
Docs, examples, issues — best-effort
Free
Sponsor
Individuals & companies who rely on Go Micro
Back ongoing development; your name/logo in the README and on the site; a voice in priorities
Recurring amounts are set on the Sponsors page; support and consulting are scoped and quoted per engagement.
Get in touch
Open a Commercial Support / Consulting request — tell us what you’re building, what you need, and your timeline, and we’ll follow up. For anything you’d rather not discuss in public, become a sponsor and message privately.
12 - Configuration
Go Micro follows a progressive configuration model so you can start with zero setup and layer in complexity only when needed.
# Use NATS for broker and transport, Consul for registryexportMICRO_BROKER=nats
exportMICRO_TRANSPORT=nats
exportMICRO_REGISTRY=consul
exportMICRO_REGISTRY_ADDRESS=127.0.0.1:8500
# Run your servicego run main.go
No code changes required. The framework internally wires the selected implementations.
The %i is replaced with the service name. So micro@users.service runs /opt/micro/bin/users.
Deployment
Basic Deploy
micro deploy user@server
Deploy Specific Service
micro deploy user@server --service users
Force Rebuild
micro deploy user@server --build
Named Deploy Targets
Add to your micro.mu:
service users
path ./users
port 8081
service web
path ./web
port 8080
deploy prod
ssh deploy@prod.example.com
deploy staging
ssh deploy@staging.example.com
Then:
micro deploy prod # deploys to prod.example.commicro deploy staging # deploys to staging.example.com
Managing Services
Check Status
# Local servicesmicro status
# Remote servicesmicro status --remote user@server
These are automatically loaded by systemd when the service starts.
SSH Setup
Key-Based Authentication
# Generate key (if you don't have one)ssh-keygen -t ed25519
# Copy to serverssh-copy-id user@server
SSH Config
Add to ~/.ssh/config:
Host prod
HostName prod.example.com
User deploy
IdentityFile ~/.ssh/deploy_key
Host staging
HostName staging.example.com
User deploy
IdentityFile ~/.ssh/deploy_key
Then deploy with:
micro deploy prod
Troubleshooting
“Cannot connect to server”
✗ Cannot connect to myserver
SSH connection failed. Check that:
• The server is reachable: ping myserver
• SSH is configured: ssh user@myserver
• Your key is added: ssh-add -l
Once services are deployed and managed by systemd, you can optionally run micro server on the same machine to get a full web dashboard with authentication:
# On your servermicro server
This gives you:
Web Dashboard at http://your-server:8080 with JWT authentication
API Gateway with authenticated HTTP-to-RPC proxy
User Management — create accounts, generate/revoke API tokens
Logs & Status — view service logs and uptime from the browser
The server discovers services via the registry automatically. Default login: admin / micro.
Go 1.24+ for development. The curl install below gives you the micro binary without Go, but micro run compiles your services, so you’ll want Go installed to build them.
No LLM provider key is required for the first run below. Add an Anthropic, OpenAI, Gemini, or other provider key only when you reach the provider-backed generation and chat steps.
Install
# Binary (no Go required)curl -fsSL https://go-micro.dev/install.sh | sh
# Or with Gogo install go-micro.dev/v6/cmd/micro@latest
If install or shell setup fails, start with Install troubleshooting to verify the binary installer or go install, PATH, micro --version, and the no-secret smoke path.
Quick Start: Scaffold, Run, Call
Start with the path that proves the runtime works before any provider setup: install the CLI, scaffold one service, run it locally, then call it through the gateway.
micro new helloworld
cd helloworld
micro run
In another terminal, call the generated service:
curl -X POST http://localhost:8080/api/helloworld/Helloworld.Call \
-H 'Content-Type: application/json' -d '{"name":"World"}'
That install → scaffold → run → call loop is the 0→1 contract. It requires Go and the micro binary, but no LLM key. Once this succeeds, you know the local runtime, hot reload, gateway, and service registration are working.
First-agent on-ramp
After this quick start, follow the agent path in order:
Run make docs-wayfinding to verify the focused no-secret docs/CLI contract that keeps these website and README commands aligned with the installed CLI.
micro agent demo — print the provider-free first-agent demo command and next docs steps from the installed CLI.
micro agent quickcheck (or micro agent debug) — when scaffold → run → chat → inspect stalls, print the short recovery map before you dive into the full debugging guide.
micro examples — print the maintained provider-free runnable examples in copy/paste order.
micro zero-to-hero — print the maintained one-command no-secret lifecycle harness and runnable examples.
Your First Agent — build a service-backed agent and talk to it with micro chat.
Debugging your agent — use micro agent preflight before micro run, micro agent doctor after micro run, then micro chat and micro inspect agent <name> to recover service registration, tool calls, run history, memory, provider failures, and flow handoffs when the agent surprises you.
0→hero reference path — prove the full scaffold → run → chat → inspect → deploy dry-run lifecycle with commands exercised by make harness.
Write a Service
Create and run a service manually:
micro new helloworld
cd helloworld
micro run
Open http://localhost:8080 to see the dashboard, call endpoints, and chat with your service.
A service is a Go struct with methods. Doc comments and @example tags become tool descriptions for AI agents:
packagemainimport("context""go-micro.dev/v6")typeRequeststruct{Namestring`json:"name"`}typeResponsestruct{Messagestring`json:"message"`}typeSaystruct{}// Hello greets a person by name.// @example {"name": "Alice"}func(h*Say)Hello(ctxcontext.Context,req*Request,rsp*Response)error{rsp.Message="Hello "+req.Namereturnnil}funcmain(){service:=micro.NewService("greeter")service.Handle(new(Say))service.Run()}
micro run gives you:
Dashboard at http://localhost:8080
API Gateway at http://localhost:8080/api/{service}/{method}
Agent Playground at http://localhost:8080/agent
MCP Tools at http://localhost:8080/mcp/tools
Hot Reload — auto-rebuild on file changes
micro new scaffolds a reflection-based service by default — plain Go types, no code generation, so go run . works with nothing else installed. If you prefer Protocol Buffers, add --proto (this requires the protoc toolchain; the command tells you what to install).
Templates are available for common patterns. These use Protocol Buffers, so they need the protoc toolchain (protoc, protoc-gen-go, protoc-gen-micro — micro new prints the install commands if they’re missing):
micro new contacts --template crud
micro new events --template pubsub
micro new gateway --template api
Generate from a Prompt — with an LLM key
After the no-secret path works, set a provider key if you want Go Micro to design services and an agent from a prompt:
exportANTHROPIC_API_KEY=sk-ant-... # or OPENAI_API_KEY, GEMINI_API_KEY, ...micro run --prompt "task management system" --provider anthropic
You’ll see the design, confirm it, and then services plus an agent start:
Use the interactive console, micro run -d plus micro chat, or the agent playground to talk to the generated services.
Before your first provider-backed agent run, check the local path with:
micro agent preflight
The preflight is read-only: it verifies Go 1.24+, the micro binary, provider-key setup, and whether the default micro run gateway port is free, without calling an LLM provider. When a check fails it prints the exact fix plus the next guide to open, so the scaffold → run → chat path stays walkable.
Building Agents
For a complete service-backed walkthrough, start with Your First Agent. If you want to run before you write, use examples/support for the full services → agents → workflows lifecycle or examples/agent-plan-delegate for the smallest multi-agent planning/delegation path.
An Agent is an intelligent layer that manages one or more services:
packagemainimport"go-micro.dev/v6"funcmain(){agent:=micro.NewAgent("task-mgr",micro.AgentServices("task","project"),micro.AgentPrompt("You manage tasks and projects. You understand deadlines, priorities, and assignments."),micro.AgentProvider("anthropic"),micro.AgentAPIKey("sk-ant-..."),)agent.Run()}
An agent is a service — it has a proto-defined Agent.Chat RPC endpoint and registers in the registry like everything else. It:
Discovers its services from the registry
Only sees endpoints from its assigned services (scoped tools)
Maintains conversation memory in the store (persists across restarts)
Is callable via micro call, the interactive console, or any go-micro client
Use it programmatically:
resp,_:=agent.Ask(ctx,"What tasks are overdue for Alice?")fmt.Println(resp.Reply)
Or via the CLI:
micro agent list # list registered agentsmicro call task-mgr Agent.Chat '{"message": "What tasks are overdue?"}'
When multiple agents are registered, the console routes to the right agent automatically.
Event-Driven Flows
A Flow subscribes to a broker topic and triggers an LLM when events arrive. You can define flows in code or run them from the CLI.
In code:
f:=micro.NewFlow("onboard-user",micro.FlowTrigger("events.user.created"),micro.FlowPrompt("New user created: {{.Data}}. Send welcome email and create workspace."),micro.FlowProvider("anthropic"),micro.FlowAPIKey(os.Getenv("MICRO_AI_API_KEY")),)f.Register(service.Options().Registry,service.Options().Broker,service.Client())
From the CLI:
micro flow run --trigger events.user.created --prompt "New user: {{.Data}}. Send welcome email."micro flow exec --prompt "Summarize all open tickets and email the report."
The flow discovers all services as tools and lets the LLM decide which RPCs to call in response to the event.
CLI Workflow
Command
Purpose
micro run --prompt "..."
Generate services + agent, start with interactive console
micro run
Dev mode: hot reload, gateway, interactive console
micro run -d
Detached mode (no console)
micro chat
Standalone chat (when not using micro run)
micro agent list
List registered agents
micro flow run --trigger <topic>
Run an event-driven flow
micro flow exec --prompt "..."
Execute a one-shot flow
micro new myservice
Scaffold a service
micro call service endpoint '{}'
Call a service or agent
micro build
Compile production binaries
micro deploy user@server
Deploy via SSH + systemd
Next Steps
Learn by Example — runnable examples mapped to services, agents, and workflows
This document outlines what hosting looks like for go-micro services, the options available today, and what an ideal hosting platform would provide.
Overview
Go Micro services are compiled Go binaries that communicate via RPC and event-driven messaging. Hosting them requires infrastructure that supports service discovery, inter-service communication, persistent storage, and configuration management. Because go-micro uses a pluggable architecture, the hosting environment can range from a single VPS to a fully orchestrated cluster.
Current Hosting Options
Single VPS or Bare Metal
The simplest approach. Deploy compiled binaries to a Linux server and manage them with systemd. This is the model described in the Deployment Guide.
Good for: Small teams, early-stage projects, predictable workloads.
Server
├── micro@users.service
├── micro@posts.service
├── micro@web.service
└── mdns for discovery
Use micro deploy to push binaries over SSH
systemd handles process supervision and restarts
mDNS provides zero-configuration service discovery on the local host
Run services across several machines. This requires replacing mDNS with a network-aware registry like Consul or Etcd so services can discover each other across hosts.
# Point all services at a shared registryMICRO_REGISTRY=consul MICRO_REGISTRY_ADDRESS=consul.internal:8500
Deploy with micro deploy to each target server
Use a central registry (Consul, Etcd, or NATS) for cross-host discovery
Place a load balancer or API gateway in front of public-facing services
Containers and Kubernetes
Package each service as a Docker image and deploy to a Kubernetes cluster or a simpler container runtime like Docker Compose.
Dockerfile example:
FROMgolang:1.21-alpineASbuildWORKDIR/appCOPY . .RUN go build -o service ./cmd/serviceFROMalpine:3.19COPY --from=build /app/service /serviceENTRYPOINT["/service"]
Kubernetes considerations:
Use the Kubernetes registry plugin or run Consul/Etcd as a StatefulSet
ConfigMaps and Secrets replace environment files
Kubernetes Services and Ingress handle external traffic
Horizontal Pod Autoscaler manages scaling
Liveness and readiness probes map to go-micro health checks
Platform as a Service (PaaS)
Deploy to managed platforms like Railway, Render, or Fly.io. Each service runs as a separate application.
Configuration via platform-provided environment variables
Managed TLS and load balancing out of the box
Use NATS or a hosted registry for service discovery between apps
Limited control over networking and co-location
What a Hosting Platform Needs
A purpose-built platform for go-micro services would integrate with the framework’s core abstractions rather than treating services as generic containers.
Service Discovery
The platform must run or integrate with a supported registry so services find each other automatically.
Environment
Recommended Registry
Single host
mDNS (default, zero config)
Multi-host / cloud
Consul, Etcd, or NATS
Kubernetes
Kubernetes registry plugin
RPC and Messaging
Services communicate over RPC (request/response) and asynchronous messaging (pub/sub). The platform must allow direct service-to-service communication on the configured transport.
Transport: HTTP (default), gRPC, or NATS
Broker: HTTP event broker (default), NATS, or RabbitMQ
Internal traffic should stay on a private network
External traffic flows through a gateway or load balancer
Configuration Management
Each service loads configuration from environment variables, files, or remote sources. The platform should provide:
Per-service environment variables or config files
Secret management with restricted access
Hot-reload support for dynamic configuration changes
Data Storage
go-micro’s store interface supports multiple backends. The platform should provide or connect to durable storage.
Development: In-memory store (default)
Production: Postgres, MySQL, Redis, or other supported backends
Persistent volumes or managed database services for stateful data
Health Checks and Observability
The platform should monitor service health and provide visibility into behavior.
Health endpoints for liveness and readiness
Structured logs collected and searchable
Metrics (request rates, latencies, error rates) scraped or pushed
Distributed tracing across service boundaries
See Observability for details on logs, metrics, and traces.
Security
TLS for all inter-service communication
Service-level authentication and authorization via go-micro’s auth interface
Network isolation between services and the public internet
Secret rotation and audit logging
Scaling
Horizontal scaling: run multiple instances of a service behind the client-side load balancer
The registry tracks all instances; the selector distributes requests
Auto-scaling based on resource usage or request volume
Ideal Platform Architecture
A hosting platform tailored for go-micro would look like this:
Performance — Performance characteristics and tuning
18 - Micro Server (Optional)
The Micro server is an optional web dashboard and authenticated API gateway for production environments. It provides a secure entrypoint for discovering and interacting with services that are already running (e.g., managed by systemd via micro deploy).
micro server does not build, run, or watch services. It only discovers services via the registry and provides a UI/API to interact with them.
micro server vs micro run
micro run
micro server
Purpose
Local development
Production dashboard
Builds services
Yes
No
Runs services
Yes (as child processes)
No (discovers already-running services)
Hot reload
Yes
No
Authentication
Yes (default admin/micro)
Yes (default admin/micro)
Scopes
Yes (/auth/scopes)
Yes (/auth/scopes)
Dashboard
Full gateway UI with auth, scopes, agent
Full dashboard with API explorer, logs, user/token management
Services managed by systemd are discovered via the registry and appear in the dashboard automatically. The server provides the authenticated API and web UI for interacting with them.
When to use it
You have services running in production (via systemd or otherwise) and want a web UI
You need authenticated API access with JWT tokens
You want user management and token revocation
You’re running a shared environment where multiple people interact with services
Go Micro provides built-in support for the Model Context Protocol (MCP), enabling AI agents like Claude to discover and interact with your microservices as tools.
Overview
MCP gateway automatically exposes your microservices as AI-accessible tools through:
Automatic service discovery via the registry
Dynamic tool generation from service endpoints
Stdio transport for local AI tools (Claude Code, etc.)
HTTP/SSE transport for web-based agents
Automatic documentation extraction from Go comments
Quick Start
1. Add Documentation to Your Service
Simply write Go doc comments on your handler methods:
packagemainimport("context""go-micro.dev/v6")typeGreeterServicestruct{}// SayHello greets a person by name. Returns a friendly greeting message.//// @example {"name": "Alice"}func(g*GreeterService)SayHello(ctxcontext.Context,req*HelloRequest,rsp*HelloResponse)error{rsp.Message="Hello "+req.Namereturnnil}typeHelloRequeststruct{Namestring`json:"name" description:"Person's name to greet"`}typeHelloResponsestruct{Messagestring`json:"message" description:"Greeting message"`}funcmain(){service:=micro.NewService("greeter")service.Init()// Register handler - docs extracted automatically from comments!service.Handle(new(GreeterService))service.Run()}
That’s it! Documentation is automatically extracted from your Go comments.
2. Start the MCP Server
Option A: Stdio Transport (for Claude Code)
# Start your servicego run main.go
# In another terminal, start MCP server with stdiomicro mcp serve
Add to Claude Code config (`~/.claude/claude_desktop_config.json`):
MCP tool calls go through the same authentication and scope enforcement as regular API calls. This means you can control which tokens (and therefore which users, services, or AI agents) can invoke which tools.
Restricting MCP Tool Access
Set endpoint scopes — Visit /auth/scopes and set required scopes on service endpoints. For example, set internal on billing.Billing.Charge to restrict it.
Create scoped tokens — Visit /auth/tokens and create tokens with specific scopes:
A token with scope internal can call endpoints requiring internal
A token with scope * has unrestricted access (admin)
A token with no matching scope gets 403 Forbidden
Use the token — Pass it in the Authorization header for API/MCP calls:
# List available MCP tools (requires valid token)curl http://localhost:8080/mcp/tools \
-H "Authorization: Bearer <token>"# Call a specific tool (scope-checked)curl -X POST http://localhost:8080/mcp/call \
-H "Authorization: Bearer <token>"\
-d '{"tool":"greeter.GreeterService.SayHello","input":{"name":"World"}}'
Common MCP Token Patterns
Use Case
Token Scopes
What It Can Do
Internal tooling
internal
Call endpoints tagged with internal scope
Production AI agent
greeter, users
Only call greeter and user service endpoints
Admin / debugging
*
Full access to all tools
Read-only agent
readonly
Call endpoints tagged with readonly scope
Agent Playground
The agent playground at /agent uses the logged-in user’s session token. Scope checks apply based on the scopes of the user’s account. The default admin user has * scope (full access).
MCP Command Line
The `micro mcp` command provides tools for working with MCP:
# Start MCP server (stdio by default)micro mcp serve
# Start with HTTP transportmicro mcp serve --address :3000
# List available toolsmicro mcp list
# Test a specific toolmicro mcp test greeter.GreeterService.SayHello
Transport Options
Stdio - For local AI tools (Claude Code, recommended)
HTTP/SSE - For web-based agents
See examples for complete usage.
Examples
See `examples/mcp/documented` for a complete working example.
Correlation – IDs flowing through all three signals
Logging
The default logger can be replaced. Use env vars to adjust level:
MICRO_LOG_LEVEL=debug go run main.go
Recommended fields:
service – service name
version – release identifier
trace_id – propagated context id
span_id – current operation id
Metrics
Patterns:
Emit counters for request totals
Use histograms for latency
Track error rates per endpoint
Example (pseudo-code):
// Wrap handler to record metricsfuncMetricsWrapper(fnmicro.HandlerFunc)micro.HandlerFunc{returnfunc(ctxcontext.Context,reqmicro.Request,rspinterface{})error{start:=time.Now()err:=fn(ctx,req,rsp)latency:=time.Since(start)metrics.Inc("requests_total",req.Endpoint(),errorLabel(err))metrics.Observe("request_latency_seconds",latency,req.Endpoint())returnerr}}
Tracing
Distributed tracing links calls across services.
Propagation strategy:
Extract trace context from incoming headers
Inject into outgoing RPC calls/broker messages
Create spans per handler and client call
Local Development Strategy
Start with only structured logs. Add metrics when operating multiple services. Introduce tracing once debugging multi-hop latency or failures.
go-micro is designed for developer productivity and ease of use while maintaining good performance for most use cases. This document explains the performance characteristics and trade-offs.
Reflection Usage
go-micro uses Go’s reflection package to enable its core feature: registering any Go struct as a service handler without code generation or boilerplate.
Why Reflection?
// Simple handler registration - no proto files, no code generationtypeGreeterServicestruct{}func(g*GreeterService)SayHello(ctxcontext.Context,req*Request,rsp*Response)error{rsp.Message="Hello "+req.Namereturnnil}server.Handle(server.NewHandler(&GreeterService{}))
This simplicity is only possible with reflection. Alternative approaches (like gRPC or psrpc) require:
Writing .proto files
Running code generators
Implementing generated interfaces
Managing generated code in version control
Performance Impact
Reflection adds approximately 40-60 microseconds (0.04-0.06ms) overhead per RPC call for:
Method discovery and validation (~5μs)
Dynamic method invocation (~30-40μs)
Request/response type construction (~10-15μs)
This totals ~50μs on average, though the exact overhead depends on the complexity of the handler signature and request/response types.
Context: In typical RPC scenarios:
Component
Typical Time
Network I/O
1-10ms
Protobuf serialization
0.1-0.5ms
Business logic
Variable (often 1-100ms+)
Reflection + framework overhead
~0.06ms (0.6-6% of total)
When Reflection Matters
Reflection overhead is only significant when ALL of these conditions are true:
✅ Request rate >100,000 RPS
✅ Business logic <100μs
✅ Local/loopback communication
✅ Sub-millisecond latency requirements
For 99% of applications, database queries, external services, and business logic dominate performance. Reflection is negligible.
Performance Best Practices
1. Profile Before Optimizing
Always measure before assuming reflection is your bottleneck:
# Enable pprof in your serviceimport _ "net/http/pprof"# Profile CPU usagego tool pprof http://localhost:6060/debug/pprof/profile?seconds=30
If reflection shows up as <5% of CPU time, optimizing elsewhere will have more impact.
2. Optimize Business Logic First
Common optimization opportunities (typically 10-100x more impact than removing reflection):
Database queries: Use connection pooling, indexes, query optimization
External API calls: Use caching, batching, async processing
Serialization: Use efficient protobuf instead of JSON
Concurrency: Use goroutines and channels effectively
3. Use Appropriate Transports
go-micro supports multiple transports:
HTTP: Good for debugging, ~1-2ms overhead
gRPC: Binary protocol, ~0.2-0.5ms overhead
In-memory: Development/testing, <0.1ms overhead
Choose based on your deployment:
import"go-micro.dev/v6/server/grpc"// Use gRPC for better performanceservice:=micro.NewService("performance-example",micro.Server(grpc.NewServer()),)
4. Enable Connection Pooling
Reuse connections to avoid handshake overhead:
// Client-side connection pooling (enabled by default)client:=service.Client()
Plugins are scoped under each interface directory within this repository. To use a plugin, import it directly from the corresponding interface subpackage and pass it to your service via options.
For native gRPC compatibility (required for grpcurl, polyglot gRPC clients, etc.), use the gRPC server and client plugins. Note: This is different from the gRPC transport.
Defaults: If you don’t set an implementation, Go Micro uses sensible in-memory or local defaults (e.g., mDNS for registry, HTTP transport, memory broker/store).
Options: Each plugin exposes constructor options to configure addresses, credentials, TLS, etc.
Imports: Only import the plugin you need; this keeps binaries small and dependencies explicit.
23 - Quick Start
Get up and running with go-micro in under 5 minutes.
Install
The recommended way is the precompiled binary — no Go toolchain required:
curl -fsSL https://go-micro.dev/install.sh | sh
Or, if you have Go and prefer to build from source:
go install go-micro.dev/v6/cmd/micro@latest
If the installer finishes but your shell cannot find micro, open Install troubleshooting before creating your first service.
Create Your First Service
# Create a new servicemicro new helloworld
cd helloworld
# Review the generated codels -la
# Run locally with hot reloadmicro run
# Test itcurl -X POST http://localhost:8080/api/helloworld/Helloworld.Call \
-H "Content-Type: application/json"\
-d '{"name": "World"}'
Next Steps
You now have the service half of the services → agents → workflows lifecycle running locally. Keep the on-ramp going in this order:
Install troubleshooting - verify the binary installer or go install, PATH, micro --version, and the no-secret smoke path.
micro agent demo - print the provider-free first-agent demo command and the next docs steps from the installed CLI.
micro agent quickcheck (or micro agent debug) - print the short recovery map when scaffold → run → chat → inspect stalls.
micro examples - print the maintained provider-free runnable examples in copy/paste order.
micro zero-to-hero - print the maintained one-command no-secret lifecycle harness and runnable examples.
Your First Agent - turn this service into an agent-callable tool, chat with it, and learn the micro agent preflight → micro run → micro chat loop.
Debugging your agent - use micro inspect agent <name> to inspect service registration, tool calls, run history, memory, provider failures, and flow handoffs when the agent does something surprising.
0→hero Reference - walk the maintained scaffold → run → chat → inspect → deploy dry-run path that proves services, agents, and workflows together.
import("context""go-micro.dev/v6")funcmain(){service:=micro.NewService("subscriber")// Subscribe to eventsmicro.RegisterSubscriber("user.created",service.Server(),func(ctxcontext.Context,event*UserCreatedEvent)error{// Handle the event here.returnnil},)service.Run()}
The registry is responsible for service discovery in Go Micro. It allows services to register themselves and discover other services.
Features
Service registration and deregistration
Service lookup
Watch for changes
Implementations
Go Micro supports multiple registry backends, including:
MDNS (default)
Consul
Etcd
NATS
You can configure the registry when initializing your service.
Plugins Location
Registry plugins live in this repository under go-micro.dev/v6/registry/<plugin> (e.g., consul, etcd, nats). Import the desired package and pass it via micro.Registry(...).
Configure via environment
MICRO_REGISTRY=etcd MICRO_REGISTRY_ADDRESS=127.0.0.1:2379 micro server
Common variables:
MICRO_REGISTRY: selects the registry implementation (mdns, consul, etcd, nats).
MICRO_REGISTRY_ADDRESS: comma-separated list of registry addresses.
Backend-specific variables:
Etcd: ETCD_USERNAME, ETCD_PASSWORD for authenticated clusters.
Example Usage
Here’s how to use a custom registry (e.g., Consul) in your Go Micro service:
Go Micro is a framework for building agents and services in Go. An agent is a distributed system — it discovers services, calls them, holds state, and recovers from failure — so building an agent is building a service. The roadmap has two jobs: make agentic development excellent, and make the developer experience around it excellent. Nothing else.
Where we are (v6)
The foundation is in place:
Services — register, discover, RPC, events; every endpoint is automatically an MCP tool.
Agents — a model with memory and tools that manages services, with plan, delegate, guardrails (MaxSteps, LoopLimit, ApproveTool), tool-execution middleware (WrapTool), run metadata, checkpoint/resume, and OpenTelemetry run spans built in.
Flows — durable, event-driven workflows: ordered steps that checkpoint and resume after a crash.
Interop — the MCP gateway (services as tools) and the A2A gateway (agents as agents, both directions, including A2A streaming, push notifications, and multi-turn continuation), both generated from the registry; x402 for paid tools.
Secure by default — TLS verification on, state scoped per component.
Principles
These constrain everything below:
Build into what people run, never a separate product. No hosted platform, no enterprise edition. Improvements go deeper into the framework, not beside it.
CLI-first. The CLI is the experience. Any UI must be genuinely good and earn its place; bloat gets trimmed, not maintained.
The getting-started flow is a contract.0→1 (scaffold → run → call) and 0→hero (the ~10 steps to a working multi-agent system) must always work, and every change is checked against them.
Interaction is as important as running. Talking to an agent, inspecting runs and history — end to end, not just “it starts.”
Battle-tested. Works across every provider, fails safely, and is observable.
Now — hardening (“functional on every level”)
The priority is that what exists works everywhere, under real conditions.
Cross-provider conformance. Each of the seven providers implements its own tool-call loop; today only the mock and one live provider are exercised. A suite that runs the same agent scenario (tool-calling, multi-step, plan/delegate, guardrails) across every provider, gated on keys, on a schedule.
Failure & resilience. Provider timeouts, rate limits, and cancellation mid-run; deadline/context propagation through the agent loop; retry and backoff at the model call.
The getting-started contract. Define and CI-verify the 0→1 and 0→hero flows so they can’t silently break.
Shipped agent depth
Durable agent loop. Opt-in Checkpoint support now lets agent Ask and
streaming runs persist, list pending work, and resume without replaying completed
tool calls. Human-input pauses resume through explicit input helpers.
Agent observability.RunInfo now feeds OpenTelemetry spans and events for
agent runs, model turns, tool calls, retries, delegation lineage, and resume
checkpoints so production runs are traceable.
Next — agentic depth
Streaming. Broaden provider-backed ai.Stream coverage and keep chat plus A2A message/stream working end to end for real chat and long-task UX.
Resume operations polish. Keep improving CLI/docs breadcrumbs for finding
pending agent runs and deciding whether to call resume, resume-input, or stream
resume in production.
Observability hardening. Keep span attributes and run inspection coherent
across agents, flows, and gateways as more providers and workflow paths are
exercised.
Later
Memory management — summarization and retrieval (RAG) beyond a fixed buffer.
Human-in-the-loop — broaden pause/resume UX around input-required runs and approvals.
A2A — richer live-stream reconnection (tasks/resubscribe) and input-required handoffs.
Developer experience (ongoing)
The CLI inner loop — scaffold → run → chat → inspect (runs/history) → deploy, made seamless. This is the main lever for “dramatically improve the experience.”
UI discipline — keep only high-value, well-built surfaces; trim or cut the rest. The web UI should never be a worse version of the CLI.
Examples & a real-world build — a maintained example that builds something real with the framework, doubling as the 0→hero reference and continuous battle-testing.
Docs in lockstep — the getting-started guide tracks the code on every change.
How it’s sustained
The framework is the product. It’s funded by sponsorship from the people and companies who run it — not a hosted service, not an enterprise tier, not venture funding. The model is deliberate: keep refining the framework, aligned users adopt and depend on it, and that dependence funds the work. (See blog/27 for why.)
Feedback
Open an issue or start a discussion on GitHub, or join the Discord.
26 - Store
The store provides a pluggable interface for data storage in Go Micro.
Plugins are scoped under go-micro.dev/v6/store/<plugin>.
Configure the store in code or via environment variables.
Example Usage
Here’s how to use the store in your Go Micro service:
packagemainimport("go-micro.dev/v6""go-micro.dev/v6/store""log")funcmain(){service:=micro.NewService("store-example")service.Init()// Write a recordiferr:=store.Write(&store.Record{Key:"foo",Value:[]byte("bar")});err!=nil{log.Fatal(err)}// Read a recordrecs,err:=store.Read("foo")iferr!=nil{log.Fatal(err)}log.Printf("Read value: %s",string(recs[0].Value))}
After comprehensive analysis of go-micro’s reflection usage and comparison with livekit/psrpc (the referenced example), we recommend AGAINST removing reflection from go-micro.
Key Findings
1. Reflection is Fundamental to go-micro’s Architecture
Reflection enables go-micro’s core value proposition:
// Simple, idiomatic Go - no proto files, no code generationtypeMyServicestruct{}func(s*MyService)SayHello(ctxcontext.Context,req*Request,rsp*Response)error{rsp.Message="Hello "+req.Namereturnnil}server.Handle(server.NewHandler(&MyService{}))
This requires reflection. There is no way to achieve this simplicity with generics or code generation.
2. livekit/psrpc Uses a Completely Different Architecture
psrpc avoids reflection through code generation from proto files:
Write .proto service definitions
Run protoc --psrpc_out=. to generate code
Implement generated interfaces
Register via generated registration functions
This is fundamentally incompatible with go-micro’s “register any struct” design.
After thorough evaluation comparing go-micro with livekit/psrpc and analyzing the feasibility of removing reflection, we’ve determined this would require a fundamental architectural redesign incompatible with go-micro’s goals.
Key findings:
psrpc avoids reflection through code generation - Requires .proto files and generated interfaces, a completely different architecture from go-micro
go-micro’s strength is “register any struct” - This requires runtime type introspection (reflection) and cannot be achieved with Go generics or code generation
Reflection overhead is ~50μs per RPC, typically <5% of total latency in real-world applications where network I/O (1-10ms) and business logic dominate
Removing reflection would:
Break all existing code (100% breaking change)
Require 6-12 months of development
Eliminate go-micro’s key advantage (simplicity)
Provide <5% performance improvement for most users
For users needing maximum performance, alternatives already exist:
performance.md - Performance best practices and when to consider alternatives
Recommendation: Keep reflection as a deliberate architectural choice that enables go-micro’s simplicity and developer productivity. Profile before optimizing, and consider code-generation-based alternatives (gRPC/psrpc) only if profiling proves reflection is genuinely a bottleneck.
Closing as “won’t fix” - reflection is an intentional design decision, not a technical limitation.
Next Steps
Add this comment to the original issue
Close the issue as “won’t fix”
Consider adding a FAQ entry about reflection and performance
Link to the new documentation from the main website
Prepared by: GitHub Copilot Agent Review: Ready for maintainer decision Impact: Documentation only, no code changes
28 - TLS Security Migration Guide
Overview
Go Micro v6 verifies TLS certificates by default. This guide is for teams
upgrading from v5, where TLS verification was disabled by default for backward
compatibility.
Current Status (v6)
Default Behavior: TLS certificate verification is enabled by default
(InsecureSkipVerify: false).
What changed from v5: v5 allowed MICRO_TLS_SECURE=true to opt into
certificate verification. In v6, secure verification is the default and
MICRO_TLS_SECURE is no longer used.
Development escape hatch: for local self-signed certificates only, set
MICRO_TLS_INSECURE=true or provide an explicit insecure TLS config.
Migration Path from v5
1. Remove the old opt-in flag
Delete any use of the v5-only environment variable:
unset MICRO_TLS_SECURE
No replacement is required for production: verification is already on in v6.
2. Use the default secure config
Most services need no TLS-specific code. If you configure TLS explicitly, use a standard crypto/tls config with verification enabled:
import("crypto/tls""go-micro.dev/v6/broker")// Create broker with certificate verification enabled.b:=broker.NewHttpBroker(broker.TLSConfig(&tls.Config{MinVersion:tls.VersionTLS12}),)
3. Provide a custom trust root when needed
For private CAs, provide your own TLS configuration:
import("crypto/tls""crypto/x509""go-micro.dev/v6/broker""os")// Load CA certificatescaCert,err:=os.ReadFile("/path/to/ca-cert.pem")iferr!=nil{log.Fatal(err)}caCertPool:=x509.NewCertPool()caCertPool.AppendCertsFromPEM(caCert)// Create custom TLS configtlsConfig:=&tls.Config{RootCAs:caCertPool,MinVersion:tls.VersionTLS12,}// Create broker with custom configb:=broker.NewHttpBroker(broker.TLSConfig(tlsConfig),)
4. Use insecure mode only for local development
If a development environment still uses self-signed certificates that are not in
your trust store, opt out explicitly:
The default changed at the v6 major-version boundary. Before rolling v6 into a
fleet that uses TLS, verify that:
All services present certificates trusted by their peers.
Private or self-signed CAs are installed consistently on every host.
Certificates include the DNS names or IP subject alternative names used by
clients.
Any deliberate development-only insecure settings are excluded from
production manifests.
Recommended Approach
Test in Staging with the same certificate chain and service names used in
production.
Remove v5 flags such as MICRO_TLS_SECURE; they no longer control v6.
Monitor for Issues: watch for TLS handshake failures or certificate
validation errors.
Use explicit insecure mode only in dev when a short-lived environment
cannot yet provide trusted certificates.
Multi-Host/Multi-Process Considerations
Certificate Trust: With secure mode as the default, ensure:
All hosts trust the same root CAs.
Self-signed certificates are properly distributed if used.
Certificate validity periods are monitored.
Certificate chains are complete.
Service Mesh Alternative: Consider using a service mesh (Istio, Linkerd, etc.) for:
Automatic mTLS between services
Certificate management and rotation
No application code changes required
Testing Your Migration
Verify Secure Mode is Active
packagemainimport("crypto/tls""fmt")funcmain(){config:=&tls.Config{MinVersion:tls.VersionTLS12}fmt.Printf("InsecureSkipVerify: %v (should be false)\n",config.InsecureSkipVerify)}
Test Certificate Validation
Create a test service and verify it:
Accepts valid certificates
Rejects invalid/self-signed certificates (when not in CA)
Properly validates certificate chains
Common Issues and Solutions
Issue: “x509: certificate signed by unknown authority”
Cause: The server certificate is not signed by a trusted CA
Solution:
Add the CA certificate to the trusted root CAs
Use a properly signed certificate
For development only: use MICRO_TLS_INSECURE=true or an explicit insecure TLS config
Issue: “x509: certificate has expired”
Cause: Server certificate has expired
Solution:
Renew the certificate
Implement certificate rotation
Monitor certificate expiry dates
Issue: Services can’t communicate after upgrading to v6
Cause: Certificates that v5 accepted by default are now verified.
Solution:
Ensure all services use certificates from a trusted CA
Distribute CA certificates to all nodes
Verify certificate SANs match service addresses
Use insecure mode only as a temporary local-development workaround
Questions?
For issues or questions about TLS security migration, open an issue on GitHub or
check the documentation at https://go-micro.dev/docs/.
29 - TLS Security Update - Important Information
What Changed
Go Micro v6 verifies TLS certificates by default. This completes the v5 security
migration where verification was opt-in.
Current Behavior (v6.x)
Default: TLS certificate verification is enabled.
MICRO_TLS_SECURE was a v5 opt-in flag and is no longer used.
For local development with untrusted self-signed certificates, opt out
explicitly with MICRO_TLS_INSECURE=true or an explicit insecure TLS config.
Production Recommendation
For production deployments:
Use CA-signed certificates or distribute your private CA to every host.
Remove old MICRO_TLS_SECURE settings from v5-era manifests.
Do not set MICRO_TLS_INSECURE=true in production.
Consider service mesh mTLS (Istio, Linkerd) if certificate lifecycle should be
managed outside the application.
Migration Timeline
v5.x: Insecure by default, opt-in security via MICRO_TLS_SECURE=true.
v6.x current: Secure by default; use MICRO_TLS_INSECURE=true only for an
explicit development opt-out.
The transport layer is responsible for communication between services.
Features
Pluggable transport implementations
Secure and efficient communication
Implementations
Supported transports include:
HTTP (default)
NATS (go-micro.dev/v6/transport/nats)
gRPC (go-micro.dev/v6/transport/grpc)
Memory (go-micro.dev/v6/transport/memory)
Important: Transport vs Native gRPC
The gRPC transport uses gRPC as an underlying communication protocol, similar to how NATS or RabbitMQ might be used. It does not provide native gRPC compatibility with tools like grpcurl or standard gRPC clients generated by protoc.
If you need native gRPC compatibility (to use grpcurl, polyglot gRPC clients, etc.), you must use the gRPC server and client packages instead: