在 AI 驱动开发的浪潮中,MCP (Model Context Protocol) 正在迅速成为连接大语言模型 (LLM) 与私有数据、企业内部 API 的事实标准。然而,当开发者从本地的“玩具级” Demo 走向企业级生产环境时,往往会遭遇一系列严峻的挑战:如何保证私有数据的安全?如何进行身份鉴权?如何处理大文件或高并发请求?

本文将跳出基础概念,带你深入探讨 MCP 协议的高阶架构,并手把手构建一个具备 JWT 身份认证和流式传输能力的企业级 MCP Server。

1. 传统架构的局限与痛点

在基础的 MCP 实现中(例如直接通过 stdio 运行一个本地脚本),架构往往非常简单直接,但也因此存在明显的局限性:

  1. 安全性与鉴权缺失:本地脚本通常拥有当前用户的所有权限。如果将其暴露为远程服务(如基于 SSE 或 WebSocket),任何接入的 LLM 都可以毫无阻碍地访问敏感数据。
  2. 状态隔离困难:在多租户(Multi-tenant)场景下,不同用户(或不同的 AI 助手实例)需要访问不同的数据集,基础 MCP Server 难以实现细粒度的权限控制。
  3. 大数据传输瓶颈:当工具(Tools)或资源(Resources)需要返回 MB 级别甚至更大的数据时,一次性序列化 JSON 会导致严重的内存飙升和超时。

为了解决这些问题,我们需要引入更稳健的 Transport 层设计、标准的 JWT 鉴权机制以及基于流(Stream)的数据处理方案。

2. MCP 高阶架构解析

企业级 MCP Server 通常不再依赖简单的 stdio,而是采用基于 HTTP SSE (Server-Sent Events) 或 WebSocket 的远程部署模式。

graph TD Client["MCP Client / LLM App"] -->|1. HTTP POST /auth (JWT)| Auth["API Gateway / Auth Server"] Auth -->|2. Token Validated| Proxy["Load Balancer"] Proxy -->|3. SSE Connection| MCPServer["Enterprise MCP Server"] Proxy -->|4. HTTP POST /message| MCPServer MCPServer -->|5. Access Check| DB[("Enterprise Data / APIs")] MCPServer -->|6. Stream Response| Proxy

核心设计决策:

  • Transport 层:采用 SSE 处理从 Server 到 Client 的推送(单向),使用标准的 HTTP POST 处理从 Client 到 Server 的请求(RPC 消息)。
  • 鉴权层:在建立 SSE 连接或发送 POST 请求时,必须在 HTTP Header 中携带有效的 Authorization: Bearer <token>
  • 上下文隔离:每个 SSE 连接都会绑定到一个特定的 Session ID,Server 内部根据 Session ID 和 Token 中的 User ID 划分数据隔离沙箱。

3. 实战:构建带认证的流式 MCP Server

下面我们将使用 Node.js 构建一个核心片段,展示如何将 JWT 鉴权融入 MCP 的 SSE Transport 中。

3.1 准备鉴权中间件

首先,我们需要一个中间件来验证传入请求的 JWT Token。如果你还没有 JWT,可以使用 QubitTool 提供的 JWT 生成与解析工具 快速生成一个测试 Token。

javascript
import jwt from 'jsonwebtoken';

const SECRET_KEY = process.env.JWT_SECRET || 'your-super-secret-key';

export const authenticateToken = (req, res, next) => {
  const authHeader = req.headers['authorization'];
  const token = authHeader && authHeader.split(' ')[1];

  if (!token) return res.status(401).json({ error: 'Missing token' });

  jwt.verify(token, SECRET_KEY, (err, user) => {
    if (err) return res.status(403).json({ error: 'Invalid token' });
    req.user = user; // 将用户信息注入请求上下文
    next();
  });
};

3.2 建立安全的 SSE 传输通道

在 MCP SDK 中,我们可以自定义 Express 路由来处理 SSE 连接,并在建立连接前应用鉴权中间件。

