构建 MCP 服务器:第 4 部分 — 创建工具

news2025/6/8 1:10:21

 

这是我们构建 MCP 服务器的四部分教程的最后一部分。在第一部分中,我们使用基本资源创建了第一个 MCP 服务器。第二部分添加了资源模板并改进了代码组织。在第三部分中,我们添加了提示符并进一步完善了服务器结构。现在,我们将通过添加工具来完成服务器的搭建。

什么是 MCP 工具?

工具是 LLM 可以调用来执行操作或检索动态信息的可执行函数。与只读的资源和构建 LLM 交互的提示不同,工具允许 LLM 主动执行诸如计算值、进行 API 调用或修改数据等操作。

为什么要使用工具?

工具使 LLM 能够与系统交互并执行操作。以下是一些实际示例:

文件操作

name: "write-file"
arguments: {
  path: "/logs/report.txt",
  content: "Daily summary..."
}

用户:

将此报告保存到文件”

人工智能:

我将使用写入文件工具...文件已成功保存。

API 交互

name: "fetch-weather"
arguments: {
  location: "San Francisco",
  units: "celsius"
}

用户

旧金山的天气怎么样?

人工智能:

让我查一下……根据天气 API,气温为 18°C,晴朗。

数据处理

name: "analyze-data"
arguments: {
  dataset: "sales_2024_q1",
  operation: "summary_stats"
}

用户:

计算第一季度销售额的汇总统计数据

人工智能:

正在运行分析…平均销售额为 342,中位数为 342,…

添加工具

为我们的新工具创建一个新文件,并添加一个“创建消息”工具:

// src/tools.ts

// Allowed values
const messageTypes = ['greeting', 'farewell', 'thank-you'] as const;
const tones = ['formal', 'casual', 'playful'] as const;

// tool definitions
export const tools = {
  'create-message': {
    name: 'create-message',
    description: 'Generate a custom message with various options',
    inputSchema: {
      type: 'object',
      properties: {
        messageType: {
          type: 'string',
          enum: messageTypes,
          description: 'Type of message to generate',
        },
        recipient: {
          type: 'string',
          description: 'Name of the person to address',
        },
        tone: {
          type: 'string',
          enum: tones,
          description: 'Tone of the message',
        },
      },
      required: ['messageType', 'recipient'],
    },
  },
};

到目前为止,我们所添加的只是我们工具的描述,它将允许使用该工具的模型了解它的作用以及它期望什么样的信息。

现在让我们添加实际的处理程序:

// src/tools.ts

// Allowed values
const messageTypes = ['greeting', 'farewell', 'thank-you'] as const;
const tones = ['formal', 'casual', 'playful'] as const;

// tool definitions
export const tools = {
  // ... existing defs
};

type CreateMessageArgs = {
  messageType: typeof messageTypes[number];
  recipient: string;
  tone?: typeof tones[number];
};

// Simple templates for the various message combinations
const messageFns = {
  greeting: {
    formal: (recipient: string) =>
      `Dear ${recipient}, I hope this message finds you well`,
    playful: (recipient: string) => `Hey hey ${recipient}! 🎉 What's shakin'?`,
    casual: (recipient: string) => `Hi ${recipient}! How are you?`,
  },
  farewell: {
    formal: (recipient: string) =>
      `Best regards, ${recipient}. Until we meet again.`,
    playful: (recipient: string) =>
      `Catch you later, ${recipient}! 👋 Stay awesome!`,
    casual: (recipient: string) => `Goodbye ${recipient}, take care!`,
  },
  "thank-you": {
    formal: (recipient: string) =>
      `Dear ${recipient}, I sincerely appreciate your assistance.`,
    playful: (recipient: string) =>
      `You're the absolute best, ${recipient}! 🌟 Thanks a million!`,
    casual: (recipient: string) =>
      `Thanks so much, ${recipient}! Really appreciate it!`,
  },
};

