Generate a complete, starter 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: The tools the user asks for (by default 2-3 simple example tools free of side effects) 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/
│ └── tool1.go
├── config/
│ └── config.go
├── README.md
└── main_test.go
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,
},
nil,
)
// 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, req *mcp.CallToolRequest, input Tool1Input) (
*mcp.CallToolResult,
Tool1Output,
error,
) {
// Validate input
if input.Param1 == "" {
return nil, Tool1Output{}, fmt.Errorf("param1 is required")
}
// Check context
if ctx.Err() != nil {
return nil, Tool1Output{}, ctx.Err()
}
// Perform operation
result := fmt.Sprintf("Processed: %s", input.Param1)
return nil, 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"
func RegisterTools(server *mcp.Server) {
RegisterTool1(server)
// 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,
}
result, output, err := tools.Tool1Handler(ctx, nil, input)
if err != nil {
t.Fatalf("Tool1Handler failed: %v", err)
}
if output.Status != "success" {
t.Errorf("Expected status 'success', got '%s'", output.Status)
}
if result != nil {
t.Error("Expected result to be nil")
}
}
# {{PROJECT_NAME}}
A Model Context Protocol (MCP) server built with Go.
## Description
{{PROJECT_DESCRIPTION}}
## Installation
\`\`\`bash
go mod download
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 build -o {{PROJECT_NAME}}
\`\`\`
## License
MIT
When generating a Go MCP server:
- Initialize Module: Create
go.mod with proper module path; ask the user for their real module path and use it in place of the yourusername placeholder in every file
- Structure: Follow the template directory structure
- Type Safety: Use structs with JSON schema tags for all inputs/outputs
- Error Handling: Validate inputs, check context, wrap errors, and keep file paths, environment values and stack traces out of messages returned to callers
- 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), and send all logs to standard error, because standard output carries the stdio transport
- Graceful Shutdown: Handle signals properly
- Transport: Default to stdio, document alternatives; any documented network transport listens only on the local machine by default, requires authentication, and uses TLS when reachable from other machines, with this warning in the README
- Project Name: Accept a project name made only of lowercase letters, digits and hyphens that starts with a letter and is short; otherwise ask the user for a different name. Put user supplied descriptions into Go code only as properly escaped string literals, and into the README only as plain text outside commands and code blocks
- Output Location: Write generated files only inside one new folder in the current working directory, named after the checked project name, and ask the user before replacing any existing file
- Dependencies: Create go.sum by running go mod tidy with the default module proxy and checksum database, keep checksum checks turned on, and get user approval before adding or replacing any module beyond the official SDK
- Tool Naming: Keep tool names short, using lowercase letters, digits and underscores, and write tool descriptions as short factual statements of what the tool does, in plain text only
- Schema Tags: In the official SDK the schema tag holds only plain description text, and a field is optional when its json tag marks it to be omitted when empty
- Configuration Checks: Check configuration values from environment variables before use, for example that the log level is a known level, and consider a project specific prefix for variable names
- 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 short, safe error messages to callers and log full details to standard error
- Treat tool arguments as untrusted input from the model or another party
- Keep tool arguments out of shell commands and out of file paths outside a fixed base folder, and check their length and format
- Add shell execution, file access or network calls only when the user asks for them, and then use allowlists, a fixed base folder, input checks and timeouts
- Keep secrets out of configuration defaults
- Keep main.go minimal, logic in packages
- Write tests for tool handlers
- Document all exported functions