AI 终于有了"插件系统"——MCP 完全指南

写给想搞懂 MCP 的开发者,不讲废话,直接上干货


先说一个让人头疼的问题

你有没有遇到过这种情况:

你想让 AI 帮你查 GitHub 上的 issue,它说:"我没有访问 GitHub 的权限。"

你想让 AI 帮你操作数据库,它说:"我无法连接到您的数据库。"

你想让 AI 读取本地文件,它说:"我没有文件系统访问权限。"

每次都要自己写一堆胶水代码,把 AI 和各种工具连起来。写完这个项目,下个项目又得重新写一遍。

这就是 MCP 要解决的问题。


MCP 到底是什么?

MCP 全称是 Model Context Protocol(模型上下文协议),是 Anthropic 在 2024 年底推出的一个开放标准。

一句话解释:

MCP 是 AI 的"USB 接口"------定义了一套标准,让任何 AI 应用都能用同一种方式接入任何外部工具和数据源。

在 MCP 出现之前,每个 AI 应用想接入外部工具,都要自己造轮子,写各种适配代码。就像早年电脑接外设,每个厂商接口都不一样,乱得一塌糊涂。

MCP 就是那个统一的 USB 标准------工具只需要实现一次,所有支持 MCP 的 AI 应用都能直接用。


MCP 的三个核心角色

理解 MCP,先搞清楚三个角色:

scss 复制代码
┌─────────────────────────────────────────┐
│           MCP Host(宿主)               │
│   Claude Desktop / Cursor / 你的应用    │
└──────────────────┬──────────────────────┘
                   │ MCP 协议
        ┌──────────┴──────────┐
        ↓                     ↓
┌──────────────┐    ┌──────────────────┐
│  MCP Server  │    │   MCP Server     │
│  (文件系统)  │    │   (数据库)       │
└──────────────┘    └──────────────────┘
  • MCP Host(宿主) :使用 AI 的应用,比如 Claude Desktop、Cursor、你自己写的 AI 应用
  • MCP Server(服务端) :提供具体能力的服务,比如文件读写、数据库查询、GitHub 操作
  • MCP Client(客户端) :内嵌在 Host 里,负责和 Server 通信

你只需要写一个 MCP Server,所有支持 MCP 的 Host 都能直接用你的工具。


MCP Server 能提供什么?

MCP Server 可以暴露三种东西:

🔧 Tools(工具)

AI 可以调用的函数,类似 Function Calling。

erlang 复制代码
查询数据库、发送邮件、操作文件、调用 API...

📄 Resources(资源)

AI 可以读取的数据,类似文件系统。

erlang 复制代码
本地文件、数据库记录、API 返回的数据...

💬 Prompts(提示词模板)

预定义的提示词,可以复用。

erlang 复制代码
代码审查模板、文档生成模板、分析报告模板...

来一个真实的代码示例(JavaScript)

用 @modelcontextprotocol/sdk 写一个最简单的 MCP Server:

安装依赖

bash 复制代码
npm install @modelcontextprotocol/sdk

写一个天气查询 MCP Server

javascript 复制代码
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';

// 创建 MCP Server
const server = new McpServer({
  name: 'weather-server',
  version: '1.0.0',
});

// 注册一个 Tool:查询天气
server.tool(
  'get_weather',
  '获取指定城市的实时天气信息',
  {
    city: z.string().describe('城市名称,例如:北京、上海、广州'),
  },
  async ({ city }) => {
    // 实际项目里这里调用真实天气 API
    const weatherData = {
      北京: { temperature: 22, condition: '晴', humidity: 40 },
      上海: { temperature: 25, condition: '多云', humidity: 65 },
      广州: { temperature: 30, condition: '阵雨', humidity: 80 },
    };

    const weather = weatherData[city];
    if (!weather) {
      return {
        content: [{ type: 'text', text: `未找到城市:${city}` }],
      };
    }

    return {
      content: [
        {
          type: 'text',
          text: `${city}天气:${weather.condition},气温 ${weather.temperature}°C,湿度 ${weather.humidity}%`,
        },
      ],
    };
  }
);

// 注册一个 Resource:读取本地文件
server.resource(
  'local-file',
  'file://{path}',
  async (uri) => {
    const path = uri.pathname;
    const fs = await import('fs/promises');

    try {
      const content = await fs.readFile(path, 'utf-8');
      return {
        contents: [{ uri: uri.href, mimeType: 'text/plain', text: content }],
      };
    } catch (error) {
      throw new Error(`读取文件失败:${error.message}`);
    }
  }
);

// 启动 Server(通过标准输入输出通信)
const transport = new StdioServerTransport();
await server.connect(transport);

console.error('Weather MCP Server 已启动');

就这么几十行,一个 MCP Server 就写好了。

在 Claude Desktop 里使用

