游侠网云服务,免实名免备案服务器 游侠云域名,免实名免备案域名

统一声明:

1.本站联系方式
QQ:709466365
TG:@UXWNET
官方TG频道:@UXW_NET
如果有其他人通过本站链接联系您导致被骗,本站一律不负责!

2.需要付费搭建请联系站长QQ:709466365 TG:@UXWNET
3.免实名域名注册购买- 游侠云域名
4.免实名国外服务器购买- 游侠网云服务

Model Context Protocol (MCP) 正在改变 AI 应用连接外部工具和数据的方式。虽然许多教程停留在”连接 GitHub”或”读取文件”的层面,但 MCP 的真正力量在于解锁您的内部数据——数据库、内部 API、知识库和专有系统——以结构化、安全的方式向 AI 助手开放。

本文将带您完成构建生产级 MCP 服务器的全过程,将组织的内部数据暴露给 AI 模型,涵盖认证、多租户、流式传输和实际部署所需的模式。

How to Build MCP Servers for Your Internal Data

什么是 MCP,为什么它对内部数据很重要?

MCP 是由 Anthropic 创建的开放协议,标准化了 AI 助手发现和调用外部工具的方式。将其想象成 AI 的 USB-C 端口——一个标准接口,让任何 AI 模型连接到任何数据源。

在 MCP 之前,将 AI 助手连接到内部数据库意味着:

  • 为每个 LLM 提供商编写自定义工具定义
  • 将数据访问逻辑硬编码到 AI 应用中
  • 切换模型或添加新数据源时重新构建一切

MCP 将数据层AI 层分离。您的 MCP 服务器暴露工具和资源,任何兼容 MCP 的客户端(Claude、ChatGPT、您的自定义应用)都可以使用它们而无需修改。

架构概览

MCP server architecture

MCP 服务器位于 AI 客户端和内部系统之间,处理:

  • 工具发现:告诉 AI 有哪些可用操作
  • 参数验证:确保 AI 发送正确的输入
  • 数据访问:查询内部系统
  • 响应格式化:返回 AI 可以推理的结构化数据
  • 认证:验证请求者身份

项目设置

让我们构建一个暴露内部员工目录和项目管理系统的 MCP 服务器:

mkdir internal-data-mcp && cd internal-data-mcp
npm init -y
npm install @modelcontextprotocol/sdk zod express pg
npm install -D typescript @types/node @types/express @types/pg tsx

构建 MCP 服务器

步骤 1:服务器骨架

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";

const server = new McpServer(
  { name: "internal-data", version: "1.0.0" },
  { capabilities: { tools: {}, resources: {} } }
);

步骤 2:连接内部数据

创建数据访问层(以 PostgreSQL 为例):

import pg from "pg";

const pool = new pg.Pool({
  connectionString: process.env.INTERNAL_DB_URL,
  max: 10,
  idleTimeoutMillis: 30000,
});

export async function searchEmployees(
  query: string,
  department?: string
): Promise<Employee[]> {
  // 参数化查询防止 SQL 注入
  const result = await pool.query(
    `SELECT id, name, email, department, role FROM employees 
     WHERE name ILIKE $1 OR email ILIKE $1
     ORDER BY name LIMIT 25`,
    [`%${query}%`]
  );
  return result.rows;
}

步骤 3:定义工具

工具设计是最重要的部分。好的工具定义直接影响 AI 使用数据的效果:

server.tool(
  "search_employees",
  "搜索内部员工目录,按姓名、邮箱或角色查找。当用户询问人员、团队或组织结构时使用。",
  {
    query: z.string().describe("搜索词:员工姓名、邮箱或角色"),
    department: z.string().optional().describe("按部门名称过滤"),
  },
  async ({ query, department }) => {
    const employees = await searchEmployees(query, department);
    if (employees.length === 0) {
      return { content: [{ type: "text", text: `未找到匹配 "${query}" 的员工。` }] };
    }
    const formatted = employees.map(e => 
      `- **${e.name}** (${e.email})\n 角色:${e.role} | 部门:${e.department}`
    ).join("\n");
    return { content: [{ type: "text", text: `找到 ${employees.length} 名员工:\n\n${formatted}` }] };
  }
);

