---
title: "Tools as Services: Why Go Micro Was Always Ready for AI"
linkTitle: "Tools as Services"
date: 2026-05-30
description: "The path from API gateway to MCP to LLM tools was shorter than you'd think — because services were always self-describing."
category:
- opinion
tags:
- AI
- MCP
- architecture
aliases:
  - /blog/12
---
When people see `micro chat` or the MCP gateway, they assume we built something new. We didn't. We exposed something that was already there.

Go Micro has always treated services as self-describing, addressable units. Every service registers its name, endpoints, and request types with the registry. Every endpoint is callable through a standardised path. The only thing that changed is *who's calling*.

## The Pattern

Go Micro was built on one idea: **a service should be accessible the same way regardless of how you access it**.

When we launched in 2015, that meant:

**HTTP API Gateway** — every service was reachable via `/{service}/{endpoint}`:

```
POST /users/Users.Create {"name": "Alice"}
```

No routing configuration. No URL mapping. You add a handler, it's accessible. The gateway reads the registry and routes.

**Web Dashboard** — every service appeared as a page. Endpoints were browseable, callable from the UI. Same registry, different presentation.

**CLI** — every service became a command:

```bash
micro call users Users.Create '{"name": "Alice"}'
```

Same registry, same endpoint, different interface. The service didn't change. The *access layer* changed.

## The Evolution to AI

MCP is just the next access layer.

**MCP Gateway** — every service is an AI-callable tool:

```json
{
  "name": "users_Users_Create",
  "description": "Create a new user account",
  "parameters": {
    "name": {"type": "string"},
    "email": {"type": "string"}
  }
}
```

Same registry. Same endpoints. Same service code. The gateway reads the registry, translates each endpoint into a tool definition, and exposes it over the Model Context Protocol. Claude, ChatGPT, or any MCP-compatible agent can discover and call your services — without you writing a single line of AI-specific code.

**`micro chat`** — every service is something you can talk to:

```
> create a user named Alice with email alice@example.com
  → users_Users_Create({"name":"Alice","email":"alice@example.com"})
Done. User Alice created.
```

Same registry. Same endpoints. The LLM reads the tool descriptions and decides what to call. The service doesn't know it's being called by an AI.

## Why This Works

The reason the AI integration was straightforward — not months of work, not a rewrite — is that Go Micro services were already:

1. **Named and discoverable.** The registry knows what's running and where.
2. **Self-describing.** Endpoints have typed request/response schemas. Doc comments on handlers become descriptions.
3. **Uniformly callable.** The client makes RPC calls by service name and endpoint name. It doesn't matter if the caller is an HTTP gateway, a CLI, or an LLM.

When we added MCP, we didn't add a new way to call services. We added a new way to *discover* them — one that LLMs understand. The calling mechanism was already there.

When we added `micro chat`, we didn't build an agent framework. We connected the existing tool discovery to an existing model interface and added a for-loop. The whole thing is [~150 lines](/blog/2026/05/30/build-your-own-ai-agent-cli-in-150-lines.html).

## The Access Layer Pattern

Here's the mental model:

```
Service (Go handler + registry metadata)
  ↓ accessed via
API Gateway       →  HTTP clients
Web Dashboard     →  browsers
CLI               →  developers
MCP Gateway       →  AI agents
micro chat        →  natural language
```

Each layer does the same thing: read the registry, present the services in a format the consumer understands, and route calls back to the service. The service is oblivious. It just handles requests.

This is why we never needed an "agent package" or an "AI framework." The framework already had the right shape. Services were always tools — they just didn't know it yet.

## What Doc Comments Buy You

The one thing that changed: **documentation became functional.**

In the API gateway era, doc comments were nice to have. In the MCP era, they're what the LLM reads to decide which tool to call:

```go
// CreateUser creates a new user account with the given name and email.
// Returns the created user with a generated ID.
// @example {"name": "Alice", "email": "alice@example.com"}
func (h *Users) CreateUser(ctx context.Context, req *pb.CreateRequest, rsp *pb.CreateResponse) error {
```

The comment becomes the tool's description. The `@example` becomes the LLM's hint for what arguments look like. Good comments mean the AI picks the right tool. Bad comments mean it guesses.

This is a genuine incentive to write documentation — not because a human might read it, but because a machine *will* read it and make decisions based on it.

## What's Next

The pattern extends beyond what we've built:

- **`micro registry list`** already shows you what's running. An agent could use the same data to reason about service topology.
- **`micro broker subscribe`** streams events. An agent could monitor events and react — which is what `micro flow` does.
- **`micro store`** persists data. An agent could read and write state as part of a multi-step workflow.

Every framework primitive that has a CLI command could also be a tool. The registry, broker, store, and config interfaces are all accessible from the terminal *and* from code. Making them accessible to AI agents is the same step we've already taken for services.

The thesis hasn't changed since 2015: **build the service once, access it everywhere**. The "everywhere" just expanded to include AI.

