构建 MCP 服务器:第 4 部分 — 创建工具
这是我们构建 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 thisGetPromptRequestSchema,ListPromptsRequestSchema,ListResourcesRequestSchema,ListResourceTemplatesRequestSchema,ListToolsRequestSchema, // <-- and thisReadResourceRequestSchema,
} 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
相关文章:

构建 MCP 服务器:第 4 部分 — 创建工具
这是我们构建 MCP 服务器的四部分教程的最后一部分。在第一部分中,我们使用基本资源创建了第一个 MCP 服务器。第二部分添加了资源模板并改进了代码组织。在第三部分中,我们添加了提示符并进一步完善了服务器结构。现在,我们将通过添加工具来…...
2.1 Windows编译环境介绍
一、Windows四个主要编译工具套件 MSVC:Windows原生编译套件,Microsoft Visual C,VS2019默认使用,编译生成原生Windows程序。Cygwin:不仅移植GCC,还移植了Linux命令(如ls、mkdir、clear&#x…...

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

生成JavaDoc文档
生成 JavaDoc 文档 1、快速生成 文档 注解 2、常见的文档注解 3、脚本生成 doc 文档 4、IDEA工具栏生成 doc 文档 第一章 快速入门 第01节 使用插件 在插件工具当中,找到插件 javaDoc 使用方式,在代码区域,直接点击右键。选择 第02节 常用注…...
八股学习-JS的闭包
一.闭包的定义 闭包是指函数和其周围的词法环境的引用的组合。 简单来说,就是函数可以记住并访问其在定义时的作用域内的变量,即使该函数在其它作用域调用。 也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。 function …...

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…...
学习记录aigc
1、DIT https://zhuanlan.zhihu.com/p/683612528 DiT最大的创新点是将Transformer引入到了扩散模型中,并完全抛弃了CNN。但是DiT并不是第一个引入Transformer的,例如之前的U-ViT,UniDiffuser等都尝试了将Transformer引入到扩散模型中。至于…...

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);//默…...
Q: dify前端使用哪些开发框架?
【回到目录】~~~~【回到问题集】 Q: dify前端使用哪些开发框架? A: 通过查看Readme.md,可以了解到使用以下框架 1. [Next.js] (https://nextjs.org/) React Framework 2. Node.js > v22.11.x 3. pnpm v10.x 4. Storybook UI component development 4. Je…...

面试题小结(真实面试)
面试题 1.call与apply的区别2.vue3的响应式原理3.js的垃圾回收机制4.说说原型链5.什么是防抖和节流6.说一下作用域链7.在一个页面加载数据时(还没加载完成),切换到另一个页面,怎么暂停之前页面的数据加载。 浏览器自动中止机制 这…...
【PmHub面试篇】PmHub中基于Redis加Lua脚本的计数器算法限流实现面试专题解析
你好,欢迎来到本次关于PmHub中基于Redis加Lua脚本的计数器算法限流实现的面试系列分享。在这篇文章中,我们将深入探讨这一技术领域的相关面试题预测。若想对相关内容有更透彻的理解,强烈推荐参考之前发布的博文:【PmHub后端篇】Pm…...

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

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

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

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

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

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模型的语音识别应用程序,使用PyQt5构建了简洁直观的用户界面。该应用支持多语言识别,特别优化了中文识别体验。 项目下载:链接 功能特点 简洁现代的深色主题界面支持多语言识别(中文、英语、日语等…...

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

升级:用vue canvas画一个能源监测设备和设备的关系监测图!
用vue canvas画一个能源电表和设备的监测图-CSDN博客 上一篇文章,我是用后端的数据来画出监测图。这次我觉的,用前端来控制数据,更爽。 本期实现功能: 1,得到监测设备和设备的数据,然后进行存库 2&…...
Elasticsearch + Milvus 构建高效知识库问答系统《一》
🔍 Elasticsearch Milvus 构建高效知识库问答系统(RAG 技术实战) 📌 目录 背景介绍Elasticsearch 在知识库检索中的作用Milvus 在知识库检索中的作用混合检索:Elasticsearch Milvus完整代码实现部署建议与优化方向…...

深入理解 transforms.Normalize():PyTorch 图像预处理中的关键一步
深入理解 transforms.Normalize():PyTorch 图像预处理中的关键一步 在使用 PyTorch 进行图像分类、目标检测等深度学习任务时,我们常常会在数据预处理部分看到如下代码: python复制编辑transform transforms.Compose([transforms.ToTensor…...
leetcode 2434. 使用机器人打印字典序最小的字符串 中等
给你一个字符串 s 和一个机器人,机器人当前有一个空字符串 t 。执行以下操作之一,直到 s 和 t 都变成空字符串: 删除字符串 s 的 第一个 字符,并将该字符给机器人。机器人把这个字符添加到 t 的尾部。删除字符串 t 的 最后一个 字…...

爆炸仿真的学习日志
今天学习了一下【Workbench LS-DYNA中炸药在空气中爆炸的案例-哔哩哔哩】 https://b23.tv/kmXlN29 一开始 如果你的 ANSYS Workbench 工具箱(Toolbox)里 只有 SPEOS,即使尝试了 右键刷新、重置视图、显示全部 等方法仍然没有其他分析系统&a…...
【Fiddler抓取手机数据包】
Fiddler抓取手机数据包的配置方法 确保电脑和手机在同一局域网 电脑和手机需连接同一Wi-Fi网络。可通过电脑命令行输入ipconfig查看电脑的本地IP地址(IPv4地址),手机需能ping通该IP。 配置Fiddler允许远程连接 打开Fiddler,进入…...

[华为eNSP] OSPF综合实验
目录 配置流程 画出拓扑图、标注重要接口IP 配置客户端IP 配置服务端IP 配置服务器服务 配置路由器基本信息:名称和接口IP 配置路由器ospf协议 测试结果 通过配置OSPF路由协议,实现跨多路由器的网络互通,并验证终端设备的访问能力。 …...
东芝Toshiba DP-4528AG打印机信息
东芝 Toshiba DP 4528AG 是一款黑白激光数码复合机: 类型:激光数码复合机,涵盖复印、打印、扫描、传真功能,能满足办公室多样化的文档处理需求。速度类型:中速,黑白复印和打印速度可达 45 页 / 分钟&#…...
Vue3+Vite中lodash-es安装与使用指南
在 Vue 3 Vite 项目中安装和使用 lodash-es 的详细指南如下: 一、为什么选择 lodash-es? ES 模块支持:lodash-es 以原生 ES 模块格式发布,支持现代构建工具的 Tree Shaking 按需加载:只引入需要的函数,显…...

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

c++中的输入输出流(标准IO,文件IO,字符串IO)
目录 (1)I/O概述 I/O分类 不同I/O的继承关系 不同I/O对应的头文件 (2)iostream 标准I/O流 iostream头文件中的IO流对象 iostream头文件中重载了<<和>> 缓冲区示意图 标准输入流 cin用法 cin:按空…...