这是一个用Go语言实现的MCP(模型上下文协议)服务器,它通过工具、资源和提示提供咖啡店信息,并遵循Go项目的最佳实践布局。
stdio(默认,兼容MCP Inspector),http(带SSE)simple-mcp-server-refactored/
├── cmd/mcpserver/ # 应用程序入口点
├── pkg/ # 公共库代码
│ ├── mcp/ # 核心MCP协议类型
│ ├── config/ # 配置管理
│ ├── transport/ # 传输实现
│ └── handlers/ # 领域特定处理器
├── internal/server/ # 服务器实现
└── go.mod
# 构建应用程序
go build -o mcpserver ./cmd/mcpserver
# 使用stdio传输运行(默认)
./mcpserver
# 使用HTTP传输运行
./mcpserver -transport http -port 8080
stdio:标准输入/输出(默认,兼容MCP Inspector)http:带有服务端发送事件(SSE)支持的HTTP您可以使用Docker运行MCP服务器:
构建Docker镜像:
docker build -t mcp-server .
运行容器:
# 对于HTTP传输(暴露8080端口)
docker run -p 8080:8080 mcp-server --transport http --port 8080
# 对于stdio传输(与MCP Inspector配合使用)
docker run -it mcp-server --transport stdio
使用环境变量:
docker run -p 8080:8080 -e TRANSPORT=http -e PORT=8080 mcp-server
git clone <repository-url>
cd simple-mcp-server
go build
# 使用stdio传输启动(默认)
go run ./...
# 使用HTTP传输启动
go run ./... --transport http --port 8080
# 自定义配置
go run ./... --transport http --port 9000 --request-timeout 45s
| 标志 | 描述 | 默认值 | 示例 |
|---|---|---|---|
--transport | 传输类型(stdio 或 http) | stdio | --transport http |
--port | HTTP端口(对于stdio无效) | 8080 | --port 9000 |
--request-timeout | 请求超时时间 | 30s | --request-timeout 45s |
服务器使用Go内置的标志解析。配置主要通过命令行标志进行。
适用于命令行工具和MCP Inspector集成:
go run ./... --transport stdio
应用场景:
带有可选服务端发送事件(SSE)的RESTful HTTP API:
go run ./... --transport http --port 8080
端点:
POST /mcp - 发送JSON-RPC请求GET /mcp - 打开SSE流GET /health - 健康检查示例:
# 普通JSON响应
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"initialize","id":1}'
# SSE流响应
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"}'
要向MCP服务器添加一个新的处理器,请按照以下步骤操作,以getWeather处理器为例:
pkg/handlers/中创建一个新的处理器文件(例如,weather.go):package handlers
import (
"context"
"encoding/json"
"github.com/your-org/simple-mcp-server-refactored/pkg/mcp"
)
type WeatherHandler struct {
// 在这里添加任何依赖项(如API客户端、配置)
}
// WeatherRequest 表示预期的请求参数
type WeatherRequest struct {
Location string `json:"location"`
}
// WeatherResponse 表示响应结构
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 处理天气请求
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("无效的请求参数")
}
// TODO: 实现实际的天气数据检索
// 这是一个模拟实现
return WeatherResponse{
Location: req.Location,
Temperature: 72.5,
Condition: "晴朗",
Humidity: 45,
WindSpeed: 8.2,
Unit: "华氏度",
}, nil
}
// Register 将处理器注册到MCP服务器
func (h *WeatherHandler) Register(router *mcp.Router) {
router.RegisterHandler("getWeather", h.Handle)
}
internal/server/server.go中注册处理器:// 在NewServer函数中
weatherHandler := &handlers.WeatherHandler{}
weatherHandler.Register(router)
pkg/handlers/weather_test.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("成功的请求", func(t *testing.T) {
req := map[string]interface{}{
"location": "纽约, 纽约州",
}
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, "纽约, 纽约州", resp.Location)
})
t.Run("无效的请求", func(t *testing.T) {
req := map[string]interface{}{
"invalid": "数据",
}
reqBytes, _ := json.Marshal(req)
_, err := h.Handle(context.Background(), reqBytes)
assert.Error(t, err)
})
}
可以由LLM调用的交互式功能:
| 工具 | 描述 | 参数 |
|---|---|---|
getDrinkNames | 获取可用饮品列表 | 无 |
getDrinkInfo | 获取详细饮品信息 | name: 字符串(必需) |
示例:
{
"jsonrpc": "2.0",
"method": "tools/call",
"id": "1",
"params": {
"name": "getDrinkInfo",
"arguments": {"name": "拿铁"}
}
}
由应用程序管理的上下文数据:
| 资源 | URI | 描述 |
|---|---|---|
menu | menu://app | 完整的咖啡店菜单 |
示例:
{
"jsonrpc": "2.0",
"method": "resources/read",
"id": "1",
"params": {"uri": "menu://app"}
}
驱动LLM交互的模板:
| 提示 | 描述 | 参数 |
|---|---|---|
drinkRecommendation | 获取个性化的饮品推荐 | budget: 数字(可选)<br>preference: 字符串(可选) |
drinkDescription | 获取详细的饮品描述 | drink_name: 字符串(必需) |
示例:
{
"jsonrpc": "2.0",
"method": "prompts/get",
"id": "1",
"params": {
"name": "drinkRecommendation",
"arguments": {"budget": 6, "preference": "甜"}
}
}
安装MCP Inspector:
npm install -g @modelcontextprotocol/inspector
启动Inspector:
npx @modelcontextprotocol/inspector
连接到服务器:
stdiogorun ./...# 测试stdio传输
echo '{"jsonrpc":"2.0","method":"initialize","id":1}' | go run ./... --transport stdio
# 测试HTTP传输
go run ./... --transport http &
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"initialize","id":1}'
| 方法 | 描述 | 参数 |
|---|---|---|
initialize | 初始化MCP会话 | 客户端信息(可选) |
tools/list | 列出可用工具 | 无 |
tools/call | 执行一个工具 | name, arguments |
resources/list | 列出可用资源 | 无 |
resources/read | 读取资源内容 | uri |
prompts/list | 列出可用提示 | 无 |
prompts/get | 获取提示模板 | name, arguments(可选) |
ping | 健康检查 | 无 |
| 代码 | 含义 | 描述 |
|---|---|---|
-32700 | 解析错误 | 接收到无效的JSON |
-32600 | 无效请求 | 无效的JSON-RPC请求 |
-32601 | 方法未找到 | 方法不存在 |
-32602 | 无效参数 | 无效的方法参数 |
-32603 | 内部错误 | 内部JSON-RPC错误 |
[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
连接被拒绝(HTTP)
# 检查服务器是否正在运行
curl http://localhost:8080/health
# 验证端口未被占用
lsof -i :8080
Stdio传输不响应
# 检查JSON格式
echo '{"jsonrpc":"2.0","method":"ping","id":1}' | go run ./...
请求超时
# 增加超时时间
go run ./... --request-timeout 60s
解析错误
对于问题和疑问:
MIT