保姆级MCP开发上手指南

超详细MCP开发指南,有手就会,开发属于自己的MCP吧。

什么是MCP

MCP官方文档modelcontextprotocol.io/quickstart

用户视角 :MCP与传统的Chat对话服务系统不同,它可以直接处理、编辑和分析用户本地的文件,就像拥有一位能够亲自处理本地文件的"AI PC私人助理"!

技术视角 :MCP(Model Context Protocol)本质上是一个允许AI模型安全访问外部工具和数据的协议。然而,它的意义远不止于此------它是连接AI能力与现实世界的桥梁。通过MCP,AI助手可以"看到 "文件系统、"操作 "数据库以及"使用 "各种工具,这极大地扩展了AI的实际应用范围。在稳定性方面,MCP拥有官方服务和社区贡献,已经建立了标准协议、软件开发工具包(SDK)和安全框架,同时也正在形成新的软件范式。举个例子,当您想将一些指定的后缀文件放入到一个新的文件夹时,只需要输入给MCP一行指令,便可以直接帮您省略终端输入命令行的流程

一、环境准备

step1: 检查环境

目前mcp官方提供了NodeJs 和 Python 两个语言版本的开发SDK,配套工具都很成熟。下面以Node举例。

js 复制代码
# 检查环境,node版本需要>=16,建议>=20
node --version

# 如果没有node,推荐安装nvm,进行node版本管理。
# 安装 nvm (Node 版本管理器)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash

Step 2:创建项目存在以下三种方法

方法一: 使用create-typescript-server 工具快速初始化一个,快速记笔记的小工具(是一个通过mcp-server 链接 local data sources 的例子)

js 复制代码
# Create a new server in the directory `my-server`
npx @modelcontextprotocol/create-server my-server

# With options
npx @modelcontextprotocol/create-server my-server --name "My MCP Server" --description "A custom MCP server"

推荐使用这个方法。

方法二: 参考quickstart文档。 以下是基于node实现一个图片读取的MCP为例:

1、需要先创建文件:

js 复制代码
// 创建项目的文件夹
mkdir image-processor
cd image-processor

// 初始化项目(执行完后,项目中会初始化相关的文件)
npm init -y

// 创建开发文件(MCP的逻辑在这个文件编写)
mkdir src
touch src/index.ts

2、 配置 package.json 开发MCP时需要借助于一些外部依赖库的能力进行文件读写,需要在配置​package.json后,​npm install安装。

json 复制代码
{
  "name": "@block/image-processor",
  "version": "1.0.0",
  "description": "A MCP server for image",
  "type": "module",
  "bin": {
    "image-processor": "./build/index.js"
  },
  "scripts": {
    "build": "tsc && chmod +x build/index.js",
    "prepublishOnly": "npm run build"
  },
  "files": [
    "build"
  ],
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.0.4",
    "axios": "^1.7.9",
    "form-data": "^4.0.1",
    "fs": "^0.0.1-security",
    "os": "^0.1.2",
    "path": "^0.12.7",
    "zod": "^3.24.1",
    "zod-to-json-schema": "^3.24.1"
  },
  "devDependencies": {
    "@types/node": "^22.10.2",
    "typescript": "^5.7.2"
  },
  "publishConfig": {
    "access": "public"
  },
  "keywords": [
    "mcp",
    "image"
  ]
}

如果是基于​typescript实现,还需配置​tsconfig.json

json 复制代码
{
    "compilerOptions": {
      "target": "ES2022",
      "module": "Node16",
      "moduleResolution": "Node16",
      "outDir": "./build",
      "rootDir": "./src",
      "strict": true,
      "esModuleInterop": true,
      "skipLibCheck": true,
      "forceConsistentCasingInFileNames": true,
    },
    "include": ["src/**/*"],
    "exclude": ["node_modules"]
  }

方法三: 从官方的server汇总仓库里fork一个你感兴趣的server,然后在上面二开

二、 开发MCP

MCP的逻辑主要在​src/index.ts中实现。以图片阅读MCP为例,编写代码如下,代码逻辑在此不赘述。 关于你想尝试实现的任何功能,都可以利用AI coding完成

js 复制代码
#!/usr/bin/env node

import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { zodToJsonSchema } from "zod-to-json-schema";
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
  ToolSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";
import path from "path";
import fs from "fs/promises";
import os from "os";

const ToolInputSchema = ToolSchema.shape.inputSchema;
type ToolInput = z.infer<typeof ToolInputSchema>;

const args = process.argv.slice(2);
if (args.length === 0) {
  console.error(
    "Usage: mcp-server-filesystem <allowed-directory> [additional-directories...]"
  );
  process.exit(1);
}

// Normalize all paths consistently
function normalizePath(p: string): string {
  return path.normalize(p);
}

function expandHome(filepath: string): string {
  if (filepath.startsWith("~/") || filepath === "~") {
    return path.join(os.homedir(), filepath.slice(1));
  }
  return filepath;
}

// Store allowed directories in normalized form
const allowedDirectories = args.map((dir) =>
  normalizePath(path.resolve(expandHome(dir)))
);

// Validate that all directories exist and are accessible
await Promise.all(
  args.map(async (dir) => {
    try {
      const stats = await fs.stat(dir);
      if (!stats.isDirectory()) {
        console.error(`Error: ${dir} is not a directory`);
        process.exit(1);
      }
    } catch (error) {
      console.error(`Error accessing directory ${dir}:`, error);
      process.exit(1);
    }
  })
);

/** 读取图片的工具 */
const ReadImageArgsSchema = z.object({
  path: z.string(),
});

// Create server instance
const server = new Server(
  {
    name: "image-processor",
    version: "1.0.0",
  },
  {
    capabilities: {
      tools: {},
    },
  }
);

