e071084ebe
govulncheck / govulncheck (push) Has been cancelled
Lint / golangci-lint (push) Has been cancelled
Run Tests / Unit Tests (push) Has been cancelled
Run Tests / Etcd Integration Tests (push) Has been cancelled
Harness (E2E) / Harnesses (mock LLM) (push) Has been cancelled
Harness (E2E) / Provider harnesses (live LLM conformance) (push) Has been cancelled
403 lines
11 KiB
Markdown
403 lines
11 KiB
Markdown
# 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!
|
|
|
|
### 1. Go Doc Comments (Automatic - Recommended)
|
|
|
|
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": "alice@example.com",
|
|
// "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
|