javascript
import express from 'express';
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';

const app = express();
const mcpServer = new Server({ name: 'Enterprise-MCP', version: '1.0.0' }, {
  capabilities: { tools: {}, resources: {} }
});

// 存储活跃的传输通道
const transports = new Map();

// 1. 建立 SSE 连接 (需要鉴权)
app.get('/sse', authenticateToken, async (req, res) => {
  const transport = new SSEServerTransport('/message', res);
  
  // 将 userId 绑定到特定的 sessionId
  const sessionId = req.query.sessionId || crypto.randomUUID();
  transport.userId = req.user.id; 
  transports.set(sessionId, transport);

  await mcpServer.connect(transport);
  
  req.on('close', () => {
    transports.delete(sessionId);
  });
});

// 2. 接收客户端消息 (同样需要鉴权)
app.post('/message', authenticateToken, express.json(), async (req, res) => {
  const sessionId = req.query.sessionId;
  const transport = transports.get(sessionId);

  if (!transport) {
    return res.status(404).send('Session not found');
  }
  
  // 安全校验:确保发送消息的用户与建立连接的用户一致
  if (transport.userId !== req.user.id) {
    return res.status(403).send('Access denied for this session');
  }

  await transport.handlePostMessage(req, res);
});

3.3 实现大型数据的流式读取 (Stream)

当工具需要返回大量数据时,直接加载到内存会引发 OOM (Out of Memory)。虽然 MCP 协议本身的 JSON-RPC 消息有大小限制,但我们可以通过“分页(Pagination)”或“资源流式读取”的模式来优化。

javascript
// 注册一个分页读取大日志文件的 Tool
mcpServer.setRequestHandler(CallToolRequestSchema, async (request) => {
  if (request.params.name === 'read_large_log') {
    const { offset = 0, limit = 1000 } = request.params.arguments;
    
    // 使用流读取特定范围的数据,避免将整个文件读入内存
    const logChunk = await streamFileChunk('/var/log/enterprise.log', offset, limit);
    
    // 验证返回的 JSON 结构是否符合规范
    // (在调试阶段,可以使用 QubitTool 的 JSON 格式化工具进行检查)
    return {
      content: [
        {
          type: 'text',
          text: JSON.stringify({
            data: logChunk,
            nextOffset: offset + limit,
            hasMore: logChunk.length === limit
          })
        }
      ]
    };
  }
  throw new Error('Tool not found');
});

4. FAQ 常见问题解答

Q: 如何在不同的 LLM 客户端间复用我的企业级 MCP Server? A: 只要客户端(如 Claude Desktop、Cursor 等)支持配置 HTTP Header,就可以复用。对于仅支持 stdio 的客户端,你可以编写一个轻量级的本地中继脚本(Relay Script),由本地脚本读取环境变量中的 Token,再通过 SSE/HTTP 转发给远端企业 Server。

Q: 调试 MCP Server 时遇到 JSON 解析错误怎么办? A: MCP 的通信本质是 JSON-RPC 2.0。如果遇到解析错误,通常是工具返回的结构不符合 MCP Schema 规范。建议将 Server 打印出的原始 JSON 粘贴到 QubitTool 的 JSON 格式化与校验工具 中,快速定位缺失的字段(如 content, type, text 等)。

Q: Base64 编码的二进制数据如何通过 MCP 传输? A: 可以在 Tool 返回的结果中使用 type: 'image',并将图像数据进行 Base64 编码。如果你需要验证编码是否正确,可以使用 Base64 编解码工具 进行双向测试。

总结

构建一个企业级的 MCP Server 并非难事,关键在于从一开始就将鉴权、状态隔离和性能优化纳入架构考量。通过结合 JWT 和 SSE,我们可以安全地将强大的企业数据能力赋予 AI,开启 Agentic Workflow 的无限可能。

希望这篇实战指南能为你的 AI 工程化之路提供启发!