深入MCP本质——编写自定义MCP Server并通过Cursor调用

这篇文章将深入探讨 MCP (Model Context Protocol) 的核心原理,并通过一个简单的自定义天气工具示例,揭示 AI 模型如何与本地系统进行高效、安全的交互。


实战配置:启动你的自定义 MCP Server

首先,我们通过一个简单的步骤来配置并运行一个自定义的 MCP Server,该 Server 能够获取电脑所在地的实时时间和天气(不使用代理的前提下)。

步骤 1: 代码准备

下面是我编写的 MCP Server ,你可以将代码库拉取到本地目录

github.com/Objecteee/m...

步骤 2: Cursor 配置

在 Cursor IDE 的 mcp.json 配置文件中添加如下配置,指定你的本地 Server 脚本的路径:

json 复制代码
{
  "Real Weather Tool": {
    "command": "node",
    "args": [
      "E:\project\mcp\myMCP\index.js"  # 请确保这里是你的本地文件路径
    ]
  }
}

配置完成后,您即可在 Cursor 的对话框中通过自然语言调用这个工具,例如询问:"现在的天气怎么样?"


1. 核心架构:什么是 MCP?

MCP (Model Context Protocol) 是由 Anthropic 提出的一种开放协议,旨在解决 大型语言模型 (LLM)外部世界(本地文件、数据库、API 等) 之间的信息鸿沟。

  • 没有 MCP 时: LLM 仅依赖训练数据,无法感知外部世界的实时状态(如当前时间、实时天气、本地代码结构)。它就像一个"孤岛上的智者"。
  • 有了 MCP: 我们为 LLM 提供了一个标准化的"工具插座"。任何遵循 MCP 协议的外部程序(即 MCP Server )都可以插入这个插座,使 LLM 能够 控制 这些工具并 获取 实时数据。

2. 本质原理:Cursor 如何连接并调用工具?

Cursor 连接和调用自定义 Server 的过程,本质上是基于 标准输入/输出 (Stdio)进程间通信 (IPC) ,它采用 JSON-RPC 格式进行数据交换。我们可以将整个过程分为两个阶段:

阶段一:握手(Handshake)与工具发现

  1. 启动子进程: 当你在 Cursor 中保存 mcp.json 配置后,Cursor 会在后台启动一个 子进程 (Child Process) ,执行你指定的 node index.js 脚本。
  2. 发送询问: Cursor(父进程)通过 标准输入 (stdin) 向你的脚本发送一条 JSON-RPC 格式的消息,请求获取可用的工具列表。
  3. 返回列表: 你的脚本(子进程)收到请求后,会通过 标准输出 (stdout) 返回一个包含其所有工具定义(如 TimeWeatherReporter)的 JSON 响应。这对应于代码中的 server.setRequestHandler(ListToolsRequestSchema...)

阶段二:待命与工具调用

  1. 待命: 只要 Cursor 不关闭,你的脚本进程就会在后台持续运行,等待调用指令。
  2. LLM 触发: 用户在聊天框输入指令(如"现在的天气怎么样?"),Cursor 内置的 AI 模型分析请求,决定调用哪个工具。
  3. 发送指令: Cursor 向你的脚本发送一条 JSON-RPC 调用请求,指定要执行的工具名称和参数。
  4. 执行任务: 你的脚本接收指令,执行相应的内部函数(例如请求 wttr.in 获取天气)。
  5. 反馈结果: 脚本将执行结果打包成 JSON,通过 stdout 发回给 Cursor。
  6. 结果展示: Cursor 接收到工具输出的原始数据,将其作为 上下文 传递给 AI 模型,由模型组织成自然语言的答案回复用户。

3. 代码层面的关键实现:MCP Server 运行机制 (index.js)

我们的注意力不要放在代码的实现上,要放在代码的流程上,因为MCP只是一种规范。

这个 Node.js 脚本通过遵循 MCP 规范,并利用标准输入/输出流 (Stdio) 实现通信。以下是驱动 MCP Server 运行的五个关键代码片段:

3.1 核心模块导入:定义协议与传输层

代码片段 作用描述 核心原理
import { Server } from "@modelcontextprotocol/sdk/server/index.js"; Server 实例 MCP 服务端核心类,负责处理 JSON-RPC 请求和响应。
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; Stdio 传输层 声明通信载体是基于 标准输入/输出 (stdin/stdout) ,而非网络端口。
import { CallToolRequestSchema, ListToolsRequestSchema } from ... 协议定义 导入请求的 JSON Schema,用于解析 Cursor 发来的不同类型的指令。

3.2 初始化 MCP 服务器

vbscript 复制代码
const server = new Server(
  {
    name: "simple-weather-server",
    version: "1.0.0",
  },
  {
    capabilities: {
      tools: {},
    },
  }
);

作用: 创建服务器实例。capabilities.tools: {} 明确告诉 MCP,该服务器具备提供工具的能力。

3.3 注册工具列表:响应"握手"请求

php 复制代码
server.setRequestHandler(ListToolsRequestSchema, async () => {
  return {
    tools: [
      {
        name: "TimeWeatherReporter",
        description: "Get current system time and weather...",
        inputSchema: {
          type: "object",
          properties: {
            location: {
              type: "string",
              description: "Optional. City name (e.g. 'Beijing')...",
            },
          },
          required: [],
        },
      },
    ],
  };
});

