This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Migration Guides

Step-by-step guides for migrating to Go Micro from other frameworks.

Migration Guides

Step-by-step guides for migrating to Go Micro from other frameworks.

Available Guides

Coming Soon

We’re working on additional migration guides:

  • From go-kit - Migrate from Go kit microservices framework
  • From Standard Library - Upgrade from net/http and net/rpc
  • From Gin/Echo - Transition from HTTP-only frameworks
  • From Micro v3 - Upgrade from older Go Micro versions

Why Migrate to Go Micro?

  • Pluggable Architecture - Swap components without changing code
  • Zero Configuration - Works out of the box with sensible defaults
  • Progressive Enhancement - Start simple, add complexity when needed
  • Unified Abstractions - Registry, transport, broker, store all integrated
  • Active Development - Regular updates and community support

Need Help?

1 - Add MCP to Existing Services

Add MCP to Existing Services

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.

Add a single option to your service constructor:

import "go-micro.dev/v6/gateway/mcp"

func main() {
    service := micro.NewService("myservice",
        mcp.WithMCP(":3001"),  // Add this line
    )
    service.Init()
    // ... register handlers as before
    service.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 service
go mcp.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 normally
go run .

# In another terminal, start the MCP gateway
micro 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.

Step 1: Add Doc Comments

Before:

func (s *Users) Get(ctx context.Context, req *GetRequest, rsp *GetResponse) error {

After:

// 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(ctx context.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

type GetRequest struct {
    ID string `json:"id" description:"User ID in UUID format"`
}

type GetResponse struct {
    Name   string `json:"name" description:"Display name"`
    Email  string `json:"email" description:"Primary email address"`
    Active bool   `json:"active" description:"Whether the account is active"`
}

Step 3: Add Auth Scopes (Optional)

Restrict which agents can call which endpoints:

handler := service.Server().NewHandler(
    new(Users),
    server.WithEndpointScopes("Users.Delete", "users:admin"),
    server.WithEndpointScopes("Users.Get", "users:read"),
)

Then configure the MCP gateway with auth:

mcp.ListenAndServe(":3001", mcp.Options{
    Registry: service.Options().Registry,
    Auth:     authProvider,
    Scopes: map[string][]string{
        "myservice.Users.Delete": {"users:admin"},
        "myservice.Users.Get":    {"users:read"},
    },
})

Using with Claude Code

Once your service is running with MCP, connect it to Claude Code:

# Option A: stdio transport (recommended for local dev)
micro mcp serve

# Option B: Add to Claude Code settings
{
  "mcpServers": {
    "my-services": {
      "command": "micro",
      "args": ["mcp", "serve"]
    }
  }
}

Verify It Works

# List all tools the MCP gateway exposes
curl http://localhost:3001/mcp/tools | jq

# Test a specific tool
curl -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

Next Steps

2 - Migrating from gRPC

Step-by-step guide to migrating existing gRPC services to Go Micro.

Why Migrate?

Go Micro adds:

  • Built-in service discovery
  • Client-side load balancing
  • Pub/sub messaging
  • Multiple transport options
  • Unified tooling

You keep:

  • Your proto definitions
  • gRPC performance (via gRPC transport)
  • Type safety
  • Streaming support

Migration Strategy

Phase 1: Parallel Running

Run Go Micro alongside existing gRPC services

Phase 2: Gradual Migration

Migrate services one at a time

Phase 3: Complete Migration

All services on Go Micro

Step-by-Step Migration

1. Existing gRPC Service

// proto/hello.proto
syntax = "proto3";

package hello;
option go_package = "./proto;hello";

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}
// Original gRPC server
package main

import (
    "context"
    "log"
    "net"
    "google.golang.org/grpc"
    pb "myapp/proto"
)

type server struct {
    pb.UnimplementedGreeterServer
}

func (s *server) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloReply, error) {
    return &pb.HelloReply{Message: "Hello " + req.Name}, nil
}

