https://github.com/BearHuddleston/go-mcp-server-example
Example how to make a DIY MCP server in Go
https://github.com/BearHuddleston/go-mcp-server-example
example go golang mcp-server
Last synced: about 2 months ago
JSON representation
Example how to make a DIY MCP server in Go
- Host: GitHub
- URL: https://github.com/BearHuddleston/go-mcp-server-example
- Owner: BearHuddleston
- Created: 2025-06-04T01:32:35.000Z (5 months ago)
- Default Branch: main
- Last Pushed: 2025-06-04T01:48:41.000Z (5 months ago)
- Last Synced: 2025-07-07T06:43:25.838Z (4 months ago)
- Topics: example, go, golang, mcp-server
- Language: Go
- Homepage:
- Size: 28.3 KB
- Stars: 9
- Watchers: 0
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
- awesome-mcp-servers - **go-mcp-server-example** - Example how to make a DIY MCP server in Go `go` `example` `golang` `mcp-server` `mcp` `go install BearHuddleston/go-mcp-server-example@latest` (DevOps)
- awesome-mcp-servers - **go-mcp-server-example** - Example how to make a DIY MCP server in Go `go` `example` `golang` `mcp-server` `mcp` `go install BearHuddleston/go-mcp-server-example@latest` (⚙️ DevOps)
README
# MCP Server Example (MCP Coffee Shop)
A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server implementation in Go that provides coffee shop information through tools, resources, and prompts, following Go project layout best practices.
## Features
- **MCP 2025-03-26 Specification Compliant**
- **Multiple Transport Support**: `stdio` (default, compatible with MCP Inspector), `http` (with SSE)
- **Coffee Shop Domain**: Tools, resources, and prompts for coffee shop operations
- **Graceful Shutdown & Configurable Timeouts**
- **Production Ready**: Structured logging, error handling, validation
## Project Structure
```
simple-mcp-server-refactored/
├── cmd/mcpserver/ # Application entrypoint
├── pkg/ # Public library code
│ ├── mcp/ # Core MCP protocol types
│ ├── config/ # Configuration management
│ ├── transport/ # Transport implementations
│ └── handlers/ # Domain-specific handlers
├── internal/server/ # Server implementation
└── go.mod
```
## Usage
```bash
# Build the application
go build -o mcpserver ./cmd/mcpserver
# Run with stdio transport (default)
./mcpserver
# Run with HTTP transport
./mcpserver -transport http -port 8080
```
- `stdio`: Standard input/output (default, compatible with MCP Inspector)
- `http`: HTTP with Server-Sent Events (SSE) support
- **Coffee Shop Domain**: Tools, resources, and prompts for coffee shop operations
- **Graceful Shutdown**: Proper signal handling and resource cleanup
- **Configurable Timeouts**: Request, shutdown, and HTTP timeouts
- **Production Ready**: Structured logging, error handling, and validation
## Quick Start
### Docker
You can run the MCP server using Docker:
1. **Build the Docker image**:
```bash
docker build -t mcp-server .
```
2. **Run the container**:
```bash
# For HTTP transport (exposes port 8080)
docker run -p 8080:8080 mcp-server --transport http --port 8080
# For stdio transport (useful with MCP Inspector)
docker run -it mcp-server --transport stdio
```
3. **Using environment variables**:
```bash
docker run -p 8080:8080 -e TRANSPORT=http -e PORT=8080 mcp-server
```
### Prerequisites
- Go 1.24+ installed
- For testing: [MCP Inspector](https://github.com/modelcontextprotocol/inspector)
### Installation
```bash
git clone
cd simple-mcp-server
go build
```
### Basic Usage
```bash
# Start with stdio transport (default)
go run ./...
# Start with HTTP transport
go run ./... --transport http --port 8080
# Custom configuration
go run ./... --transport http --port 9000 --request-timeout 45s
```
## Configuration
### Command Line Flags
| Flag | Description | Default | Example |
|------|-------------|---------|---------|
| `--transport` | Transport type (`stdio` or `http`) | `stdio` | `--transport http` |
| `--port` | HTTP port (ignored for stdio) | `8080` | `--port 9000` |
| `--request-timeout` | Request timeout duration | `30s` | `--request-timeout 45s` |
### Environment Variables
The server uses Go's built-in flag parsing. Configuration is primarily through command-line flags.
## Transports
### Stdio Transport
Perfect for command-line tools and MCP Inspector integration:
```bash
go run ./... --transport stdio
```
**Use Cases:**
- MCP Inspector debugging
- CLI integrations
- Development and testing
### HTTP Transport
RESTful HTTP API with optional Server-Sent Events:
```bash
go run ./... --transport http --port 8080
```
**Endpoints:**
- `POST /mcp` - Send JSON-RPC requests
- `GET /mcp` - Open SSE stream
- `GET /health` - Health check
**Examples:**
```bash
# Regular JSON response
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"initialize","id":1}'
# SSE stream response
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream" \
-d '{"jsonrpc":"2.0","method":"tools/list","id":"test"}'
```
## Implementing a New Handler
To add a new handler to the MCP server, follow these steps using the `getWeather` handler as an example:
1. **Create a new handler file** in `pkg/handlers/` (e.g., `weather.go`):
```go
package handlers
import (
"context"
"encoding/json"
"github.com/your-org/simple-mcp-server-refactored/pkg/mcp"
)
type WeatherHandler struct {
// Add any dependencies here (e.g., API clients, config)
}
// WeatherRequest represents the expected request parameters
type WeatherRequest struct {
Location string `json:"location"`
}
// WeatherResponse represents the response structure
type WeatherResponse struct {
Location string `json:"location"`
Temperature float64 `json:"temperature"`
Condition string `json:"condition"`
Humidity int `json:"humidity"`
WindSpeed float64 `json:"wind_speed"`
Unit string `json:"unit"`
}
// Handle processes the weather request
func (h *WeatherHandler) Handle(ctx context.Context, request json.RawMessage) (interface{}, error) {
var req WeatherRequest
if err := json.Unmarshal(request, &req); err != nil {
return nil, mcp.NewInvalidParamsError("invalid request parameters")
}
// TODO: Implement actual weather data retrieval
// This is a mock implementation
return WeatherResponse{
Location: req.Location,
Temperature: 72.5,
Condition: "Sunny",
Humidity: 45,
WindSpeed: 8.2,
Unit: "fahrenheit",
}, nil
}
// Register registers the handler with the MCP server
func (h *WeatherHandler) Register(router *mcp.Router) {
router.RegisterHandler("getWeather", h.Handle)
}
```
2. **Register the handler** in `internal/server/server.go`:
```go
// In NewServer function
weatherHandler := &handlers.WeatherHandler{}
weatherHandler.Register(router)
```
3. **Add tests** in `pkg/handlers/weather_test.go`:
```go
package handlers_test
import (
"context"
"encoding/json"
"testing"
"github.com/your-org/simple-mcp-server-refactored/pkg/handlers"
"github.com/stretchr/testify/assert"
)
func TestWeatherHandler(t *testing.T) {
h := &handlers.WeatherHandler{}
t.Run("successful request", func(t *testing.T) {
req := map[string]interface{}{
"location": "New York, NY",
}
reqBytes, _ := json.Marshal(req)
result, err := h.Handle(context.Background(), reqBytes)
assert.NoError(t, err)
assert.NotNil(t, result)
resp, ok := result.(handlers.WeatherResponse)
assert.True(t, ok)
assert.Equal(t, "New York, NY", resp.Location)
})
t.Run("invalid request", func(t *testing.T) {
req := map[string]interface{}{
"invalid": "data",
}
reqBytes, _ := json.Marshal(req)
_, err := h.Handle(context.Background(), reqBytes)
assert.Error(t, err)
})
}
```
4. **Update documentation** in the README.md to document the new handler.
## MCP Capabilities
### Tools
Interactive functions that can be called by the LLM:
| Tool | Description | Parameters |
|------|-------------|------------|
| `getDrinkNames` | Get list of available drinks | None |
| `getDrinkInfo` | Get detailed drink information | `name`: string (required) |
**Example:**
```json
{
"jsonrpc": "2.0",
"method": "tools/call",
"id": "1",
"params": {
"name": "getDrinkInfo",
"arguments": {"name": "Latte"}
}
}
```
### Resources
Contextual data managed by the application:
| Resource | URI | Description |
|----------|-----|-------------|
| `menu` | `menu://app` | Complete coffee shop menu |
**Example:**
```json
{
"jsonrpc": "2.0",
"method": "resources/read",
"id": "1",
"params": {"uri": "menu://app"}
}
```
### Prompts
Template-driven interactions for the LLM:
| Prompt | Description | Parameters |
|--------|-------------|------------|
| `drinkRecommendation` | Get personalized drink recommendations | `budget`: number (optional)
`preference`: string (optional) |
| `drinkDescription` | Get detailed drink descriptions | `drink_name`: string (required) |
**Example:**
```json
{
"jsonrpc": "2.0",
"method": "prompts/get",
"id": "1",
"params": {
"name": "drinkRecommendation",
"arguments": {"budget": 6, "preference": "sweet"}
}
}
```
## Testing with MCP Inspector
1. **Install MCP Inspector:**
```bash
npm install -g @modelcontextprotocol/inspector
```
2. **Start the inspector:**
```bash
npx @modelcontextprotocol/inspector
```
3. **Connect to the server:**
- **Transport**: `stdio`
- **Command**: `go`
- **Args**: `run ./...`
## Manual Testing
```bash
# Test stdio transport
echo '{"jsonrpc":"2.0","method":"initialize","id":1}' | go run ./... --transport stdio
# Test HTTP transport
go run ./... --transport http &
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"initialize","id":1}'
```
## API Reference
### JSON-RPC Methods
| Method | Description | Parameters |
|--------|-------------|------------|
| `initialize` | Initialize MCP session | Client info (optional) |
| `tools/list` | List available tools | None |
| `tools/call` | Execute a tool | `name`, `arguments` |
| `resources/list` | List available resources | None |
| `resources/read` | Read resource content | `uri` |
| `prompts/list` | List available prompts | None |
| `prompts/get` | Get prompt template | `name`, `arguments` (optional) |
| `ping` | Health check | None |
### Error Codes
| Code | Meaning | Description |
|------|---------|-------------|
| `-32700` | Parse Error | Invalid JSON was received |
| `-32600` | Invalid Request | Invalid JSON-RPC request |
| `-32601` | Method Not Found | Method does not exist |
| `-32602` | Invalid Params | Invalid method parameters |
| `-32603` | Internal Error | Internal JSON-RPC error |
### Systemd Service
```ini
[Unit]
Description=MCP Coffee Server
After=network.target
[Service]
Type=simple
User=mcp
ExecStart=/usr/local/bin/mcp-server --transport http --port 8080
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
```
## Troubleshooting
### Common Issues
**Connection Refused (HTTP)**
```bash
# Check if server is running
curl http://localhost:8080/health
# Verify port is not in use
lsof -i :8080
```
**Stdio Transport Not Responding**
```bash
# Check JSON format
echo '{"jsonrpc":"2.0","method":"ping","id":1}' | go run ./...
```
**Request Timeout**
```bash
# Increase timeout
go run ./... --request-timeout 60s
```
**Parse Errors**
- Ensure JSON is valid and properly formatted
- Check that all required fields are present
- Verify JSON-RPC 2.0 compliance
## Resources
- [Model Context Protocol Specification](https://modelcontextprotocol.io/specification/2025-03-26)
- [MCP Inspector](https://github.com/modelcontextprotocol/inspector)
- [Go Documentation](https://golang.org/doc/)
- [Jack Herrington's YouTube video on DIY MCP Server](https://www.youtube.com/watch?v=nTMSyldeVSw)
## Support
For issues and questions:
- Create an issue in the repository
- Check the [MCP documentation](https://modelcontextprotocol.io/)
- Review the [troubleshooting section](#troubleshooting)
## License
MIT