// List available tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
  return {
    tools: [
      {
        name: "read-image",
        description: "通过图片路径读取图片",
        inputSchema: zodToJsonSchema(ReadImageArgsSchema) as ToolInput,
      },
    ],
  };
});

// Handle tool execution
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const { name, arguments: args } = request.params;

  try {
    if (name === "read-image") {
      const parsed = ReadImageArgsSchema.safeParse(args);
      if (!parsed.success) {
        throw new Error(`Invalid arguments for read_file: ${parsed.error}`);
      }
      // 获取图片路径
      const imagePath = parsed.data.path;
      // 读取图片文件为 Buffer
      const imageBuffer = await fs.readFile(imagePath);
      // 转换为 base64
      const base64String = imageBuffer.toString("base64");
      // 获取文件扩展名并确定 MIME 类型
      const ext = path.extname(imagePath).toLowerCase();
      const mimeType =
        {
          ".png": "image/png",
          ".jpg": "image/jpeg",
          ".jpeg": "image/jpeg",
          ".gif": "image/gif",
          ".webp": "image/webp",
        }[ext] || "application/octet-stream";

      return {
        content: [
          {
            type: "image",
            data: base64String,
            mimeType: mimeType,
          },
        ],
      };
    } else {
      throw new Error(`Unknown tool: ${name}`);
    }
  } catch (error) {
    console.error("Error in request handler:", error);

    if (error instanceof z.ZodError) {
      throw new Error(
        `Invalid arguments: ${error.errors
          .map((e) => `${e.path.join(".")}: ${e.message}`)
          .join(", ")}`
      );
    }
    throw new Error(`Failed to fetch image`);
  }
});

// Start the server
async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error("Image Processor Server running on stdio");
}

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

三、本地验证

3.1 本地打包构建

本地开发完成后,还需要打包构建,才能接入大模型客户端使用(比如Cursor等) 由于已经在​package.json配置了打包构建的相关信息,执行​npm run build,可以在src的同级目录发现一个新的 build 文件夹,内部包含一个index.js文件。

3.2 验证

方式一: 直接将本地构建的产物接入大模型客户端。在客户端配置文件中增加本地已经构建的MCP入口文件,看是否能正常使用该MCP。(如果仅需要自己本地使用,那么到这一步就可以了,不需要发布流程)

js 复制代码
// args 1为我本地项目的打包构建后的地址,args 2为用户给这个MCP提供的参数,代表提供权限的路径,使用方式与filesystem MCP一致
"image-processor": {
	"command": "node",
	"args": [
		"/Users/ruixingshi/project/image-processor/build/index.js",
		"/Users/ruixingshi/Documents"
	]
}

方式二: 使用​@modelcontextprotocol/inspector 连接本地开发好的MCP,在本地Server中看是否可以正常读取MCP的tool。在控制台执行命令:

js 复制代码
/* 全局安装 @modelcontextprotocol/inspector */
npm install -g @modelcontextprotocol/inspector 

/* 运行: */
npx @modelcontextprotocol/inspector node path/to/server/index.js args...


// 解释: path/to/server/index.js 是你的mcp工程在你终端的相对路径

// args 是需要传入的参数: 例如你写的MCP需要创建文档到你的电脑文档层 就是: /Users/xxx/Documents。 需要改为你自己电脑的路径


// 举例
npx @modelcontextprotocol/inspector /Users/xxx/project/build/index.js /Users/xxx/Documents

运行成功后会启动一个5173端口的界面,启动的本地页面http://localhost:5173

点击Connect之后 Run Tool,就可以进行调试了。

方式三: 在Claude桌面端中调试 目前Claude 注册需要国外手机号验证码,本文作者并未验证,后续更新。

四、发布

可以将本地代码发布为npm包,允许其他用户在大模型客户端上通过配置直接接入,无需手动下载任何代码。

js 复制代码
​npm publish

效果:

写一个记笔记的MCP,效果如下:

思考:

MCP这种以大模型工具为中心的交互范式,未来是有可能颠覆之前大家在各个web页面使用各种工具的范式的,我们需要积极拥抱和探索。

从前端的视角简单类比下:

Claude Desktop < ---------------> 浏览器

MCP server <---------------> 各种站点 & 工具(devtools\mep 等等)

本地数据/远端接口 <---------------> 后端接口

相关推荐
小青龙emmm4 分钟前
机器学习(七)
人工智能·机器学习
数字供应链安全产品选型8 分钟前
安全左移动赋能:灵脉IAST交互式应用安全测试平台
网络·人工智能·安全·开源·开源软件
Mountain and sea1 小时前
焊接机器人与线激光视觉系统搭配的详细教程
人工智能·opencv·机器人
灏瀚星空1 小时前
高效图像处理工具:从需求分析到落地实现
图像处理·人工智能·经验分享·python·学习方法
安冬的码畜日常1 小时前
【AI 加持下的 Python 编程实战 2_02】第一章:利用 GitHub Copilot 叩开 AI 辅助编程的大门
人工智能·python·ai·copilot·ai助手·ai应用·ai辅助编程
Terrence Shen1 小时前
跟着AI复习一下pytorch原理和操作
人工智能·pytorch·python·深度学习·算法·机器学习
月落星还在2 小时前
AI学习——深度学习核心技术深度解析
人工智能·深度学习
Allen_LVyingbo2 小时前
历次科技泡沫对人工智能发展的启示与规避措施
人工智能·科技
平凡而伟大(心之所向)2 小时前
一文讲清楚CUDA与PyTorch、GPU之间的关系
人工智能·pytorch·python
Channing Lewis2 小时前
大语言模型的潜力是否被高估
人工智能·机器学习·语言模型