func main() {
    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-micro
go install go-micro.dev/v6/cmd/protoc-gen-micro@latest

# Generate both gRPC and Go Micro code
protoc --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 server
package main

import (
    "context"
    "go-micro.dev/v6"
    "go-micro.dev/v6/server"
    pb "myapp/proto"
)

type Greeter struct{}

func (s *Greeter) SayHello(ctx context.Context, req *pb.HelloRequest, rsp *pb.HelloReply) error {
    rsp.Message = "Hello " + req.Name
    return nil
}

func main() {
    svc := micro.NewService("greeter",
    )
    svc.Init()

    pb.RegisterGreeterHandler(svc.Server(), new(Greeter))

    if err := svc.Run(); err != nil {
        log.Fatal(err)
    }
}

Key differences:

  • No manual port binding (Go Micro handles it)
  • Automatic service registration
  • Returns error, response via pointer parameter

4. Migrate Client

Original gRPC client:

conn, _ := grpc.Dial("localhost:50051", grpc.WithInsecure())
defer conn.Close()

client := pb.NewGreeterClient(conn)
rsp, err := client.SayHello(context.Background(), &pb.HelloRequest{Name: "John"})

Go Micro client:

svc := micro.NewService("client")
svc.Init()

client := pb.NewGreeterService("greeter", svc.Client())
rsp, err := client.SayHello(context.Background(), &pb.HelloRequest{Name: "John"})

Benefits:

  • No hardcoded addresses
  • Automatic service discovery
  • Client-side load balancing
  • Automatic retries

5. Keep gRPC Transport (Optional)

Use gRPC as the underlying transport:

import (
    "go-micro.dev/v6"
    "go-micro.dev/v6/client"
    "go-micro.dev/v6/server"
    grpcclient "go-micro.dev/v6/client/grpc"
    grpcserver "go-micro.dev/v6/server/grpc"
)

svc := micro.NewService("greeter",
    micro.Client(grpcclient.NewClient()),
    micro.Server(grpcserver.NewServer()),
)

This gives you:

  • gRPC performance
  • Go Micro features (discovery, load balancing)
  • Compatible with existing gRPC clients

Streaming Migration

Original gRPC Streaming

service Greeter {
  rpc StreamHellos (stream HelloRequest) returns (stream HelloReply) {}
}
func (s *server) StreamHellos(stream pb.Greeter_StreamHellosServer) error {
    for {
        req, err := stream.Recv()
        if err == io.EOF {
            return nil
        }
        if err != nil {
            return err
        }
        
        stream.Send(&pb.HelloReply{Message: "Hello " + req.Name})
    }
}

Go Micro Streaming

func (s *Greeter) StreamHellos(ctx context.Context, stream server.Stream) error {
    for {
        var req pb.HelloRequest
        if err := stream.Recv(&req); err != nil {
            return err
        }
        
        if err := stream.Send(&pb.HelloReply{Message: "Hello " + req.Name}); err != nil {
            return err
        }
    }
}

Service Discovery Migration

Before (gRPC with Consul)

// Manually register with Consul
config := 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 shutdown
defer client.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 shutdown
svc.Run()

Load Balancing Migration

Before (gRPC with custom LB)

// Need external load balancer or custom implementation
// Example: round-robin DNS, Envoy, nginx

After (Go Micro)

import "go-micro.dev/v6/selector"

// Client-side load balancing built-in
svc := micro.NewService("greeter",
    micro.Selector(selector.NewSelector(
        selector.SetStrategy(selector.RoundRobin),
    )),
)

Gradual Migration Path

1. Start with New Services

New services use Go Micro, existing services stay on gRPC.

// New Go Micro service can call gRPC services
// Configure gRPC endpoints directly
grpcConn, _ := 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 service
type GRPCAdapter struct {
    microClient pb.GreeterService
}

func (a *GRPCAdapter) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloReply, error) {
    return a.microClient.SayHello(ctx, req)
}

// Register adapter as gRPC server
s := 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 choose
svc := micro.NewService("greeter")

// Or specify
svc := micro.NewService("greeter",
    micro.Address(":50051"),
)

Service Not Found

Check registry:

# Consul
curl http://localhost:8500/v1/catalog/services

# Or use micro CLI
micro services

Different Serialization

gRPC uses protobuf by default. Go Micro supports multiple codecs.

Ensure both use protobuf:

import "go-micro.dev/v6/codec/proto"

svc := micro.NewService("greeter",
    micro.Codec("application/protobuf", proto.Marshaler{}),
)

Performance Comparison

ScenariogRPCGo Micro (HTTP)Go Micro (gRPC)
Simple RPC~25k req/s~20k req/s~24k req/s
With DiscoveryN/A~18k req/s~22k req/s
Streaming~30k msg/s~15k msg/s~28k msg/s

Go Micro with gRPC transport performs similarly to pure gRPC

Next Steps

Need Help?

3 - Migrating from v5 to v6

v6 is a small, mechanical upgrade. The bulk of it is the Go module path; the behavioral changes are two, both with a one-line fix.

1. Module path: go-micro.dev/v6

Go puts the major version in the import path, so every import changes:

// before
import "go-micro.dev/v5"
import "go-micro.dev/v5/server"

// after
import "go-micro.dev/v6"
import "go-micro.dev/v6/server"

A repo-wide find/replace does it:

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:

service := micro.NewService("greeter", micro.Address(":8080"))
agent   := micro.NewAgent("task-mgr", micro.AgentServices("task"))
flow    := micro.NewFlow("onboard", micro.FlowTrigger("events.user.created"))
  • 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.