Back to Go Micro

MCP Tool Documentation

gateway/mcp/DOCUMENTATION.md

5.19.111.2 KB
Original Source

MCP Tool Documentation

This document explains how to document your go-micro services so that AI agents can understand them better.

Overview

The MCP gateway automatically exposes your microservices as tools that AI agents (like Claude) can call. By adding proper documentation to your service handlers, you help agents understand:

  • What the tool does - The purpose and behavior
  • What parameters it needs - Types, formats, constraints
  • What it returns - Response structure and meaning
  • How to use it - Example inputs and outputs

Documentation Methods

go-micro automatically extracts documentation from your Go doc comments at registration time. You don't need to write any extra code!

Just write standard Go documentation comments on your handler methods:

go
// GetUser retrieves a user by ID from the database. Returns full profile including email, name, and preferences.
//
// @example {"id": "user-1"}
func (s *UserService) GetUser(ctx context.Context, req *GetUserRequest, rsp *GetUserResponse) error {
    // implementation
}

When you register the handler, go-micro automatically:

  • Extracts the doc comment as the tool description
  • Parses the @example tag for example inputs
  • Registers everything in the service registry
  • Makes it available to the MCP gateway

Supported Tags:

  • @example <json> - Example JSON input (highly recommended for AI agents)

That's it! No extra registration code needed:

go
// Documentation is extracted automatically from method comments
handler := service.Server().NewHandler(new(UserService))
service.Server().Handle(handler)

2. Manual Registration (Optional Override)

For more control or to override auto-extracted docs, use server.WithEndpointDocs():

go
handler := service.Server().NewHandler(
    new(UserService),
    server.WithEndpointDocs(map[string]server.EndpointDoc{
        "UserService.GetUser": {
            Description: "Custom description that overrides the comment",
            Example:     `{"id": "user-123"}`,
        },
    }),
)

Manual metadata takes precedence over auto-extracted comments.

3. Endpoint Scopes (Auth)

Use server.WithEndpointScopes() to declare the auth scopes required for each endpoint. The MCP gateway reads these from the registry and enforces them when an Auth provider is configured.

go
handler := service.Server().NewHandler(
    new(BlogService),
    server.WithEndpointScopes("Blog.Create", "blog:write"),
    server.WithEndpointScopes("Blog.Delete", "blog:write", "blog:admin"),
    server.WithEndpointScopes("Blog.Read", "blog:read"),
)

Scopes are stored as comma-separated values in endpoint metadata ("scopes" key) and are propagated through the service registry just like descriptions and examples.

Gateway-Level Scope Overrides

An operator can also define or override scopes at the MCP gateway without modifying individual services. This is useful for centralized policy management:

go
mcp.Serve(mcp.Options{
    Registry: reg,
    Auth:     authProvider,
    Scopes: map[string][]string{
        "blog.Blog.Create": {"blog:write"},
        "blog.Blog.Delete": {"blog:admin"},
    },
})

Gateway-level scopes take precedence over service-level scopes.

4. Struct Tags (For Field Descriptions)

Add descriptions to struct fields using the description tag:

go
type User struct {
    ID    string `json:"id" description:"User's unique identifier (UUID format)"`
    Name  string `json:"name" description:"User's full name"`
    Email string `json:"email" description:"User's email address"`
    Age   int    `json:"age,omitempty" description:"User's age (optional)"`
}

The description tag is used to generate parameter descriptions in the JSON Schema.

How It Works

Automatic Extraction Pipeline

1. Handler Registration (Your Service)
   ├─> You write Go doc comments on methods
   ├─> Call service.Server().NewHandler(yourHandler)
   └─> go-micro automatically parses source files using go/ast

2. Documentation Extraction (Automatic)
   ├─> Read Go doc comments from handler method source
   ├─> Parse @example tags for sample inputs
   ├─> Extract struct tag descriptions
   └─> Merge with any manual metadata (manual wins)

3. Service Registry
   ├─> Store endpoint metadata in registry.Endpoint.Metadata
   ├─> Metadata distributed with service information
   └─> Available to all components (gateway, discovery, etc.)

4. MCP Gateway Discovery
   ├─> Query registry for services and endpoints
   ├─> Read description and example from endpoint.Metadata
   └─> Generate JSON Schema with documentation

5. Tool Creation
   └─> Create MCP tool with rich description for AI agents

Example Output

For a documented handler, the MCP gateway generates:

json
{
  "name": "users.UserService.GetUser",
  "description": "GetUser retrieves a user by ID from the database. Returns full profile including email, name, and preferences.",
  "inputSchema": {
    "type": "object",
    "description": "This endpoint fetches a user's complete profile...",
    "properties": {
      "id": {
        "type": "string",
        "description": "User ID in UUID format (e.g., \"123e4567-e89b-12d3-a456-426614174000\")"
      }
    },
    "required": ["id"],
    "examples": [
      "{\"id\": \"user-1\"}"
    ]
  }
}

Best Practices

Write for AI, Not Just Humans

AI agents parse your documentation literally. Be explicit:

✅ Good:

go
// GetUser retrieves a user by their unique ID from the database.
// Returns the user's full profile including name, email, and preferences.
// If the user doesn't exist, returns an error with status 404.
//
// @param id {string} User ID in UUID v4 format (e.g., "123e4567-e89b-12d3-a456-426614174000")
// @return {User} User object with all profile fields populated

