中间件
中间件让您用日志记录、身份验证和速率限制等横切逻辑包装工具处理程序。中间件按注册顺序执行(先注册先运行),形成围绕底层工具处理程序的链。
概述
ToolHandlerMiddleware 是一个接受 ToolHandlerFunc 并返回新 ToolHandlerFunc 的函数。此模式与 Go 标准库中的 HTTP 中间件完全相同。
type ToolHandlerFunc func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error)
type ToolHandlerMiddleware func(ToolHandlerFunc) ToolHandlerFunc注册中间件
使用 Use()(运行时)
Use() 方法在运行的服务器上注册一个或多个工具处理程序中间件。中间件可以随时添加——Use() 是并发安全的。
s := server.NewMCPServer("my-server", "1.0.0")
// 注册单个中间件
s.Use(loggingMiddleware)
// 一次注册多个中间件
s.Use(authMiddleware, rateLimitMiddleware)通过 Use() 注册的中间件按添加顺序执行。在上面的示例中,工具调用通过 loggingMiddleware → authMiddleware → rateLimitMiddleware → 工具处理程序。
使用服务器选项(构建时)
您也可以在构建服务器时注册中间件:
s := server.NewMCPServer("my-server", "1.0.0",
server.WithToolHandlerMiddleware(loggingMiddleware),
server.WithResourceHandlerMiddleware(resourceLogger),
server.WithPromptHandlerMiddleware(promptLogger),
)WithToolHandlerMiddleware、WithResourceHandlerMiddleware 和 WithPromptHandlerMiddleware 在构建时分别为各自的处理程序类型注册中间件。
编写中间件
简单中间件
记录每个工具调用的基本中间件:
import (
"context"
"log"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
)
func loggingMiddleware(next server.ToolHandlerFunc) server.ToolHandlerFunc {
return func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Printf("tool call: %s", req.Params.Name)
return next(ctx, req)
}
}
s := server.NewMCPServer("my-server", "1.0.0")
s.Use(loggingMiddleware)计时中间件
测量每个工具调用需要多长时间:
func timingMiddleware(next server.ToolHandlerFunc) server.ToolHandlerFunc {
return func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
start := time.Now()
result, err := next(ctx, req)
log.Printf("tool=%s duration=%v", req.Params.Name, time.Since(start))
return result, err
}
}身份验证中间件
在对工具处理程序调用之前拒绝未认证的请求。您需要提供自己的 extractToken、validateToken 和 userKey 实现:
// userKey 是用于存储已认证用户的自定义上下文键。
type contextKey string
const userKey contextKey = "user"
func authMiddleware(next server.ToolHandlerFunc) server.ToolHandlerFunc {
return func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
token := extractToken(ctx) // 您的令牌提取逻辑
if token == "" {
return nil, fmt.Errorf("authentication required")
}
user, err := validateToken(token) // 您的令牌验证逻辑
if err != nil {
return nil, fmt.Errorf("invalid token: %w", err)
}
ctx = context.WithValue(ctx, userKey, user)
return next(ctx, req)
}
}速率限制中间件
限制每个会话的工具调用次数。此示例使用 golang.org/x/time/rate 和 server.GetSessionID() 来识别调用者:
type rateLimiter struct {
limiters sync.Map
rate rate.Limit
burst int
}
func newRateLimiter(rps float64, burst int) *rateLimiter {
return &rateLimiter{rate: rate.Limit(rps), burst: burst}
}
func (r *rateLimiter) middleware(next server.ToolHandlerFunc) server.ToolHandlerFunc {
return func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
sessionID := server.GetSessionID(ctx)
limiter, _ := r.limiters.LoadOrStore(sessionID, rate.NewLimiter(r.rate, r.burst))
if !limiter.(*rate.Limiter).Allow() {
return nil, fmt.Errorf("rate limit exceeded")
}
return next(ctx, req)
}
}
// 使用
rl := newRateLimiter(10, 20)
s.Use(rl.middleware)执行顺序
中间件按注册顺序执行——第一个注册的中间件是最外层包装器,先运行:
s.Use(A, B)
s.Use(C)
// 调用顺序:A → B → C → 工具处理程序 → C → B → A这与 Go 中 net/http 中间件使用的约定一致。
任务增强工具
通过 Use() 注册的中间件也适用于通过任务路径执行的常规工具(当工具具有 TaskSupportOptional 或 TaskSupportPreferred 且客户端请求任务执行时)。相同的中间件链自动包装常规工具处理程序。
注意: 本机任务工具(使用 TaskToolHandlerFunc 注册的那些)不会应用 Use() 中间件。如果需要为本机任务工具提供横切逻辑,请在任务处理程序中直接实现。
tool := mcp.NewTool("long-running",
mcp.WithDescription("支持任务模式的工具"),
mcp.WithTaskSupport(mcp.TaskSupportOptional),
)
s.AddTool(tool, handler)
// 此中间件适用于常规调用和通过任务路径执行此常规工具时
s.Use(loggingMiddleware)内置中间件
MCP-Go 包含内置恢复中间件,可捕获工具处理程序中的 panic 并返回错误响应而不是使服务器崩溃:
s := server.NewMCPServer("my-server", "1.0.0",
server.WithRecovery(),
)并发安全
Use() 可以从多个 goroutine 并发安全调用。它在内部使用读写互斥锁来保护中间件切片。这意味着您可以在运行时动态添加中间件而无需停止服务器。

