Build Model Context Protocol servers that provide Go package documentation from pkg.go.dev with search and detailed info tools
Build Model Context Protocol (MCP) servers that provide access to Go documentation from pkg.go.dev. This skill teaches you to implement MCP tools, handle HTTP/stdio transports, scrape documentation sites, and implement caching layers.
You'll learn to create an MCP server with two primary tools:
The server uses the `github.com/mark3labs/mcp-go` framework and supports both stdio and HTTP transports.
```
cmd/godoc-mcp-server/ # Main MCP server application
├── main.go # Entry point, sets up MCP server with tools
├── search.go # searchPackages tool implementation
└── packageInfo.go # getPackageInfo tool implementation
pkg/godoc/ # Core documentation extraction logic
├── client.go # HTTP client for pkg.go.dev
├── search.go # Package search functionality
├── package.go # Package documentation parsing
└── cache.go # Local caching with freecache
```
Create the directory structure shown above. Initialize Go modules:
```bash
go mod init github.com/yourusername/godoc-mcp-server
go get github.com/mark3labs/mcp-go
go get github.com/PuerkitoBio/goquery
go get github.com/coocood/freecache
go get github.com/pkg/errors
go get go.uber.org/multierr
```
In `cmd/godoc-mcp-server/main.go`, define tools using the modern mcp-go v0.32.0+ API:
```go
mcp.NewTool("searchPackages",
mcp.WithDescription("Search for Go packages on pkg.go.dev"),
mcp.WithString("query", mcp.Required(), mcp.Description("Search query")),
mcp.WithBoolean("needURL", mcp.Description("Include URLs in results")),
),
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
query, err := request.RequireString("query")
if err != nil {
return mcp.NewToolResultError(err.Error()), nil
}
needURL := request.GetBool("needURL", false)
// ... implementation
return mcp.NewToolResultText(jsonResult), nil
}
```
**Key Parameter Handling Patterns:**
In `pkg/godoc/client.go`:
```go
type Client struct {
baseURL string
client *http.Client
cache *freecache.Cache
}
func (c *Client) fetchPage(url string) (*goquery.Document, error) {
resp, err := c.client.Get(url)
if err != nil {
return nil, errors.Wrap(err, "failed to fetch page")
}
defer resp.Body.Close()
return goquery.NewDocumentFromReader(resp.Body)
}
```
In `pkg/godoc/package.go`, parse pkg.go.dev HTML structure:
**Key Selectors:**
**Example Parsing Pattern:**
```go
doc.Find(".Documentation-function").Each(func(i int, s *goquery.Selection) {
name := s.Find(".Documentation-functionHeader").Text()
signature := s.Find(".Documentation-declaration").Text()
description := s.Find(".Documentation-functionBody").Text()
if needURL {
href, _ := s.Find("a").Attr("href")
url = baseURL + href
}
functions = append(functions, Function{
Name: name,
Signature: signature,
Description: description,
URL: url,
})
})
```
In `pkg/godoc/cache.go`:
```go
type Cache struct {
cache *freecache.Cache
}
func (c *Cache) Get(key string) ([]byte, error) {
return c.cache.Get([]byte(key))
}
func (c *Cache) Set(key string, value []byte, expireSeconds int) error {
return c.cache.Set([]byte(key), value, expireSeconds)
}
```
**Cache Key Pattern:** `functionName + parameters`
In `cmd/godoc-mcp-server/main.go`:
```go
func main() {
httpMode := flag.Bool("http", false, "Run in HTTP mode")
host := flag.String("host", "localhost", "HTTP host")
port := flag.String("port", "8080", "HTTP port")
flag.Parse()
server := mcp.NewServer(
mcp.WithName("godoc-mcp-server"),
mcp.WithVersion("1.0.0"),
)
// Register tools...
if *httpMode {
server.ServeHTTP(*host + ":" + *port)
} else {
server.ServeStdio()
}
}
```
**HTTP Endpoints:**
When dealing with subpackages, concatenate parent and subpackage names with `/`:
```go
fullPackageName := parentPackage + "/" + subpackagePath
```
```bash
go build -o godoc-mcp-server ./cmd/godoc-mcp-server
GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -ldflags="-s -w" -o godoc-mcp-server-linux-arm64 ./cmd/godoc-mcp-server
GOOS=darwin GOARCH=arm64 CGO_ENABLED=0 go build -ldflags="-s -w" -o godoc-mcp-server-darwin-arm64 ./cmd/godoc-mcp-server
GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="-s -w" -o godoc-mcp-server-windows-amd64.exe ./cmd/godoc-mcp-server
./godoc-mcp-server
./godoc-mcp-server -http -host 0.0.0.0 -port 9000
```
Use structured error wrapping:
```go
import (
"github.com/pkg/errors"
"go.uber.org/multierr"
)
// Wrap errors with context
return nil, errors.Wrap(err, "failed to parse package info")
// Collect multiple errors
var errs error
errs = multierr.Append(errs, parseConstants())
errs = multierr.Append(errs, parseFunctions())
return errs
```
1. **Tool descriptions are critical** - They guide LLM usage patterns in MCP clients
2. **URL extraction is optional** - Controlled by `needURL` parameter to reduce response size
3. **All data structures must be JSON-serializable** - MCP clients expect JSON responses
4. **Cache keys should be deterministic** - Same inputs always produce same cache key
5. **HTML parsing is fragile** - pkg.go.dev structure may change; add version detection if needed
6. **Type safety matters** - Use built-in parameter methods (`RequireString`, `GetBool`) for validation
Once deployed, MCP clients can use your server like this:
```json
{
"tool": "searchPackages",
"arguments": {
"query": "http client",
"needURL": true
}
}
```
```json
{
"tool": "getPackageInfo",
"arguments": {
"packageName": "net/http",
"needURL": false
}
}
```
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/go-documentation-server-mcp/raw