❌ Bad:

go
// Gets a user
func GetUser(...) // No details, no context

Specify Formats and Constraints

Tell agents exactly what format you expect:

✅ Good:

go
// @param email {string} Email address in RFC 5322 format (must contain @ and domain)
// @param age {number} User's age (integer between 0-150)
// @param phone {string} Phone number in E.164 format (e.g., "+14155552671")

❌ Bad:

go
// @param email {string} The email
// @param age {number} Age

Provide Real Examples

Show agents actual valid inputs:

✅ Good:

go
// @example
//   {
//     "name": "Alice Smith",
//     "email": "[email protected]",
//     "age": 30,
//     "phone": "+14155552671"
//   }

❌ Bad:

go
// @example
//   {
//     "name": "string",
//     "email": "string"
//   }

Document Error Cases

Tell agents what can go wrong:

go
// GetUser retrieves a user by ID.
//
// Returns error if:
// - User ID is not a valid UUID
// - User does not exist (404)
// - Database is unavailable (503)
//
// @param id {string} User ID in UUID format

Use Descriptive Names

Field names should be self-explanatory:

✅ Good:

go
type CreateUserRequest struct {
    FullName      string `json:"full_name" description:"User's complete name"`
    EmailAddress  string `json:"email_address" description:"Primary email for contact"`
    DateOfBirth   string `json:"date_of_birth" description:"Birth date in YYYY-MM-DD format"`
}

❌ Bad:

go
type CreateUserRequest struct {
    N string `json:"n"` // What is n?
    E string `json:"e"` // What is e?
    D string `json:"d"` // What is d?
}

Impact on Agent Performance

Without Documentation

Agent: "I need to call GetUser but I don't know what format the ID should be.
        Is it a number? A string? A UUID? Let me try..."

❌ Calls with: {"id": 123}
❌ Calls with: {"id": "user123"}
❌ Calls with: {"id": "abc"}
✅ Calls with: {"id": "550e8400-e29b-41d4-a716-446655440000"} (after 4 attempts)

With Documentation

Agent: "GetUser needs an ID in UUID format. The example shows the format.
        I'll use a valid UUID."

✅ Calls with: {"id": "550e8400-e29b-41d4-a716-446655440000"} (first attempt)

Result:

  • 75% fewer failed calls
  • Faster task completion
  • Better user experience

Parser Implementation

The MCP gateway uses several parsers:

1. Go Doc Parser (parseServiceDocs)

  • Extracts godoc comments from handler methods
  • Parses JSDoc-style tags
  • Returns ToolDescription struct

2. Struct Tag Parser (ParseStructTags)

  • Reads description tags from struct fields
  • Generates JSON Schema with field descriptions
  • Marks required vs optional fields (omitempty)

3. Comment Parser (ParseGoDocComment)

  • Regex-based extraction of @param, @return, @example tags
  • Splits summary from detailed description
  • Builds structured documentation

4. Type Mapper (reflectTypeToJSONType)

  • Converts Go types to JSON Schema types
  • Handles: string, int, float, bool, array, object
  • Used for automatic schema generation

Examples

See complete examples in:

  • examples/mcp/documented/ - Fully documented service
  • examples/auth/ - Auth service with documentation
  • examples/hello-world/ - Basic service

Testing Documentation

1. List Tools

bash
curl http://localhost:3000/mcp/tools | jq '.tools[0]'

Verify the description and schema are correct.

2. Use with Claude Code

Add to your Claude Code config and ask Claude to use your service. Claude will show you how it interprets your documentation.

3. Check Examples Work

Try the examples from your @example tags:

bash
curl -X POST http://localhost:3000/mcp/call \
  -H "Content-Type: application/json" \
  -d '{
    "tool": "users.UserService.GetUser",
    "input": <your-example-json>
  }'

Future Enhancements

Planned improvements:

  • Auto-extract examples from test files
  • Validate documentation completeness (lint)
  • Generate documentation from OpenAPI specs
  • Support custom validation rules in tags
  • Interactive documentation editor

FAQ

Q: Do I need to document every field? A: Document fields that are ambiguous or have constraints. Self-explanatory fields can rely on the field name.

Q: Will this slow down my service? A: No. Documentation is parsed once at startup when the MCP gateway discovers services.

Q: Can I use OpenAPI/Swagger specs instead? A: Not yet, but it's planned. For now, use Go comments and struct tags.

Q: What if I don't document my handlers? A: The MCP gateway will still work, generating basic descriptions from method names and types. But agents will perform better with documentation.

Q: How do I know if my documentation is good? A: Test it with Claude Code. If Claude understands your service and calls it correctly on the first try, your documentation is good!

Q: How do I add auth scopes to my endpoints? A: Use server.WithEndpointScopes() when registering your handler:

go
handler := service.Server().NewHandler(
    new(MyService),
    server.WithEndpointScopes("MyService.Create", "write"),
)

Or define scopes at the gateway level using Scopes in mcp.Options.

Q: Can I set scopes at the gateway without changing services? A: Yes. Use the Scopes option on mcp.Options to define or override scopes for any tool at the gateway layer. This is useful for centralized policy management.

License

Apache 2.0