Skip to content

网关中间件链

网关支持可插拔的 BeforeCall / AfterCall 中间件链,应用于每一次 tools/call 分发(issue #770)。

快速开始

rust
use dcc_mcp_gateway::gateway::middleware::{
    AuditMiddleware, MiddlewareChain, QuotaMiddleware, RedactionMiddleware,
};
use std::sync::Arc;

let config = GatewayConfig {
    middleware_chain: MiddlewareChain::new()
        .with_before(Arc::new(AuditMiddleware::default()))
        .with_before(Arc::new(QuotaMiddleware::new(100)))  // 每分钟 100 次
        .with_before(Arc::new(RedactionMiddleware::new(["api_key", "token"]))),
    ..GatewayConfig::default()
};

内置中间件

中间件用途失败行为
AuditMiddleware每次 tools/call 时输出一条 tracing::info! 结构化日志,包含方法、工具名、DCC 类型、会话 ID、耗时及结果永不阻断
QuotaMiddleware::new(N)全局每分钟最多 N 次调用;超出时返回 MiddlewareError::QuotaExceeded中止调用,返回 429 等效错误
RedactionMiddleware::new(fields)将匹配字段的参数值替换为 "[REDACTED]",防止日志或转发中泄露敏感信息永不失败

自定义中间件

实现 BeforeCallMiddlewareAfterCallMiddleware trait:

rust
use dcc_mcp_gateway::gateway::middleware::{
    AfterCallMiddleware, BeforeCallMiddleware, CallContext, CallResult, MiddlewareError,
};

pub struct MyMiddleware;

#[async_trait::async_trait]
impl BeforeCallMiddleware for MyMiddleware {
    async fn before_call(&self, ctx: &mut CallContext) -> Result<(), MiddlewareError> {
        // 在调用分发前检查或修改 ctx
        tracing::info!(tool = ?ctx.tool_slug, dcc = ?ctx.dcc_type, "自定义前置中间件");
        Ok(())
    }
}

#[async_trait::async_trait]
impl AfterCallMiddleware for MyMiddleware {
    async fn after_call(
        &self,
        ctx: &mut CallContext,
        result: &mut CallResult,
    ) -> Result<(), MiddlewareError> {
        tracing::info!(success = result.success, "自定义后置中间件");
        Ok(())
    }
}

注册方式:

rust
MiddlewareChain::new()
    .with_before(Arc::new(MyMiddleware))
    .with_after(Arc::new(MyMiddleware))

CallContext 字段

字段类型说明
methodStringMCP 方法名(tools/calltools/list 等)
tool_slugOption<String>工具名称
dcc_typeOption<String>DCC 类型(mayablender 等)
session_idOption<String>MCP 会话 ID
request_idString每次请求的唯一 ID(与审计日志 request_id 对应)
argsserde_json::Value工具参数(可变;RedactionMiddleware 会修改此字段)
metadataHashMap<String, String>中间件间传递数据的通用容器

执行顺序

请求 → BeforeCall[0] → BeforeCall[1] → ... → 分发 → AfterCall[0] → AfterCall[1] → 响应

若任意 before_call 返回 Err,调用将被中止,后续中间件不再执行。

与 Admin UI 集成

Admin UI 通过 AuditMiddleware 填充 /admin/api/calls,并把完成的调用提升为 /admin/api/traces。发布的 dcc-mcp-server 路径在 admin 启用时会自动接入 AdminAuditSink。如果你直接构造 dcc-mcp-gateway,请在启动 router 前把 audit middleware/sink 放进 GatewayConfig

rust
let audit = Arc::new(AuditMiddleware::default());

GatewayConfig {
    admin_enabled: true,
    middleware_chain: MiddlewareChain::new()
        .with_before(audit.clone())
        .with_after(audit),
    ..GatewayConfig::default()
}

运维需要跨重启保留记录时,设置 DCC_MCP_GATEWAY_AUDIT_DIR,即可获得有界的 audit.jsonltraces.jsonl 持久化。

参见

Released under the MIT License.