MCP 入门教程:给 AI 接工具和数据源的最小可行方案

智能体

用一个待办事项和文档查询案例讲清 MCP 的作用、服务端结构、工具定义、权限边界和调试方法,帮助开发者把 AI 从聊天框接到真实系统。

MCP(Model Context Protocol)可以理解成 AI 应用的“外设接口”。过去你想让模型读数据库、查文档、改任务系统,往往要为每个客户端写一套插件。MCP 的目标是把工具和数据源标准化:服务端暴露能力,客户端发现并调用,模型通过统一协议拿到上下文和工具。

工具接口与数据源连接
工具接口与数据源连接

适用人群

这篇教程适合想给 AI 接内部系统的开发者、自动化工具作者和团队管理员。如果你只是在一个应用里硬编码两个 API,MCP 未必必要;如果你希望同一个数据源被 Claude、Cursor、Codex 或内部 AI 助手复用,MCP 就很值得学。

MCP 解决什么问题

AI 应用需要三类外部能力:读取资源、调用工具、保持提示模板。读取资源包括文档、数据库记录、项目文件;调用工具包括创建任务、搜索网页、发起构建;提示模板则是可复用的任务说明。MCP 把这些能力封装成标准接口,让客户端不用理解你的业务细节,只需要知道“这里有一个可调用工具”。

最重要的是边界清楚。模型不是直接拿到数据库密码,而是调用你定义好的工具。工具可以做权限检查、参数校验、审计记录和速率限制。

第一步:选择一个低风险场景

不要从“让 AI 管理生产数据库”开始。最小场景可以是:读取团队知识库、查询待办事项、创建草稿任务。比如我们做一个“项目助手”:它能读取项目文档,查询待办列表,并创建新的待办草稿。

这个场景有三个好处:数据结构简单,风险可控,用户能立刻感受到价值。创建任务也先做成草稿,不直接分配给别人,避免误操作。

第二步:定义资源与工具

资源适合只读内容,例如 docs://project/roadmaptodos://current-user。工具适合动作,例如 searchDocs(query)createTodoDraft(title, detail, dueDate)。定义时要注意参数尽量明确,不要把整个自由文本塞给后端。

一个工具的设计应该包含:名称、用途说明、参数 schema、返回结构、错误信息。模型会根据说明决定什么时候调用,所以描述要写给模型看,也要写给人看。比如“创建待办草稿,不会直接发布;返回草稿 ID 和预览链接”就比“创建任务”安全得多。

团队项目管理与任务卡片
团队项目管理与任务卡片

第三步:实现服务端

服务端可以用 Node.js 或 Python。最小实现要完成三件事:声明能力、处理工具调用、返回结构化结果。开发时建议先用本地 stdio 或本地 HTTP 调试,跑通后再考虑部署。

不要在工具函数里直接信任模型参数。比如 dueDate 要校验日期格式,title 要限制长度,query 要限制字符数。模型可能不是恶意的,但它会生成不符合业务规则的输入。服务端必须像对待普通外部请求一样做防护。

TypeScript 版工具定义示例

下面用 TypeScript 展示一个“创建待办草稿”的 MCP 工具。不同 SDK 的注册函数名称会不同,但参数 schema、权限检查和返回结构可以直接照搬。

const createTodoDraftTool = {
  name: "createTodoDraft",
  description: "创建待办草稿,不会直接发布,返回草稿 ID 和预览链接。",
  inputSchema: {
    type: "object",
    properties: {
      title: { type: "string", maxLength: 80 },
      detail: { type: "string", maxLength: 1000 },
      dueDate: { type: "string", description: "YYYY-MM-DD" },
    },
    required: ["title", "detail"],
  },
  async handler(input, context) {
    if (!context.user.canCreateTodo) {
      throw new Error("当前用户没有创建待办草稿权限");
    }

    const draft = await todoService.createDraft({
      title: input.title,
      detail: input.detail,
      dueDate: input.dueDate || null,
      createdBy: context.user.id,
    });

    return {
      draftId: draft.id,
      previewUrl: `/todos/drafts/${draft.id}`,
      status: "draft_created",
    };
  },
};

截图检查点

这类教程最该截图的不是“AI 回答很聪明”,而是 MCP 客户端发现了哪些工具、一次工具调用传入了什么参数、服务端日志记录了什么。读者看到日志,才知道权限和审计不是口号。

第四步:权限与审计

MCP 真正进入团队使用时,权限是第一优先级。不同用户能看到的资源不同,能调用的工具也不同。比如普通员工可以查询自己的待办,项目管理员可以创建团队任务,访客只能读取公开文档。

每次工具调用都应记录:谁调用、何时调用、参数是什么、结果是什么、是否成功。对于高风险工具,还要加入人类确认。例如“发送邮件”“删除记录”“修改价格”“部署生产”都不应该让模型直接执行。

第五步:调试与提示词

调试 MCP 时,不要只看模型最终回答。要看它有没有选择正确工具、参数是否合理、工具返回是否被正确理解。如果模型总是不调用工具,可能是工具描述不清楚;如果它频繁调用错误工具,可能是工具边界重叠。

提示词里可以明确:“涉及项目文档时优先调用 searchDocs;涉及创建任务时先生成草稿并请用户确认。”这类规则能让模型更稳。

开发者调试接口与日志
开发者调试接口与日志

一个可复用的服务设计

如果团队准备认真使用 MCP,建议把服务端拆成三层:协议层、业务层和审计层。协议层只负责接收客户端请求和返回 MCP 需要的结构;业务层调用真实系统,比如文档库、任务系统、CRM;审计层记录每一次读取和动作。这样将来更换客户端时,不需要重写业务逻辑。

工具也要分级。只读工具可以默认开放给更多用户,例如搜索文档、读取公开项目状态;写入工具需要确认,例如创建任务草稿、生成邮件草稿;高风险工具默认不开放,例如删除数据、修改订单、发布内容。分级以后,模型能力再强也不会越过组织边界。

常见坑

| 现象 | 原因 | 修法 | |------|------|------| | 模型总是不调用工具 | 工具描述太像普通说明 | 描述里写清触发条件,例如“查询项目文档时使用” | | 调错工具 | 多个工具边界重叠 | 合并同类工具,或在 description 里写“不用于……” | | 返回内容挤爆上下文 | 工具返回整篇文档 | 返回前 5 条摘要和资源 ID,需要全文再二次读取 | | 用户越权看到数据 | 只在客户端做限制 | 服务端 handler 里检查 context.user 权限 | | 出错后模型胡编 | 错误信息不可读 | 返回结构化错误:codemessageretryable |

错误返回建议统一成这样:

return {
  ok: false,
  error: {
    code: "PERMISSION_DENIED",
    message: "当前用户不能读取该项目文档",
    retryable: false,
  },
};

替代方案

如果只是单应用内部调用,可以直接写函数调用或 API 路由。如果你需要低代码自动化,n8n、Zapier、Make 更快。如果你做的是复杂 Agent 图,可以用 LangGraph、LlamaIndex Workflows 这类框架。MCP 的优势在于跨客户端复用和标准化,不一定是所有场景的最短路径。

小结

MCP 的价值不是“让 AI 更神奇”,而是让 AI 接入工具时更可控、更可复用。最小可行方案从只读资源和低风险草稿工具开始,等权限、审计、错误处理稳定后,再逐步接入更关键的业务动作。