---
title: "Agents Meet Microservices: A Hands-On Demo"
linkTitle: "Agents Meet Microservices"
date: 2026-03-04
description: "Build three microservices and let an AI agent manage them with natural language — no glue code, no API wrappers, just Go comments"
category:
- tutorial
tags:
- AI
- Agents
aliases:
  - /blog/4
---
We talk a lot about AI-native microservices. Time to show it. In this post we'll build three services — projects, tasks, and team — and then hand them to an AI agent. The agent will create projects, assign tasks, and query team skills using nothing but natural language.

No API wrappers. No tool definitions. Just Go comments.

## The Setup

The full code is at [`examples/agent-demo`](https://github.com/micro/go-micro/tree/master/examples/agent-demo). Here's the architecture:

```mermaid
flowchart TD
    User[<b>User</b> <br/>natural language] --> Agent[<b>AI Agent</b> <br/>Claude, GPT, etc.]
    Agent --> Gateway[<b>MCP Gateway</b> <i>:3000</i>]

    Gateway --> Project[<b>ProjectService</b><br/>Create / Get / List]
    Gateway --> Task[<b>TaskService</b><br/>Create / List / Update]
    Gateway --> Team[<b>TeamService</b><br/>Add / List / Get]

```

The MCP gateway discovers all three services automatically and exposes 9 tools. The agent sees them and knows how to call them — because we wrote good comments.

## Step 1: Define Your Types

Every field gets a `description` tag. This is what the agent reads:

```go
type Task struct {
    ID        string `json:"id" description:"Unique task identifier"`
    ProjectID string `json:"project_id" description:"ID of the project this task belongs to"`
    Title     string `json:"title" description:"Short task title"`
    Status    string `json:"status" description:"Task status: todo, in_progress, or done"`
    Assignee  string `json:"assignee,omitempty" description:"Username of the person assigned"`
    Priority  string `json:"priority" description:"Priority: low, medium, or high"`
}
```

Notice we list valid enum values (`todo, in_progress, done`) and mark optional fields with `omitempty`. This is how the agent knows what it can send.

## Step 2: Write Handler Comments

Each handler method gets a doc comment explaining what it does, plus an `@example` with realistic input:

```go
// Create creates a new task in a project.
// Returns the task with a generated ID, initial status of "todo",
// and default priority of "medium".
//
// @example {"project_id": "proj-1", "title": "Design homepage mockup", "assignee": "alice", "priority": "high"}
func (s *TaskService) Create(ctx context.Context, req *CreateTaskRequest, rsp *CreateTaskResponse) error {
    // ...
}
```

The MCP gateway extracts this at registration time via `go/ast` and turns it into a JSON Schema tool definition. The agent sees:

```json
{
  "name": "demo.TaskService.Create",
  "description": "Create creates a new task in a project. Returns the task with a generated ID, initial status of \"todo\", and default priority of \"medium\".",
  "inputSchema": {
    "type": "object",
    "properties": {
      "project_id": {"type": "string", "description": "Project ID to add the task to (required)"},
      "title": {"type": "string", "description": "Task title (required)"},
      "assignee": {"type": "string", "description": "Username to assign (optional)"},
      "priority": {"type": "string", "description": "Priority: low, medium, or high (default: medium)"}
    }
  }
}
```

That's everything an agent needs to call this tool correctly.

## Step 3: Wire It Up

One file, one `main()`. Three handlers registered with auth scopes, and MCP enabled with a single option:

```go
func main() {
    service := micro.NewService(
        micro.Name("demo"),
        micro.Address(":9090"),
        mcp.WithMCP(":3000"), // ← MCP gateway on port 3000
    )
    service.Init()

    srv := service.Server()
    srv.Handle(srv.NewHandler(
        &ProjectService{projects: make(map[string]*Project)},
        server.WithEndpointScopes("ProjectService.Create", "projects:write"),
        server.WithEndpointScopes("ProjectService.Get", "projects:read"),
        server.WithEndpointScopes("ProjectService.List", "projects:read"),
    ))
    srv.Handle(srv.NewHandler(
        &TaskService{tasks: make(map[string]*Task)},
        server.WithEndpointScopes("TaskService.Create", "tasks:write"),
        server.WithEndpointScopes("TaskService.List", "tasks:read"),
        server.WithEndpointScopes("TaskService.Update", "tasks:write"),
    ))
    srv.Handle(srv.NewHandler(
        &TeamService{members: make(map[string]*Member)},
        server.WithEndpointScopes("TeamService.Add", "team:write"),
        server.WithEndpointScopes("TeamService.List", "team:read"),
        server.WithEndpointScopes("TeamService.Get", "team:read"),
    ))

    service.Run()
}
```

`mcp.WithMCP(":3000")` starts the MCP gateway after the service boots. All registered handlers become tools automatically. That's it.

## What the Agent Can Do

The demo seeds realistic data — 2 projects, 7 tasks, 4 team members. Here's what happens when you talk to the agent:

### Multi-service queries

> **You:** "Give me a status update on the Website Redesign project"

The agent calls `ProjectService.Get` to fetch the project, then `TaskService.List` filtered by project ID, and synthesizes:

> **Agent:** The Website Redesign (proj-1) is active. 4 tasks total: 1 in progress (Design new homepage layout, assigned to bob), 3 todo. High-priority items: homepage layout and responsive nav component. The "Write copy for about page" task is unassigned.

That's three tool calls, zero glue code.

### Cross-service reasoning

> **You:** "Who on the team knows React and has available capacity?"

The agent calls `TeamService.List` with `skill=react`, gets bob and charlie, then calls `TaskService.List` for each to check their workload:

> **Agent:** Two team members know React: Bob Park (designer, 1 task in progress) and Charlie Kim (engineer, 1 task todo). Charlie has lighter load — recommend assigning React work to him.

### Write operations

> **You:** "Create a high-priority task for alice to set up monitoring on the API v2 project"

The agent calls `TaskService.Create`:

```json
{
  "project_id": "proj-2",
  "title": "Set up monitoring",
  "assignee": "alice",
  "priority": "high"
}
```

It knows `proj-2` is the API v2 project because it queried `ProjectService.List` earlier in the conversation.

## The Key Insight

None of this required:
- Writing tool definitions or OpenAPI specs
- Building an API wrapper or integration layer
- Configuring the agent with service details
- Any code beyond normal Go handlers with comments

The MCP gateway does the translation. Your comments become the agent's documentation. Your struct tags become the parameter schema. Your services become tools.

**Write a good Go service. Get AI integration for free.**

## Try It

```bash
# Clone and run
git clone --depth 1 https://github.com/micro/go-micro
cd go-micro/examples/agent-demo
go run main.go
```

Then connect with Claude Code:

```json
{
  "mcpServers": {
    "demo": {
      "command": "go",
      "args": ["run", "."],
      "cwd": "/path/to/go-micro/examples/agent-demo"
    }
  }
}
```

Or use the WebSocket endpoint at `ws://localhost:3000/mcp/ws` from any MCP-compatible client.

## What's Next

This demo is a starting point. In production you'd run each service as a separate process, use Consul or etcd for discovery, add JWT authentication, and deploy the standalone `micro-mcp-gateway` binary in front of everything.

The guides cover all of this:
- [Building AI-Native Services](/docs/guides/ai-native-services.html) — End-to-end tutorial
- [MCP Security](/docs/guides/mcp-security.html) — Auth, scopes, rate limiting
- [Agent Patterns](/docs/guides/agent-patterns.html) — Architecture patterns for production