const createMessage = (args: CreateMessageArgs) => {
  if (!args.messageType) throw new Error("Must provide a message type.");
  if (!args.recipient) throw new Error("Must provide a recipient.");

  const { messageType, recipient } = args;
  const tone = args.tone || "casual";
  if (!messageTypes.includes(messageType)) {
    throw new Error(
      `Message type must be one of the following: ${messageTypes.join(", ")}`,
    );
  }
  if (!tones.includes(tone)) {
    throw new Error(
      `If tone is provided, it must be one of the following: ${
        tones.join(", ")
      }`,
    );
  }

  const message = messageFns[messageType][tone](recipient);

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

export const toolHandlers = {
  "create-message": createMessage,
};

现在让我们更新我们的处理程序:

// src/handlers.ts
import {
  CallToolRequestSchema, // <-- Add this
  GetPromptRequestSchema,
  ListPromptsRequestSchema,
  ListResourcesRequestSchema,
  ListResourceTemplatesRequestSchema,
  ListToolsRequestSchema, // <-- and this
  ReadResourceRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { resourceHandlers, resources } from "./resources.js";
import {
  getResourceTemplate,
  resourceTemplates,
} from "./resource-templates.js";
import { promptHandlers, prompts } from "./prompts.js";
import { toolHandlers, tools } from "./tools.js"; // <-- import our tools
import type { Server } from "@modelcontextprotocol/sdk/server/index.js";

export const setupHandlers = (server: Server): void => {
  // List available resources when clients request them
  
  // ... previously created handlers here

  // tools 
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
    tools: Object.values(tools),
  }));

  server.setRequestHandler(CallToolRequestSchema, async (request) => {
    type ToolHandlerKey = keyof typeof toolHandlers;
    const { name, arguments: params } = request.params ?? {};
    const handler = toolHandlers[name as ToolHandlerKey];

    if (!handler) throw new Error("Tool not found");

    type HandlerParams = Parameters<typeof handler>;
    return handler(...[params] as HandlerParams);
  });
};

最后,更新服务器初始化:

// src/index.ts
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { setupHandlers } from "./handlers.js";

const server = new Server(
  {
    name: "hello-mcp",
    version: "1.0.0",
  },
  {
    capabilities: {
      resources: {},
      prompts: {},
      tools: {}, // <-- Add tools capability
    },
  },
);

setupHandlers(server);

// Start server using stdio transport
const transport = new StdioServerTransport();
await server.connect(transport);

console.info(
  '{"jsonrpc": "2.0", "method": "log", "params": { "message": "Server running..." }}',
);

理解代码

以下是我们目前所做的工作。

工具结构

  • 工具通过 inputSchema 定义其接口
  • 处理程序实现实际功能
  • 返回格式符合 MCP 规范

错误处理

  • 验证必需参数
  • 具体错误消息
  • 类型安全的处理程序访问

使用检查器进行测试

记住首先构建输出:

npm run build

然后启动检查器:

npx @modelcontextprotocol/inspector node build/index.js

测试工具:

  • 点击“工具”选项卡
  • 找到“创建消息”
  • 尝试不同的组合:
{
  "messageType": "thank-you",
  "recipient": "Alice",
  "tone": "playful"
}

使用 Claude Desktop 进行测试

尝试以下示例

注意:您可能会遇到授权使用工具的请求:

基本信息:

用户:

为 Bob 创建问候消息

克劳德:

我会使用消息工具……“嗨,鲍勃!你好吗?”

风格化消息:

用户:

向爱丽丝致以俏皮的谢意

克劳德:

使用消息工具……“你真是太棒了,爱丽丝!🌟 非常感谢!”

不同的消息类型:

用户:

您可以创建哪些类型的消息?

克劳德:

我可以帮助您使用 create-message 函数创建不同类型的消息。您可以生成:

1. 问候
2. 告别
3. 感谢信息

对于每种类型,您可以指定收件人,并可选择将语气设置为正式、随意或俏皮。您想让我演示如何创建特定类型的消息吗?

您的结果可能与上述不同,特别是如果您将此 MCP 与 Claude 以外的工具或 Sonnet 3.5 以外的模型一起使用

总结

恭喜!您现在已经构建了一个完整的 MCP 服务器,其中包含:

  • 静态和基于模板的资源
  • 可自定义的提示
  • 动态工具
  • 组织良好、类型安全的代码

您已学习了如何:

  • 构建 MCP 服务器
  • 实现不同的 MCP 功能
  • 有效地组织代码
  • 优雅地处理错误
  • 使用检查器进行测试
  • 与 Claude Desktop 集成

