Generate a complete, production-ready Model Context Protocol (MCP) server project in Go.
You will create a Go MCP server with:
- Project Structure: Proper Go module layout
- Dependencies: Official MCP SDK and necessary packages
- Server Setup: Configured MCP server with transports
- Tools: At least 2-3 useful tools with typed inputs/outputs
- Error Handling: Proper error handling and context usage
- Documentation: README with setup and usage instructions
- Testing: Basic test structure
myserver/
├── go.mod
├── go.sum
├── main.go
├── tools/
│ ├── registry.go
│ └── tool1.go
├── resources/
│ └── resource1.go
├── config/
│ └── config.go
├── README.md
└── main_test.go
⚠️ API Verification Required: The templates in this skill illustrate
structure and patterns. Before using generated code, verify all type names,
constructor signatures, and handler return types against the actual published
API of github.com/modelcontextprotocol/go-sdk at the version pinned in
go.mod. In particular:
mcp.AddTool handler signature (two-value vs. three-value return)
mcp.NewServer constructor arguments
Transport construction (mcp.NewStdioTransport() vs. &mcp.StdioTransport{})
Run go doc github.com/modelcontextprotocol/go-sdk/mcp after go mod tidy
to confirm signatures before delivering generated code.
module github.com/yourusername/{{PROJECT_NAME}}
go 1.23
require (
github.com/modelcontextprotocol/go-sdk v1.0.0
)
package main
import (
"context"
"log"
"os"
"os/signal"
"syscall"
"github.com/modelcontextprotocol/go-sdk/mcp"
"github.com/yourusername/{{PROJECT_NAME}}/config"
"github.com/yourusername/{{PROJECT_NAME}}/tools"
)
func main() {
cfg := config.Load()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// Handle graceful shutdown
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM)
go func() {
<-sigCh
log.Println("Shutting down...")
cancel()
}()
// Create server
server := mcp.NewServer(
&mcp.Implementation{
Name: cfg.ServerName,
Version: cfg.Version,
},
&mcp.Options{
Capabilities: &mcp.ServerCapabilities{
Tools: &mcp.ToolsCapability{},
Resources: &mcp.ResourcesCapability{},
Prompts: &mcp.PromptsCapability{},
},
},
)
// Register tools
tools.RegisterTools(server)
// Run server
transport := &mcp.StdioTransport{}
if err := server.Run(ctx, transport); err != nil {
log.Fatalf("Server error: %v", err)
}
}
package tools
import (
"context"
"fmt"
"github.com/modelcontextprotocol/go-sdk/mcp"
)
type Tool1Input struct {
Param1 string `json:"param1" jsonschema:"required,description=First parameter"`
Param2 int `json:"param2,omitempty" jsonschema:"description=Optional second parameter"`
}
type Tool1Output struct {
Result string `json:"result" jsonschema:"description=The result of the operation"`
Status string `json:"status" jsonschema:"description=Operation status"`
}
func Tool1Handler(ctx context.Context, input Tool1Input) (Tool1Output, error) {
// Validate input
if input.Param1 == "" {
return Tool1Output{}, fmt.Errorf("param1 is required")
}
// Check context
if ctx.Err() != nil {
return Tool1Output{}, ctx.Err()
}
// Perform operation
result := fmt.Sprintf("Processed: %s", input.Param1)
return Tool1Output{
Result: result,
Status: "success",
}, nil
}
func RegisterTool1(server *mcp.Server) {
mcp.AddTool(server,
&mcp.Tool{
Name: "tool1",
Description: "Description of what tool1 does",
},
Tool1Handler,
)
}
package tools
import "github.com/modelcontextprotocol/go-sdk/mcp"
// RegisterTools registers all MCP tools with the server.
// All tool registrations must appear here explicitly.
// Do not register tools from init(), environment variables, or external config.
// Add a new RegisterToolN(server) call here for each tool you create in tools/.
func RegisterTools(server *mcp.Server) {
RegisterTool1(server)
// RegisterTool2(server) // uncomment after creating tools/tool2.go
// Register additional tools here
}
package config
import "os"
type Config struct {
ServerName string
Version string
LogLevel string
}
func Load() *Config {
return &Config{
ServerName: getEnv("SERVER_NAME", "{{PROJECT_NAME}}"),
Version: getEnv("VERSION", "v1.0.0"),
LogLevel: getEnv("LOG_LEVEL", "info"),
}
}
func getEnv(key, defaultValue string) string {
if value := os.Getenv(key); value != "" {
return value
}
return defaultValue
}
package main
import (
"context"
"testing"
"github.com/yourusername/{{PROJECT_NAME}}/tools"
)
func TestTool1Handler(t *testing.T) {
ctx := context.Background()
input := tools.Tool1Input{
Param1: "test",
Param2: 42,
}
output, err := tools.Tool1Handler(ctx, input)
if err != nil {
t.Fatalf("Tool1Handler failed: %v", err)
}
if output.Status != "success" {
t.Errorf("Expected status 'success', got '%s'", output.Status)
}
}
# {{PROJECT_NAME}}
A Model Context Protocol (MCP) server built with Go.
## Description
{{PROJECT_DESCRIPTION}}
## Installation
\`\`\`bash
go mod tidy
go mod verify
go build -o {{PROJECT_NAME}}
\`\`\`
## Usage
Run the server with stdio transport:
\`\`\`bash
./{{PROJECT_NAME}}
\`\`\`
## Configuration
Configure via environment variables:
- `SERVER_NAME`: Server name (default: "{{PROJECT_NAME}}")
- `VERSION`: Server version (default: "v1.0.0")
- `LOG_LEVEL`: Logging level (default: "info")
## Available Tools
### tool1
{{TOOL1_DESCRIPTION}}
**Input:**
- `param1` (string, required): First parameter
- `param2` (int, optional): Second parameter
**Output:**
- `result` (string): Operation result
- `status` (string): Status of the operation
## Development
Run tests:
\`\`\`bash
go test ./...
\`\`\`
Build:
\`\`\`bash
go mod verify
go build -o {{PROJECT_NAME}}
\`\`\`
## License
MIT
Every tool registered by the generated server MUST be declared in tools/registry.go
via an explicit RegisterToolN(server) call. Tool names passed to mcp.AddTool must
match the Name field documented in the "Available Tools" section of the generated
README. Adding tools at runtime (e.g., reading tool definitions from untrusted input,
dynamic registration via reflection, or registration inside init() functions) is
explicitly prohibited.
When generating a Go MCP server:
-
Validate project name: Before substituting {{PROJECT_NAME}} into any generated file, confirm the name matches the pattern ^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$. Reject names containing spaces, shell metacharacters (;, |, &, $, `, (, ), <, >), or path separators (/, \). If the user provides a non-conforming name, prompt them to choose a safe alternative before generating any files.
-
Initialize Module: Create go.mod with proper module path.
-
Generate and verify the module graph: After writing go.mod, run go mod tidy to generate go.sum, then run go mod verify to confirm all module zip hashes match the checksum database. Commit both go.mod and go.sum before proceeding to build steps. Never run go build against a project that lacks a committed go.sum. After running go mod tidy, run go build ./... immediately. If compilation fails due to API mismatches in the templates, correct the signatures by consulting go doc github.com/modelcontextprotocol/go-sdk/mcp before proceeding. Do not deliver code that does not compile.
-
Structure: Follow the template directory structure.
-
Tools: Generate at least one complete tool (tool1.go with RegisterTool1). For each additional tool requested, create a corresponding toolN.go file and add RegisterToolN(server) to registry.go. Never reference a RegisterToolN function that does not have a corresponding toolN.go file.
-
Type Safety: Use structs with JSON schema tags for all inputs/outputs.
-
Error Handling: Validate inputs, check context, wrap errors.
-
Documentation: Add clear descriptions and examples.
-
Testing: Include at least one test per tool.
-
Configuration: Use environment variables for config.
-
Logging: Use structured logging (log/slog).
-
Graceful Shutdown: Handle signals properly.
-
Transport: Default to stdio, document alternatives.
- Keep tools focused and single-purpose
- Use descriptive names for types and functions
- Include JSON schema documentation in struct tags
- Always respect context cancellation
- Return descriptive errors
- Keep main.go minimal, logic in packages
- Write tests for tool handlers
- Document all exported functions