作用 这是握手阶段(Handshake) 的关键。当 Cursor 首次启动子进程时,会发送 ListToolsRequestSchema 请求。该代码片段负责返回一个清晰的 JSON Schema 描述:

  • name: 工具的唯一标识符。
  • description: 供 AI 理解工具用途的自然语言描述。
  • inputSchema: JSON Schema 格式的参数定义,AI 依赖此信息来构造正确的调用参数。

3.4 处理工具调用:执行业务逻辑

csharp 复制代码
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  if (request.params.name === "TimeWeatherReporter") {
    const location = request.params.arguments?.location;
    return await getSystemStyleWeather(location); // 调用实际的业务函数
  }
  throw new Error("Tool not found");
});

作用: 这是执行阶段(Execution) 的核心。当 Cursor 的 AI 决定调用工具时,会发送 CallToolRequestSchema 请求。这段代码根据 request.params.name 匹配到对应的业务逻辑 (getSystemStyleWeather),执行后将结果返回给 Cursor。

3.5 启动服务器:连接到 Stdio 流

javascript 复制代码
async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error("Simple Weather MCP Server running on stdio");
}

作用: 连接点。 StdioServerTransport() 创建了传输通道,server.connect(transport) 则将服务器实例绑定到这个通道上。至此,您的 Node.js 脚本准备就绪,可以通过 stdin/stdout 与 Cursor 进程进行双向通信。


4. 总结:MCP 的本质

角色 实体 职责
雇主 (Client) Cursor IDE (父进程) 启动并监控工具,发送调用指令,接收并处理结果。
雇员 (Server) node index.js (子进程) 接收指令,执行实际任务(如 API 调用、文件读写),返回原始数据。
工作合同 (Protocol) MCP 协议 规定了握手和调用过程中 JSON 数据的结构和标准。
对讲机 (Transport) Stdio (标准输入/输出) 实现本地父进程与子进程之间即时、安全的通信。

MCP 的精妙之处在于它的解耦性安全性:Cursor 客户端无需关心你的 Server 是用 Node.js、Python 还是 Go 编写。只要你的程序符合 MCP 协议,能够通过命令行流接收和发送 JSON 数据,它就能无缝地成为 AI 的外部工具,极大地扩展了 LLM 的能力边界。


自定义MCP Server代码

javascript 复制代码
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import axios from "axios";
import { z } from "zod";

// 1. Initialize MCP Server
const server = new Server(
  {
    name: "simple-weather-server",
    version: "1.0.0",
  },
  {
    capabilities: {
      tools: {},
    },
  }
);

// 2. Define Tool Logic using wttr.in (No API Key required)
async function getSystemStyleWeather(location) {
  // --- Get Real Local Time (System Time) ---
  const now = new Date();
  const localTime = now.toLocaleTimeString("zh-CN", {
    hour: "2-digit",
    minute: "2-digit",
    second: "2-digit",
    hour12: false,
  });
  const localDate = now.toLocaleDateString("zh-CN");

  // --- Determine URL ---
  // wttr.in automatically detects location by IP if no location is provided
  // format=j1 gives us a JSON response
  let url = "https://wttr.in/?format=j1";
  if (location) {
    url = `https://wttr.in/${encodeURIComponent(location)}?format=j1`;
  }

  try {
    const response = await axios.get(url);
    const data = response.data;
    
    // Parse wttr.in specific JSON structure
    const current = data.current_condition[0];
    const area = data.nearest_area[0];

    const weatherInfo = {
      location: location || area.areaName[0].value, // Use detected name if no input
      region: area.region[0].value,
      condition: current.weatherDesc[0].value,
      temperature_c: current.temp_C,
      feelslike_c: current.FeelsLikeC,
      humidity: current.humidity,
      wind_kph: current.windspeedKmph,
      data_source: "wttr.in (IP-based auto-location)"
    };

    // --- Construct Result ---
    const result = {
      status: "success",
      tool: "SystemWeatherReporter",
      system_time: `${localDate} ${localTime}`,
      weather: weatherInfo
    };

    return {
      content: [
        {
          type: "text",
          text: JSON.stringify(result, null, 2),
        },
      ],
    };

  } catch (error) {
    return {
      content: [{ type: "text", text: `Unable to fetch weather data. Please check your network connection. Error: ${error.message}` }],
      isError: true,
    };
  }
}

// 3. Register Tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
  return {
    tools: [
      {
        name: "TimeWeatherReporter",
        description: "Get current system time and weather. Automatically detects location if not specified. No API key required.",
        inputSchema: {
          type: "object",
          properties: {
            location: {
              type: "string",
              description: "Optional. City name (e.g. 'Beijing'). If omitted, uses auto-detection.",
            },
          },
          required: [],
        },
      },
    ],
  };
});

// 4. Handle Tool Calls
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  if (request.params.name === "TimeWeatherReporter") {
    const location = request.params.arguments?.location;
    return await getSystemStyleWeather(location);
  }
  throw new Error("Tool not found");
});

// 5. Start Server
async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error("Simple Weather MCP Server running on stdio");
}

main().catch((error) => {
  console.error("Server error:", error);
  process.exit(1);
});
相关推荐
想用offer打牌7 小时前
MCP (Model Context Protocol) 技术理解 - 第二篇
后端·aigc·mcp
崔庆才丨静觅8 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60619 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了9 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅9 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅9 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅10 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment10 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅10 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊10 小时前
jwt介绍
前端