从这里,您可以:

  • 添加更复杂的工具
  • 与外部 API 集成
  • 实现文件操作
  • 添加数据库连接
  • 创建您自己的自定义功能

请记住,MCP 是一个不断发展的协议,因此请关注官方文档以了解新功能和最佳实践。

资料来源及其他阅读材料:

  • Tools - Model Context Protocol
  • https://github.com/modelcontextprotocol/typescript-sdk
  • https://github.com/modelcontextprotocol/inspector

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2403559.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何以 9 种方式将照片从手机传输到笔记本电脑

使用 USB 电缆可以将照片从智能手机复制到计算机。但是&#xff0c;如果没有 USB 数据线&#xff0c;如何将照片从手机无线传输到笔记本电脑呢&#xff1f;为了解决这个问题&#xff0c;我们搜索并测试了不同的应用程序&#xff0c;然后总结了本指南中分享的 9 个有效选项。您可…

生成JavaDoc文档

生成 JavaDoc 文档 1、快速生成 文档 注解 2、常见的文档注解 3、脚本生成 doc 文档 4、IDEA工具栏生成 doc 文档 第一章 快速入门 第01节 使用插件 在插件工具当中&#xff0c;找到插件 javaDoc 使用方式&#xff0c;在代码区域&#xff0c;直接点击右键。选择 第02节 常用注…

Web后端基础(Maven基础)

https://blog.csdn.net/q20202828/article/details/148459525?spm1001.2014.3001.5501 这是我总结了一下aliyun私服maven依赖配置Maven 3.9.1下载安装的操作 Maven的作用 统一项目结构 Maven 还提供了标准、统一的项目结构 。 1). 未使用Maven 由于java的开发工具呢&#x…

set map数据结构

#include <set> #include <iostream> using namespace std;int main() {// 设置控制台输出编码为UTF-8system("chcp 65001");set<int> s1; // 创建一个整数集合// 插入元素s1.insert(5);s1.insert(3);s1.insert(7);s1.insert(1);s1.insert(9);//默…

面试题小结(真实面试)

面试题 1.call与apply的区别2.vue3的响应式原理3.js的垃圾回收机制4.说说原型链5.什么是防抖和节流6.说一下作用域链7.在一个页面加载数据时&#xff08;还没加载完成&#xff09;&#xff0c;切换到另一个页面&#xff0c;怎么暂停之前页面的数据加载。 浏览器自动中止机制 这…

计算机网络领域所有CCF-A/B/C类期刊汇总!

本期小编统计了【计算机网络】领域CCF推荐所有期刊的最新影响因子&#xff0c;分区、年发文量以及投稿经验&#xff0c;供大家参考&#xff01; CCF-A类 1 IEEE Journal on Selected Areas in Communications 【影响因子】13.8 【期刊分区】JCR1区&#xff0c;中科院1区TOP …

有意向往gis开发靠,如何规划学习?

听说GIS开发工资不错、还不像互联网那么卷&#xff1f;心动了&#xff1f;但一看那些“WebGL”、“空间分析”、“OGC规范”的词儿就头大&#xff1f;别急&#xff01; 今天咱就聊聊零基础/转行选手&#xff0c;咋规划学习GIS开发这条路。不整高大上&#xff0c;就讲实在的&am…

五、查询处理和查询优化

五、查询处理和查询优化 主要内容 查询概述查询处理过程关系操作的基本实现算法查询优化技术代数优化基于存取路径的优化基于代价估算的优化 1. 查询概述 查询是数据库管理系统中使用最频繁、最基本的操作&#xff0c;对系统性能有很大影响。 对于同一个SQL查询&#xff0c…

缓解骨质疏松 —— 补钙和补维 D

骨质老化/疏松原理&#xff08;机制&#xff09;骨密度下降与骨小梁结构退化局部受压导致的微损伤或压力集中 诊断要点治疗策略吃什么食物能补钙呢&#xff1f;钙片吃什么食物能补维生素 D 呢&#xff1f; 骨质老化/疏松 骨质老化&#xff08;常指骨密度下降或骨质疏松&#x…

《PMBOK® 指南》第八版草案重大变革:6 大原则重构项目管理体系

