发帖
 找回密码
 立即注册
搜索
0 0 0
资源分享 850 0 2 小时前

什么是模型上下文协议(MCP)

· MCP (Model Context Protocol) 是 OpenAI 推出的一个 协议,用来让大模型和外部系统(数据库、API、本地文件、工具链等)打通。

· MCP 最早由Antrhopic 于2024 年底提出,至2025 年初,OpenAI、Google 也加入支援MCP 协定。

· 同时 MCP(模型上下文协议)是一个用于连接 AI 应用与外部系统的开源标准。

· 它把「模型」和「工具」之间的交互规范化,像一个“插件协议”。

· MCP 工具就是在这个协议下挂接的外部能力
271a4fda5305a0080d025c9bf46820d2a5acdeca.webp
本文Demo GitHub - pmhw/McpDemo: Model Context Protocol 的一个简单示例代码

MCP Tools 的作用

  1. 扩展模型能力
  • 比如大模型本身不会直接操作 MySQL,但是你可以通过 MCP 工具,把 MySQL 封装成一个“工具”,模型就能发 MCP 请求去查数据。
  1. 标准化交互
  • MCP 工具有统一的输入输出格式,方便模型调用,不需要每次都为不同工具写一堆 glue code。
  1. 安全可控
  • 工具都跑在 MCP 服务里,调用权限、范围可控,避免模型随意直接访问敏感系统。
  1. 开发和复用
  • 一个 MCP 工具可以在不同模型/不同应用里复用,不用每次重写。

    f61c5d472db45fb0ff06256eb6ac619819ceffdc.webp

    编写 MCP

编写 MCP

  1. 启动 MCP Inspector ( MCP 检查器 )

https://modelcontextprotocol.io/legacy/tools/inspector

有node 环境的 直接运行

npx @modelcontextprotocol/inspecto

访问启动的URL
f1dea7307577cf65ae452007bc642f1b8fbf484a.webp
访问后界面
3e2276f0d05f66a1a0c5f14d3855a218f1745e49.png

  1. 所需依赖

modelcontextprotocol: Model Context Protocol (MCP) 是一个 开放标准,用来让 AI 模型和外部工具/数据源 互相通信。

3.编写代码

导入 MCP SDK 相关模块

// 导入 MCP SDK 相关模块

import { Server } from '@modelcontextprotocol/sdk/server/index.js';

import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';

import {

CallToolRequestSchema,

ListToolsRequestSchema,

} from '@modelcontextprotocol/sdk/types.js';

创建 MCP 服务器实例

/**
 * 创建 MCP 服务器实例
 * 
 * @param {Object} serverInfo - 服务器信息
 * @param {string} serverInfo.name - 服务器名称
 * @param {string} serverInfo.version - 服务器版本
 * @param {Object} capabilities - 服务器能力配置
 * @param {Object} capabilities.tools - 工具能力配置
 */
const server = new Server(
  {
    name: 'lottery-tool',        // 服务器名称,用于标识
    version: '1.0.0',           // 服务器版本号
  },
  {
    capabilities: {
      tools: {},                // 声明支持工具功能
    },
  }
);

定义工具列表

/**
 * 定义工具列表
 * 
 * 每个工具包含:
 * - name: 工具名称
 * - description: 工具描述
 * - inputSchema: 输入参数模式(JSON Schema)
 */
const tools = [
  {
    name: 'draw_lottery',
    description: '从给定的选项列表中随机抽取一个或多个结果',
    inputSchema: {
      type: 'object',
      properties: {
        options: {
          type: 'array',
          items: { type: 'string' },
          description: '抽签选项列表',
        },
        count: {
          type: 'number',
          description: '抽取数量,默认为1',
          default: 1,
        },
        allow_duplicate: {
          type: 'boolean',
          description: '是否允许重复抽取,默认为false',
          default: false,
        },
      },
      required: ['options'],  // 必需参数
    },
  },
  {
    name: 'roll_dice',
    description: '投掷骰子,支持自定义面数和数量',
    inputSchema: {
      type: 'object',
      properties: {
        sides: {
          type: 'number',
          description: '骰子面数,默认为6',
          default: 6,
        },
        count: {
          type: 'number',
          description: '骰子数量,默认为1',
          default: 1,
        },
      },
    },
  },
  {
    name: 'flip_coin',
    description: '抛硬币,返回正面或反面',
    inputSchema: {
      type: 'object',
      properties: {
        count: {
          type: 'number',
          description: '抛硬币次数,默认为1',
          default: 1,
        },
      },
    },
  },
];

处理工具列表请求

server.setRequestHandler(ListToolsRequestSchema, async () => {
  return {
    tools,  // 返回工具列表
  };
});