工具设计原则

  1. 描述性的名称和描述:AI 完全根据描述决定调用哪个工具
  2. 带描述的类型化参数:使用 Zod 的.describe()说明每个字段的期望
  3. 结构化返回值:使用 Markdown 表格或结构化列表,而非原始 JSON

添加认证

Bearer Token 认证

最简单的方法是验证每个请求的 token:

function authMiddleware(req, res, next) {
  const authHeader = req.headers.authorization;
  if (!authHeader?.startsWith("Bearer ")) {
    return res.status(401).json({ error: "Missing authorization header" });
  }
  const token = authHeader.slice(7);
  const claims = validateInternalToken(token);
  req.userId = claims.sub;
  req.orgId = claims.org;
  next();
}

OAuth 2.0 for MCP

对于支持 MCP 内置 OAuth 流的客户端,可以实现完整的 OAuth 握手。MCP SDK 提供OAuthServerProvider接口,包括客户端注册、授权重定向、token 交换和验证等方法。

按用户范围限制数据访问

这是内部数据 MCP 服务器最重要的部分:AI 应该只访问请求用户有权看到的数据。

server.tool(
  "search_employees",
  "搜索员工目录。结果根据您的访问级别过滤。",
  { query: z.string().describe("姓名、邮箱或角色") },
  async ({ query }) => {
    const ctx = getUserContext();
    
    // 执行访问边界
    let departmentFilter;
    if (ctx.role === "manager") {
      departmentFilter = await getUserDepartment(ctx.userId);
    }
    
    const employees = await searchEmployees(query, departmentFilter);
    
    // 根据角色隐藏敏感字段
    const results = employees.map(e => ({
      name: e.name,
      email: e.email,
      department: e.department,
      role: e.role,
      ...(["admin", "hr"].includes(ctx.role) ? 
        { start_date: e.start_date, manager_id: e.manager_id } : {}),
    }));
    
    return { content: [{ type: "text", text: formatEmployeeList(results) }] };
  }
);

生产部署

Docker 化

使用两阶段构建保持镜像精简:

FROM node:22-slim AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:22-slim AS runtime
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
ENV NODE_ENV=production
EXPOSE 3100
HEALTHCHECK --interval=30s --timeout=5s CMD curl -f http://localhost:3100/health || exit 1
CMD ["node", "dist/index.js"]

健康检查和监控

app.get("/health", async (_req, res) => {
  const checks = { database: false, ticketingApi: false };
  try {
    await pool.query("SELECT 1");
    checks.database = true;
  } catch {}
  try {
    const resp = await fetch(`${process.env.TICKETING_API_URL}/health`);
    checks.ticketingApi = resp.ok;
  } catch {}
  const healthy = Object.values(checks).every(Boolean);
  res.status(healthy ? 200 : 503).json({
    status: healthy ? "healthy" : "degraded",
    checks,
    uptime: process.uptime(),
  });
});

日志和审计

每次工具调用都应该记录:

function createAuditLogger() {
  return {
    logToolCall(params) {
      console.log(JSON.stringify({
        event: "mcp_tool_call",
        timestamp: new Date().toISOString(),
        ...params,
      }));
    },
  };
}

常见陷阱

  1. 返回太多数据:LLM 有上下文限制,分页或限制结果(25 项是合理的默认值)
  2. 工具描述太笼统:描述是模型阅读的内容,不要仅依赖工具名称
  3. 缺少错误处理:返回结构化错误消息,而非堆栈跟踪
  4. 没有限流:AI 工具调用可能在循环中发生,需要熔断器
  5. 不用实际 AI 模型测试:在单元测试中正确的工具可能让模型困惑

总结

为内部数据构建 MCP 服务器关乎三件事:

  1. 好的工具设计——清晰的描述、类型化参数、结构化响应
  2. 适当的访问控制——认证用户、范围数据访问、记录一切
  3. 生产就绪——健康检查、限流、错误处理、监控

协议本身很简单。困难的工作是在内部系统之上设计正确的抽象,使 AI 能够有效使用它们,同时不泄露数据或压垮上下文窗口。

从一个或两个高价值工具(员工查找、文档搜索)开始,用真实用户测试,然后逐步扩展。最好的内部 MCP 服务器根据人们实际询问 AI 的内容有机增长。