项目文件夹

文件
Asim Aslam 76bfeae456 Claude/update docs roadmap f zd2 j (#2880)
* docs: update all four documentation guides and mark Q2 complete

- ai-native-services: add WithMCP one-liner, standalone gateway,
  WebSocket client example, and OpenTelemetry observability section
- mcp-security: add OTel distributed tracing, WebSocket authentication
  (connection-level and per-message), DeniedReason audit field
- tool-descriptions: add manual overrides with WithEndpointDocs and
  export formats section
- agent-patterns: add LangChain/LlamaIndex SDK pattern and standalone
  gateway production pattern with Docker example
- Update roadmap: mark Q2 documentation as complete, Q2 at 100%
- Update status: reflect all recent completions, shift priorities

https://claude.ai/code/session_01GkduEhcrqcG45rdfYh8dAc

* feat: add agent demo example and blog post

Add examples/agent-demo with a multi-service project management app
(projects, tasks, team) that demonstrates AI agents interacting with
Go Micro services through MCP. Includes seed data and example prompts.

Add blog post 4 "Agents Meet Microservices: A Hands-On Demo" walking
through the example code and showing cross-service agent workflows.

https://claude.ai/code/session_01GkduEhcrqcG45rdfYh8dAc

* feat: enable multiple services in a single binary

Remove global state mutations from service and cmd option functions so
that configuring one service no longer overwrites another's settings.

Key changes:
- service/options.go: remove all DefaultXxx global writes from option
  functions; newOptions() now creates fresh Server, Client, Store, and
  Cache per service while sharing Registry, Broker, and Transport
- cmd/cmd.go: newCmd() uses local copies instead of pointers to package
  globals; Before() no longer mutates DefaultXxx vars
- cmd/options.go: remove global mutations from all option functions
- service/service.go: export ServiceImpl type for cross-package use
- service/group.go: new Group type for multi-service lifecycle
- micro.go: add Start/Stop to Service interface, expose Group and
  NewGroup convenience function
- examples/multi-service: working example with two services

https://claude.ai/code/session_01GkduEhcrqcG45rdfYh8dAc

* docs: highlight multi-service binary support

Add multi-service section to README with code example, update features
list, add to examples index, and note in status summary.

https://claude.ai/code/session_01GkduEhcrqcG45rdfYh8dAc

* feat: unify service API and clean up developer experience

- Unified service creation: micro.New("name", opts...) as canonical API
- Clean handler registration: service.Handle(handler, opts...) accepts
  server.HandlerOption args directly, no need to reach through Server()
- Unexported serviceImpl: users interact through Service interface only
- Service groups use Service interface (not concrete type)
- Fixed Stop() to properly propagate BeforeStop/AfterStop errors
- Fixed store init: error-level log instead of fatal on init failure
- Updated all examples to use consistent patterns
- Updated README, getting-started, MCP docs, and guides
- Added blog post about the DX cleanup

https://claude.ai/code/session_01GkduEhcrqcG45rdfYh8dAc

* fix: add blog post 5 to blog index

Blog post 5 (Developer Experience Cleanup) existed as a file but was
missing from the blog index page.

https://claude.ai/code/session_01GkduEhcrqcG45rdfYh8dAc

* feat: make micro new generate MCP-enabled services by default

- main.go template includes mcp.WithMCP(":3001") by default
- Handler template has agent-friendly doc comments with @example tags
- Proto template has descriptive field comments
- README includes MCP usage, Claude Code config, and tool description tips
- Makefile adds mcp-tools, mcp-test, mcp-serve targets
- go.mod updated to Go 1.22
- Added --no-mcp flag to opt out of MCP integration
- Post-create output shows MCP endpoint URLs

https://claude.ai/code/session_01GkduEhcrqcG45rdfYh8dAc

* docs: add MCP migration guide and troubleshooting guide

- Migration guide: 3 approaches to add MCP to existing services
  (WithMCP one-liner, standalone gateway, CLI)
- Troubleshooting guide: common issues with agents, WebSocket,
  Claude Code, auth, rate limiting, and performance

https://claude.ai/code/session_01GkduEhcrqcG45rdfYh8dAc

* refactor: rename model/ package to ai/ for AI model providers

The model/ package name conflicted with the conventional use of "model"
for data models. Renamed to ai/ which better describes the package's
purpose (AI provider abstraction for Anthropic, OpenAI, etc.) and frees
up model/ for future data model layer use.

- Rename model/ → ai/ with package name change
- Update all Go imports from go-micro.dev/v5/model to go-micro.dev/v5/ai
- Update cmd/micro/server/server.go references (model.X → ai.X)
- Update all documentation and roadmap references
- All tests pass, CLI builds successfully

https://claude.ai/code/session_01GkduEhcrqcG45rdfYh8dAc

* feat: add model package for typed data access with CRUD and queries

New model/ package provides a typed data model layer using Go generics.
Supports structured CRUD operations, WHERE filters, ordering, pagination,
and automatic schema creation from struct tags.

Three backends:
- memory: in-memory for development and testing
- sqlite: embedded SQL for dev and single-node production
- postgres: full PostgreSQL for production deployments

Key features:
- Generic Model[T] with Create/Read/Update/Delete/List/Count
- Query builder: Where(), WhereOp(), OrderAsc/Desc(), Limit(), Offset()
- Struct tags: model:"key" for primary key, model:"index" for indexes
- Auto table creation from struct schema
- 19 tests passing across memory and sqlite backends

https://claude.ai/code/session_01GkduEhcrqcG45rdfYh8dAc

* feat: add model code generation to protoc-gen-micro

Extend the micro plugin to generate model structs from proto messages
annotated with // @model. Generated alongside client/server code in
the same .pb.micro.go file.

For a proto message like:
  // @model
  message User { string id = 1; string name = 2; }

Generates:
- UserModel struct with model:"key" and json tags
- NewUserModel(db) factory returning *model.Model[UserModel]
- UserModelFromProto(*User) *UserModel converter
- (*UserModel).ToProto() *User converter

Supports @model(table=custom_table, key=custom_field) options.
Adds GetComments() to generator for plugin comment inspection.

https://claude.ai/code/session_01GkduEhcrqcG45rdfYh8dAc

* feat: add Model() to Service interface for Client/Server/Model trifecta

Every service now exposes Client(), Server(), and Model() — call services,
handle requests, and save/query data from the same interface. Includes
README docs, blog post, and a full model guide on the docs site.

https://claude.ai/code/session_01GkduEhcrqcG45rdfYh8dAc

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-04 13:13:34 +00:00

11 KiB

layout
layout
default

Building AI-Native Services

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/v5/cmd/micro@v5.16.0

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:

package main

import "context"

// Request types with description tags for AI agents
type Task struct {
    ID          string `json:"id" description:"Unique task identifier"`
    Title       string `json:"title" description:"Short task title (max 100 chars)"`
    Description string `json:"description" description:"Detailed task description"`
    Status      string `json:"status" description:"Task status: todo, in_progress, or done"`
    Assignee    string `json:"assignee,omitempty" description:"Username of assigned person"`
}

type CreateRequest struct {
    Title       string `json:"title" description:"Task title (required, max 100 chars)"`
    Description string `json:"description" description:"Detailed description of the task"`
    Assignee    string `json:"assignee,omitempty" description:"Username to assign the task to"`
}

type CreateResponse struct {
    Task *Task `json:"task" description:"The newly created task"`
}

type GetRequest struct {
    ID string `json:"id" description:"Task ID to retrieve"`
}

type GetResponse struct {
    Task *Task `json:"task" description:"The requested task"`
}

type ListRequest struct {
    Status string `json:"status,omitempty" description:"Filter by status: todo, in_progress, done (optional)"`
}

type ListResponse struct {
    Tasks []*Task `json:"tasks" description:"List of matching tasks"`
}

type UpdateRequest struct {
    ID     string `json:"id" description:"Task ID to update"`
    Status string `json:"status" description:"New status: todo, in_progress, or done"`
}

type UpdateResponse struct {
    Task *Task `json:"task" description:"The updated task"`
}

type DeleteRequest struct {
    ID string `json:"id" description:"Task ID to delete"`
}

type DeleteResponse struct {
    Deleted bool `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.

type TaskService struct {
    tasks map[string]*Task
    nextID int
}

// 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(ctx context.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] = task
    rsp.Task = task
    return nil
}

// 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(ctx context.Context, req *GetRequest, rsp *GetResponse) error {
    task, ok := t.tasks[req.ID]
    if !ok {
        return fmt.Errorf("task %s not found", req.ID)
    }
    rsp.Task = task
    return nil
}

// 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(ctx context.Context, req *ListRequest, rsp *ListResponse) error {
    for _, task := range t.tasks {
        if req.Status == "" || task.Status == req.Status {
            rsp.Tasks = append(rsp.Tasks, task)
        }
    }
    return nil
}

// 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(ctx context.Context, req *UpdateRequest, rsp *UpdateResponse) error {
    task, ok := t.tasks[req.ID]
    if !ok {
        return fmt.Errorf("task %s not found", req.ID)
    }
    task.Status = req.Status
    rsp.Task = task
    return nil
}

// 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(ctx context.Context, req *DeleteRequest, rsp *DeleteResponse) error {
    if _, ok := t.tasks[req.ID]; !ok {
        return fmt.Errorf("task %s not found", req.ID)
    }
    delete(t.tasks, req.ID)
    rsp.Deleted = true
    return nil
}

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:

package main

import (
    "context"
    "fmt"

    "go-micro.dev/v5"
    "go-micro.dev/v5/server"
)

func main() {
    service := micro.New("tasks", micro.Address(":8081"))
    service.Init()

    service.Handle(
        &TaskService{tasks: make(map[string]*Task)},
        // Read operations: any authenticated agent
        server.WithEndpointScopes("TaskService.Get", "tasks:read"),
        server.WithEndpointScopes("TaskService.List", "tasks:read"),
        // Write operations: agents with write scope
        server.WithEndpointScopes("TaskService.Create", "tasks:write"),
        server.WithEndpointScopes("TaskService.Update", "tasks:write"),
        // Delete: admin only
        server.WithEndpointScopes("TaskService.Delete", "tasks:admin"),
    )

    service.Run()
}

Step 5: Run with MCP

There are three ways to run your service with MCP enabled.

micro run

Your service is now available at:

Option B: WithMCP (One-Liner for Library Users)

Add MCP to your service with a single option:

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

func main() {
    service := micro.New("tasks",
        mcp.WithMCP(":3000"), // MCP gateway starts automatically
    )
    service.Init()
    // register handlers...
    service.Run()
}

This starts the MCP gateway on port 3000 alongside your service. All registered handlers are automatically exposed as MCP tools.

Option C: Standalone MCP Gateway

For production, run the MCP gateway as a separate process that discovers all services:

micro-mcp-gateway \
  --registry consul \
  --registry-address consul:8500 \
  --address :3000 \
  --auth jwt \
  --rate-limit 10

See the standalone gateway docs for more.

Use with Claude Code

# Start MCP server for Claude Code (stdio transport)
micro mcp serve

Add to your Claude Code config:

{
  "mcpServers": {
    "tasks": {
      "command": "micro",
      "args": ["mcp", "serve"]
    }
  }
}

Now Claude can manage your tasks:

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):

const ws = new WebSocket("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 tools
micro mcp list

# Test a specific tool
micro mcp test tasks.TaskService.Create

# Generate documentation
micro mcp docs

# Export for LangChain
micro mcp export --format langchain

Step 7: Add Observability (Optional)

Enable OpenTelemetry tracing to see every agent tool call as a distributed trace:

import (
    "go.opentelemetry.io/otel"
    "go-micro.dev/v5/gateway/mcp"
)

go mcp.ListenAndServe(":3000", mcp.Options{
    Registry:      service.Options().Registry,
    TraceProvider: otel.GetTracerProvider(),
})

Each tool call generates a span with attributes:

  • mcp.tool.name — which tool was called
  • mcp.transport — HTTP, WebSocket, or stdio
  • mcp.account.id — who called it
  • mcp.auth.allowed — whether it was permitted

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/v5/ai"
    _ "go-micro.dev/v5/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

Next Steps