在 Claude Desktop 的配置文件里加上你的 Server:

json 复制代码
// ~/Library/Application Support/Claude/claude_desktop_config.json
{
  "mcpServers": {
    "weather": {
      "command": "node",
      "args": ["/path/to/your/weather-server.js"]
    }
  }
}

重启 Claude Desktop,它就能直接调用你的天气查询工具了。


一个更完整的例子:数据库查询 Server

javascript 复制代码
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';
import Database from 'better-sqlite3';

const server = new McpServer({
  name: 'sqlite-server',
  version: '1.0.0',
});

const db = new Database('./mydata.db');

// Tool:执行 SQL 查询
server.tool(
  'query_database',
  '执行 SQL 查询并返回结果,只支持 SELECT 语句',
  {
    sql: z.string().describe('要执行的 SQL 查询语句,只允许 SELECT'),
  },
  async ({ sql }) => {
    // 安全检查:只允许 SELECT
    if (!sql.trim().toUpperCase().startsWith('SELECT')) {
      return {
        content: [{ type: 'text', text: '错误:只允许执行 SELECT 查询' }],
        isError: true,
      };
    }

    try {
      const rows = db.prepare(sql).all();
      return {
        content: [
          {
            type: 'text',
            text: JSON.stringify(rows, null, 2),
          },
        ],
      };
    } catch (error) {
      return {
        content: [{ type: 'text', text: `查询失败:${error.message}` }],
        isError: true,
      };
    }
  }
);

// Tool:获取表结构
server.tool(
  'get_table_schema',
  '获取数据库中指定表的结构信息',
  {
    table_name: z.string().describe('表名'),
  },
  async ({ table_name }) => {
    try {
      const schema = db
        .prepare(`PRAGMA table_info(${table_name})`)
        .all();

      return {
        content: [
          {
            type: 'text',
            text: JSON.stringify(schema, null, 2),
          },
        ],
      };
    } catch (error) {
      return {
        content: [{ type: 'text', text: `获取表结构失败:${error.message}` }],
        isError: true,
      };
    }
  }
);

// Resource:暴露数据库表列表
server.resource(
  'database-tables',
  'db://tables',
  async () => {
    const tables = db
      .prepare(`SELECT name FROM sqlite_master WHERE type='table'`)
      .all();

    return {
      contents: [
        {
          uri: 'db://tables',
          mimeType: 'application/json',
          text: JSON.stringify(tables, null, 2),
        },
      ],
    };
  }
);

const transport = new StdioServerTransport();
await server.connect(transport);

配置好之后,你就可以直接对 Claude 说:"帮我查一下 users 表里上个月注册的用户有多少",它会自己去查数据库,给你真实的结果。


MCP 的通信方式

MCP Server 和 Host 之间有两种通信方式:

Stdio(标准输入输出)

适合本地工具,Server 作为子进程运行:

ini 复制代码
const transport = new StdioServerTransport();
await server.connect(transport);

HTTP + SSE(服务端推送)

适合远程服务,Server 作为独立的 HTTP 服务运行:

ini 复制代码
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
import express from 'express';

const app = express();
const transport = new SSEServerTransport('/messages', res);
await server.connect(transport);

app.listen(3000);

MCP vs Function Calling,傻傻分不清楚?

很多人会问:MCP 和 Function Calling 有什么区别?

对比项 MCP Function Calling
定位 标准协议,跨应用复用 单次对话内的工具调用
复用性 写一次,所有 Host 都能用 每个应用自己实现
适合场景 工具生态、持久化服务 单个应用内的功能扩展
部署方式 独立进程 / 远程服务 内嵌在应用代码里
标准化 有统一协议规范 各家 API 格式不同

简单来说:

  • Function Calling 是"这个应用里我能调用哪些函数"
  • MCP 是"我写了一个工具,所有 AI 应用都能用"

两者不是替代关系,很多 MCP Server 内部就是用 Function Calling 的思路实现的。


现成的 MCP Server,拿来就用

不想自己写?社区已经有大量现成的 MCP Server:

Server 功能 地址
@modelcontextprotocol/server-filesystem 本地文件读写 官方
@modelcontextprotocol/server-github GitHub 操作 官方
@modelcontextprotocol/server-postgres PostgreSQL 查询 官方
@modelcontextprotocol/server-brave-search Brave 搜索 官方
@modelcontextprotocol/server-slack Slack 消息 官方
mcp-server-sqlite SQLite 查询 社区

安装官方文件系统 Server:

bash 复制代码
npx @modelcontextprotocol/server-filesystem /your/directory

加到 Claude Desktop 配置:

perl 复制代码
{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": [
        "@modelcontextprotocol/server-filesystem",
        "/Users/yourname/Documents"
      ]
    }
  }
}

配置完重启,Claude 就能直接读写你的文档目录了。


几个容易踩的坑