项目管理领域的权威指南迎来关键升级&#xff01;PMI 最新发布的《PMBOK 指南》第八版草案引发行业广泛关注&#xff0c;此次修订首次将项目管理原则浓缩为 6 大黄金法则&#xff0c;重构 7 大绩效域&#xff0c;并首度公开过程组与绩效域的映射关系。本文将全面解析新版核心变…

Ctrl+R 运行xxx.exe,发现有如下问题.

CtrlR 运行xxx.exe,发现有如下问题. (1)找不到Qt5Core.all,Qt5Cored.dll,Qt5Gui.dll,Qt5Guid.dll,Qt5Widgets.all,Qt5Widgetsd.dll? (2)之后找不到libwinpthread-1.dll 从这个目录拷贝相应的库到运行xx.exe目录下 方法二:将库路径添加到系统PATH环境变量里: 在Path中添加路…

极智项目 | 基于PyQT+Whisper实现的语音识别软件设计

这是一个基于OpenAI的Whisper模型的语音识别应用程序&#xff0c;使用PyQt5构建了简洁直观的用户界面。该应用支持多语言识别&#xff0c;特别优化了中文识别体验。 项目下载&#xff1a;链接 功能特点 简洁现代的深色主题界面支持多语言识别&#xff08;中文、英语、日语等…

vue+cesium示例:地形开挖(附源码下载)

基于cesium和vue绘制多边形实现地形开挖效果&#xff0c;适合学习Cesium与前端框架结合开发3D可视化项目。 demo源码运行环境以及配置 运行环境&#xff1a;依赖Node安装环境&#xff0c;demo本地Node版本:推荐v18。 运行工具&#xff1a;vscode或者其他工具。 配置方式&#x…

升级:用vue canvas画一个能源监测设备和设备的关系监测图!

用vue canvas画一个能源电表和设备的监测图-CSDN博客 上一篇文章&#xff0c;我是用后端的数据来画出监测图。这次我觉的&#xff0c;用前端来控制数据&#xff0c;更爽。 本期实现功能&#xff1a; 1&#xff0c;得到监测设备和设备的数据&#xff0c;然后进行存库 2&…

深入理解 transforms.Normalize():PyTorch 图像预处理中的关键一步

深入理解 transforms.Normalize()&#xff1a;PyTorch 图像预处理中的关键一步 在使用 PyTorch 进行图像分类、目标检测等深度学习任务时&#xff0c;我们常常会在数据预处理部分看到如下代码&#xff1a; python复制编辑transform transforms.Compose([transforms.ToTensor…

爆炸仿真的学习日志

今天学习了一下【Workbench LS-DYNA中炸药在空气中爆炸的案例-哔哩哔哩】 https://b23.tv/kmXlN29 一开始 如果你的 ANSYS Workbench 工具箱&#xff08;Toolbox&#xff09;里 只有 SPEOS&#xff0c;即使尝试了 右键刷新、重置视图、显示全部 等方法仍然没有其他分析系统&a…

[华为eNSP] OSPF综合实验

目录 配置流程 画出拓扑图、标注重要接口IP 配置客户端IP 配置服务端IP 配置服务器服务 配置路由器基本信息&#xff1a;名称和接口IP 配置路由器ospf协议 测试结果 通过配置OSPF路由协议&#xff0c;实现跨多路由器的网络互通&#xff0c;并验证终端设备的访问能力。 …

完美搭建appium自动化环境

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 桌面版appium提供可视化操作appium主要功能的使用方式&#xff0c;对于初学者非常适用。 如何在windows平台安装appium桌面版呢&#xff0c;大体分两个步骤&…

c++中的输入输出流(标准IO,文件IO,字符串IO)

目录 &#xff08;1&#xff09;I/O概述 I/O分类 不同I/O的继承关系 不同I/O对应的头文件 &#xff08;2&#xff09;iostream 标准I/O流 iostream头文件中的IO流对象 iostream头文件中重载了<<和>> 缓冲区示意图 标准输入流 cin用法 cin&#xff1a;按空…

2025年渗透测试面试题总结-ali 春招内推电话1面(题目+回答)

安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 ali 春招内推电话1面 一、Web安全核心理解 二、熟悉漏洞及防御方案 三、UDF提权原理与防御 四、XSS Fuzz…