Files
wehub-resource-sync 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
chore: import upstream snapshot with attribution
2026-07-13 12:40:33 +08:00

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