Cursor前端初体验-不懂MCP?那就做一个MCP

前言

AI越来越火了,从年初公司提出AI优先后,这股AI的风也是慢慢吹到了团队中,笔者所在的团队是一个有着十几个人的前端小团队。其他团队的小伙伴慢慢卷起来了,隔天就要开个AI工具分享会,当其他人卷起来时,你也不得不跟紧脚步,于是便开始了解cursor,慢慢发现他似乎对我们日常的工作帮助还是蛮大的,从被迫AI到接受AI再到主动AI。

了解Cursor后,会发现mcp其实是区分他与之前的AI工具非常大的一个变动,甚至于了解完Mcp后,我甚至有点想通过他搞钱的想法,加上钱的动力,让我下定决心搞懂MCP,赚钱嘛不寒碜

什么是MCP

在cursor的文档中,解释的很透彻了,虽然他有点儿很浓的技术味。MCP其实就是供LLM智能体直接使用的工具。你可以让大模型通过MCP直接调用某些能力。

拿现实类比,AI就好像电脑,MCP就好像U盘,可以让电脑用你U盘里的所有数据,或者软件。

而他最吸引我的功能,我认为就是通过MCP可以让大模型知道实时的数据,这也是我今天想分享的内容

众所周知GPT出世的那会,AI最为诟病的其实是数据的延后性与复杂性。数据的延后让你没办法实时通过AI获取到你想要的内容,比如,我今天该不该买股票啊!!我相信用到AI的时候,各位韭菜肯定动过让AI帮你买股票的心思。数据复杂性更不用说了,一股脑检索全网的数据,让AI一本正经的胡说八道。

开发MCP

关于MCP的开发,官方文档其实说的挺清晰了,但按着文档走,终归是会遇到点问题的,所以还是将开发的全过程分享出来 因为我是前端,所以自然而然的选择TS语言,其他的语言也有,奢俭由人吧

前端的话,能看到这的大家都是懂一点的,初始化工程的事长话短说
node>22 初始化package.json

js 复制代码
{
  "name": "mcp-cursor",
  "version": "1.0.0",
  "description": "",
  "main": "dist/index.js",
  "scripts": {
    "build": "tsc",
    "dev": "tsc --watch",
    "start": "node dist/index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.15.1",
    "zod": "^3.25.76"
  },
  "devDependencies": {
    "@types/node": "^24.0.13",
    "typescript": "^5.7.2"
  }
}

在入口文件src/index.ts我们进行代码的编写

编写之前,先分析下我们的需求:我想写一个MCP工具让cursor能够通过当前的工具,实时获取到某个地方的天气预报,例如获取到美国加州的当前天气。那么我们需要的就有以下几方面的内容

1、有个接口能让我实时获得天气预报,这个MCP文档中提供了

2、有现成工具包,能让我调用他以后,cursor就能自动调用上面的接口,这个便是MCP官方提供的工具包@modelcontextprotocol/sdk

一、初始化服务器,可以直接在本地运行

javascript 复制代码
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";// 基础MCP服务,官方提供
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";// 服务器与大模型通信桥梁
import { z } from "zod";// 校验工具,校验大模型调用接口时传的入参格式

const NWS_API_BASE = "https://api.weather.gov"; 
const USER_AGENT = "weather-app/1.0"; 

// 创建 server instance 
const server = new McpServer({ 
    name: "weather",
    version: "1.0.0",
    capabilities: { resources: {}, tools: {}, },
});

二、提供接口调用方法与数据处理

在该MCP工具中需要暴露出能够给AI调用的接口能力,大模型执行该方法后,能够获得到对应的数据给大模型

js 复制代码
// 定义请求方法
async function makeNWSRequest<T>(url: string): Promise<T | null> {
  const headers = {
    "User-Agent": USER_AGENT,
    Accept: "application/geo+json",
  };

  try {
    const response = await fetch(url, { headers });
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    return (await response.json()) as T;
  } catch (error) {
    console.error("Error making NWS request:", error);
    return null;
  }
}
// 定义数据结构格式化方法
function formatAlert(feature: AlertFeature): string {
  const props = feature.properties;
  return [
    `Event: ${props.event || "Unknown"}`,
    `Area: ${props.areaDesc || "Unknown"}`,
    `Severity: ${props.severity || "Unknown"}`,
    `Status: ${props.status || "Unknown"}`,
    `Headline: ${props.headline || "No headline"}`,
    "---",
  ].join("\n");
}