坑 1:忘了处理错误返回

MCP Tool 的返回里有个 isError 字段,出错时要设置为 true,这样 AI 才知道调用失败了,会尝试其他方式。

javascript 复制代码
// ❌ 错误做法:直接 throw
throw new Error('查询失败');

// ✅ 正确做法:返回错误信息
return {
  content: [{ type: 'text', text: `查询失败:${error.message}` }],
  isError: true,
};

坑 2:Tool 描述不够清晰

和 Function Calling 一样,Tool 的描述越详细,AI 越知道什么时候该用它。

坑 3:没有做权限控制

MCP Server 有访问本地资源的能力,一定要做好权限控制,别让 AI 随便删文件、执行危险 SQL。

less 复制代码
// 数据库操作只允许 SELECT
if (!sql.trim().toUpperCase().startsWith('SELECT')) {
  return { content: [{ type: 'text', text: '只允许 SELECT 查询' }], isError: true };
}

// 文件操作限制在指定目录
if (!filePath.startsWith(ALLOWED_DIR)) {
  return { content: [{ type: 'text', text: '不允许访问该目录' }], isError: true };
}

坑 4:Stdio 模式下别用 console.log

Stdio 模式下,标准输出是用来和 Host 通信的,你的日志要用 console.error 输出到标准错误,否则会破坏通信协议。

javascript 复制代码
// ❌ 会破坏通信
console.log('Server 启动了');

// ✅ 正确做法
console.error('Server 启动了');

进阶:给你的 AI 应用接入 MCP

不只是 Claude Desktop,你自己写的 AI 应用也可以作为 MCP Host:

javascript 复制代码
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';

// 连接到 MCP Server
const transport = new StdioClientTransport({
  command: 'node',
  args: ['./weather-server.js'],
});

const client = new Client({ name: 'my-app', version: '1.0.0' }, {});
await client.connect(transport);

// 列出所有可用工具
const { tools } = await client.listTools();
console.log('可用工具:', tools.map(t => t.name));

// 调用工具
const result = await client.callTool({
  name: 'get_weather',
  arguments: { city: '北京' },
});

console.log('结果:', result.content[0].text);

把这些工具列表传给 OpenAI 或其他大模型,就实现了完整的 MCP Host。


总结

MCP 的核心思想就一句话:

定义一套标准,让 AI 工具的开发和使用彻底解耦------工具只需要写一次,所有 AI 应用都能用。

它解决的是 AI 生态里的"碎片化"问题,让工具开发者和 AI 应用开发者都能专注自己的事。

对于开发者来说,现在是入场 MCP 生态的好时机:

  1. 用现成的 Server:直接用官方和社区的 MCP Server,快速扩展 AI 能力
  2. 写自己的 Server:把你的业务系统包装成 MCP Server,让 AI 直接操作
  3. 做 MCP Host:在你的 AI 应用里支持 MCP,接入整个工具生态

推荐学习路径:

  1. 在 Claude Desktop 里配置一个官方 MCP Server(比如文件系统)
  2. 跑通上面的天气查询示例
  3. 把你自己项目里的一个接口包装成 MCP Server

跑通了,你就真正理解 MCP 了。

相关推荐
2601_9689007713 分钟前
Agent沙盒基础设施拆解:三层镜像与按需加载的工程取舍
人工智能
楚楚25121 分钟前
2026实测:智能体办公平台三周上手真实体验
人工智能
智圣新创0130 分钟前
教育数据要素流通刚需下 智圣新创高校数据中台解决方案的全域建设落地框架
大数据·人工智能·物联网
xianghongtao011635 分钟前
如何选择一台适合你的本地 AI 硬件设备?Lucy AI Studio——从本地 AI 办公到家庭影音的完整指南
人工智能·端脑科技·本地ai硬件设备·kickstarter众筹项目·lucy ai studio·lucy aios
ZhangJun9541 分钟前
Mobike 共享单车分析项目
人工智能·python·算法·kmeans·聚类·knn
互联网资讯44 分钟前
本地创业者入局 AI 短剧,本地化部署怎么选更省钱
大数据·人工智能
唐维康1 小时前
昆工计算机408考研2027招285人,比去年少6人
人工智能·考研·昆明理工大学
Martina_03211 小时前
AI生成的过山车轨道导入后频繁脱轨?用5步检查样条、轨距与碰撞
图像处理·人工智能·游戏·3d·材质·游戏策划·关卡设计
小猴子爱上树1 小时前
跨马翻译:批量图片翻译+视频字幕+智能抠图,跨境电商在线图片翻译工具
大数据·人工智能·python·音视频
佳瑞Jarrett1 小时前
从 0 到 1 手搓 AI 调解训练系统:双 NPC 实时语音 + AI 自动评分(FastAPI + Qwen-Audio Realtime 实战)
人工智能