Go MCP Server 项目初始化:工具注册架构 + 编译运行通过

- Tool 接口 + Registry 注册中心,工具模块化插拔
- system 工具:health / info
- database 工具:db_query / db_tables / db_table_info / db_explain(四层 SQL 安全)
- docker 工具:docker_ps / docker_logs / docker_inspect(白名单 + 大小限制)
- middleware:auth(JWT+APIKey)/ ratelimit(令牌桶)/ audit(slog 结构化)
- stdio / SSE 双传输模式,Viper 多环境配置
- Dockerfile 多阶段构建(golang:alpine → scratch),~15MB 镜像
- docker-compose.yml 一键部署 + 安全加固(read_only / no-new-privileges / cap_drop)
- go build ./... / go vet ./... / go test ./... 全部通过

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
yangzhaohan
2026-07-06 10:26:52 +08:00
parent 68ca67a98b
commit fb2c3a6cae
26 changed files with 1854 additions and 1 deletions
+94
View File
@@ -0,0 +1,94 @@
package middleware
import (
"context"
"fmt"
"net/http"
"strings"
"github.com/golang-jwt/jwt/v5"
)
type contextKey string
const (
KeySubject contextKey = "subject"
KeyScopes contextKey = "scopes"
)
// Auth 验证 Bearer TokenAPI Key 或 JWT)。
type Auth struct {
apiKeys map[string]string // key → description
jwtSecret []byte
}
func NewAuth(apiKeys map[string]string, jwtSecret string) *Auth {
return &Auth{
apiKeys: apiKeys,
jwtSecret: []byte(jwtSecret),
}
}
// Middleware 从 HTTP Header 中提取并验证 Token。
func (a *Auth) Middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := extractBearer(r)
if token == "" {
http.Error(w, "missing Bearer token", http.StatusUnauthorized)
return
}
// 尝试 API Key 验证
if desc, ok := a.apiKeys[token]; ok {
ctx := context.WithValue(r.Context(), KeySubject, fmt.Sprintf("apikey:%s", desc))
ctx = context.WithValue(ctx, KeyScopes, []string{"*"})
next.ServeHTTP(w, r.WithContext(ctx))
return
}
// 尝试 JWT 验证
if a.jwtSecret != nil {
claims, err := a.parseJWT(token)
if err == nil {
ctx := context.WithValue(r.Context(), KeySubject, claims.Subject)
ctx = context.WithValue(ctx, KeyScopes, claims.Scopes)
next.ServeHTTP(w, r.WithContext(ctx))
return
}
}
http.Error(w, "invalid token", http.StatusUnauthorized)
})
}
type customClaims struct {
jwt.RegisteredClaims
Scopes []string `json:"scopes"`
}
func (a *Auth) parseJWT(tokenStr string) (*customClaims, error) {
token, err := jwt.ParseWithClaims(tokenStr, &customClaims{},
func(t *jwt.Token) (any, error) {
return a.jwtSecret, nil
},
)
if err != nil {
return nil, err
}
claims, ok := token.Claims.(*customClaims)
if !ok || !token.Valid {
return nil, fmt.Errorf("invalid claims")
}
return claims, nil
}
func extractBearer(r *http.Request) string {
auth := r.Header.Get("Authorization")
if auth == "" {
return ""
}
if !strings.HasPrefix(auth, "Bearer ") {
return ""
}
return strings.TrimPrefix(auth, "Bearer ")
}