三、注册Tool类,提供给大模型调用

这里只注册一个方法,就是根据缩写获取到对应州的地址 tool方法有四个参数

第一个是方法名

第二个是方法描述,

第三个是对于大模型调用该方法时入参的数据格式校验,这里用zod规定了大模型传的state只能是2个字符

第四个就是具体的方法了,return 的数据会提供给大模型

js 复制代码
server.tool(
  "get-alerts",
  "Get weather alerts for a state",
  {
    state: z.string().min(2).max(2).describe("Two-letter state code (e.g. CA, NY)"),
  },
  async ({ state }) => {
    const stateCode = state.toUpperCase();
    const alertsUrl = `${NWS_API_BASE}/alerts?area=${stateCode}`;
    //
    const alertsData = await makeNWSRequest<AlertsResponse>(alertsUrl);

    if (!alertsData) {
      return {
        content: [
          {
            type: "text",
            text: "Failed to retrieve alerts data",
          },
        ],
      };
    }

    const features = alertsData.features || [];
    if (features.length === 0) {
      return {
        content: [
          {
            type: "text",
            text: `No active alerts for ${stateCode}`,
          },
        ],
      };
    }

    const formattedAlerts = features.map(formatAlert);
    const alertsText = `Active alerts for ${stateCode}:\n\n${formattedAlerts.join("\n")}`;

    return {
      content: [
        {
          type: "text",
          text: alertsText,
        },
      ],
    };
  },
);

运行 server

完成业务逻辑开发后,启动服务

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

main().catch((error) => {
  console.error("Fatal error in main():", error);
  process.exit(1);
});

以上便完成了全部mcp的业务开发,接下来要做的就是执行build生成产物,这里生成的产物就在本地环境,dist/index.js,为了验证你的产物是否正常,可以让cursor为你生成一个测试脚本,这里就不再赘述,测试脚本内容也就是手动构造入参,然后调用对应的tool类

调试MCP

打开cursor配置,在tool目录下,将我们刚刚写的mcp工具添加进去,

这其实就是告诉cursor,执行 node dist/index.js命令就能拿到我提供的工具类了

js 复制代码
{
  "mcpServers": {
    "weather": {
      "command": "node",// 命令
      "args": ["/Users/mishi/Cursor/dist/index.js"],// 地址指向
      "env": {}
    }
  }
}

效果展示

可以看到大模型给工具类传入了{state:"CA"} 并且能够根据result给我们提供相应的建议

结语

能看到这的,你以为我们只是让大模型帮我们做天气预报吗?如果这里的数据不是天气预报,而是某个公司爬取下来的财报,亦或者就是某个公司的近半年股价。那么大模型,会帮助我们分析出什么呢?

我承认,一开始脑海中浮现的就是这个,才推动着我,手写一个MCP。不仅学到了点东西,甚至能让大模型真正为个人服务

相关推荐
爱编程的喵21 分钟前
React Router Dom 初步:从传统路由到现代前端导航
前端·react.js
每天吃饭的羊37 分钟前
react中为啥使用剪头函数
前端·javascript·react.js
Nicholas681 小时前
Flutter帧定义与60-120FPS机制
前端
多啦C梦a1 小时前
【适合小白篇】什么是 SPA?前端路由到底在路由个啥?我来给你聊透!
前端·javascript·架构
薛定谔的算法1 小时前
《长安的荔枝·事件流版》——一颗荔枝引发的“冒泡惨案”
前端·javascript·编程语言
中微子1 小时前
CSS 的 position 你真的理解了吗?
前端·css
谜构1 小时前
【0编码】我使用Trae AI开发了一个【随手记账单格式化工具】
前端
G_whang2 小时前
jenkins部署前端vue项目使用Docker+Jenkinsfile方式
前端·vue.js·jenkins
ZhangApple2 小时前
微信自动化工具:让自己的微信变成智能机器人!
前端·后端
袋鱼不重2 小时前
手把手搭建Vue轮子从0到1:2. 搭建框架雏形
前端