处理工具调用请求

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

  try {
    // 根据工具名称执行相应的功能
    switch (name) {
      case 'draw_lottery': {
        // 抽签工具:从选项列表中随机抽取结果
        const { options, count = 1, allow_duplicate = false } = args;
  
        // 参数验证
        if (!Array.isArray(options) || options.length === 0) {
          throw new Error('选项列表不能为空');
        }
  
        if (count > options.length && !allow_duplicate) {
          throw new Error('抽取数量不能超过选项数量(不允许重复时)');
        }

        // 执行抽签逻辑
        const results = [];
        const availableOptions = [...options];  // 创建选项副本

        for (let i = 0; i < count; i++) {
          // 随机选择索引
          const randomIndex = Math.floor(Math.random() * availableOptions.length);
          const selected = availableOptions[randomIndex];
          results.push(selected);
    
          // 如果不允许重复,从可用选项中移除已选择的
          if (!allow_duplicate) {
            availableOptions.splice(randomIndex, 1);
          }
        }

        // 返回格式化的结果
        return {
          content: [
            {
              type: 'text',
              text: `🎲 抽签结果:\n${results.map((result, index) => `${index + 1}. ${result}`).join('\n')}`,
            },
          ],
        };
      }

      case 'roll_dice': {
        // 投骰子工具:模拟投掷骰子
        const { sides = 6, count = 1 } = args;
  
        // 参数验证
        if (sides < 2) {
          throw new Error('骰子面数至少为2');
        }
  
        if (count < 1) {
          throw new Error('骰子数量至少为1');
        }

        // 执行投骰子逻辑
        const results = [];
        let total = 0;
  
        for (let i = 0; i < count; i++) {
          // 生成 1 到 sides 之间的随机数
          const roll = Math.floor(Math.random() * sides) + 1;
          results.push(roll);
          total += roll;
        }

        // 根据骰子数量格式化输出
        const resultText = count === 1 
          ? `🎲 投掷结果:${results[0]}`
          : `🎲 投掷结果:${results.join(', ')}\n📊 总计:${total}`;

        return {
          content: [
            {
              type: 'text',
              text: resultText,
            },
          ],
        };
      }

      case 'flip_coin': {
        // 抛硬币工具:模拟抛硬币
        const { count = 1 } = args;
  
        // 参数验证
        if (count < 1) {
          throw new Error('抛硬币次数至少为1');
        }

        // 执行抛硬币逻辑
        const results = [];
        let heads = 0;  // 正面次数
        let tails = 0;  // 反面次数
  
        for (let i = 0; i < count; i++) {
          // 50% 概率生成正面或反面
          const result = Math.random() < 0.5 ? '正面' : '反面';
          results.push(result);
          if (result === '正面') heads++;
          else tails++;
        }

        // 根据抛硬币次数格式化输出
        const resultText = count === 1 
          ? `🪙 抛硬币结果:${results[0]}`
          : `🪙 抛硬币结果:${results.join(', ')}\n📊 统计:正面 ${heads} 次,反面 ${tails} 次`;

        return {
          content: [
            {
              type: 'text',
              text: resultText,
            },
          ],
        };
      }

      default:
        // 未知工具名称
        throw new Error(`未知工具:${name}`);
    }
  } catch (error) {
    // 错误处理:返回错误信息
    return {
      content: [
        {
          type: 'text',
          text: `❌ 错误:${error.message}`,
        },
      ],
      isError: true,
    };
  }
});

启动服务器

/**
 * 启动服务器
 * 
 * 使用标准输入输出传输协议启动 MCP 服务器
 * 这种方式适合与 AI 客户端(如 Claude Desktop)集成
 */
async function main() {
  try {
    // 创建标准输入输出传输实例
    const transport = new StdioServerTransport();
  
    // 连接服务器到传输层
    await server.connect(transport);
  
    // 输出启动信息(使用 stderr 避免干扰 MCP 协议)
    console.error('🎲 抽签工具 MCP 服务器已启动');
    console.error('📡 传输方式:标准输入输出 (stdio)');
    console.error('🔧 可用工具:draw_lottery, roll_dice, flip_coin');
  } catch (error) {
    console.error('❌ 服务器启动失败:', error);
    process.exit(1);
  }
}

// 启动服务器并处理错误
main().catch((error) => {
  console.error('❌ 服务器运行错误:', error);
  process.exit(1);
});

调试

d02c4e270db81dec94debc10dd3a204097869889.webp

我的代码放置示例 src/server.js

需要对mcp 调试工具进行如下配置

Transport Type: stdio
URL: (留空)
Command: node
Args: src/server.js
Working Directory: D:\代码存储\McpDemo

bf9a001327e1541e318886a91def22c8274ed515.webp
继续配置 Working Directory: D:\代码存储\McpDemo
8d4318948bf7cfb6df65fcb9ff34da9dfa115847.webp

2fbdb4412e86f1dafb872272d71902a7e910e3f5.webp

981506a0691d27ff4ecc5f3784ad8885ae88e09d.webp
点击Connect 即可开始
ab5cbd523208e0dd9ee9924e6be2170e6a3e4b9b.png

58f2543196fd4aa214982df003fe6d4db4af8400.webp

至此您的第一个mcp 插件开发完成了

如何在AI 中调用呢

以cursor 示例

如下图设置中配置以下参数 根据您代码存储位置自行调整

{
    "mcpServers": {
      "lottery-tool": {
        "command": "node",
        "args": ["D:\\代码存储\\McpDemo\\src\\server.js"],
        "cwd": "D:\\代码存储\\McpDemo"
      }
    }
  }

fffedc5b1e1a2266a43a2b55de18386cee0f2f7d.webp

补充

Demo:https://github.com/pmhw/McpDemo

MCP英文文档:https://modelcontextprotocol.io/docs/getting-started/intro

──── 0人觉得很赞 ────

使用道具 举报

您需要登录后才可以回帖 立即登录
高级模式