构建 MCP 服务器:第 3 部分 — 添加提示

news2025/6/8 14:32:03

这是我们构建 MCP 服务器的四部分教程的第三部分。在第一部分中,我们使用基本资源创建了第一个MCP 服务器;在第二部分中,我们添加了资源模板并改进了代码组织。现在,我们将进一步重构代码并添加提示功能。

什么是 MCP 提示?

MCP 中的提示符是服务器提供的结构化模板,用于标准化与语言模型的交互。与提供数据的资源或执行操作的工具不同,提示符定义了可重用的消息序列和工作流,有助于以一致、可预测的方式指导 LLM 行为。它们可以接受参数来自定义交互,同时保持标准化的结构。如果您曾经研究过提示符工程,那么您可能对提示符的概念已经有了相当的了解。在 MCP 服务器中创建这些提示符,使我们能够为我们认为最有用的提示符创建一个空间,使其易于重用甚至共享。想象一下去餐厅,提示符就像一个菜单项,您可以从中挑选并提供给服务员。有时,您可以通过要求添加或删除某些菜品或以特定方式烹饪来自定义菜单项。以这种方式提供的提示符具有类似的功能。

为什么要使用提示?

提示有助于为 LLM 交互创建一致、可重复使用的模式。以下是一些实际示例:

代码审查提示

"name" -> code-review
Please review the following {{language}} code focusing on {{focusAreas}} for the following block of code:
```{{language}}
{{codeBlock}}
```

用户:请检查以下关注安全性和性能的 Python 代码:
“Python
...代码

数据分析提示

"name" -> analyze-sales-data
Analyze {{timeframe}} sales data focusing on {{metrics}}User: Analyze Q1 sales data focusing on revenue and growth

内容生成提示

"name" -> generate-email
Generate a {{tone}} {{type}} email for {{context}}

用户:生成一封正式的支持电子邮件,以向 Bob's Barbecue LLC 提出退款请求。

代码组织

在第二部分中,我们从 index.ts 中抽象出了处理程序代码,并将其放入 handlers.ts 文件中。这个文件可能会变得过大。我们应该将处理程序代码组织到各个模块中:

// src/resources.ts
export const resources = [
  {
    uri: "hello://world",
    name: "Hello World Message",
    description: "A simple greeting message",
    mimeType: "text/plain",
  },
];

export const resourceHandlers = {
  "hello://world": () => ({
    contents: [
      {
        uri: "hello://world",
        text: "Hello, World! This is my first MCP resource.",
      },
    ],
  }),
};

// src/resource-templates.ts
export const resourceTemplates = [
  {
    uriTemplate: "greetings://{name}",
    name: "Personal Greeting",
    description: "A personalized greeting message",
    mimeType: "text/plain",
  },
];

const greetingExp = /^greetings:\/\/(.+)$/;
const greetingMatchHandler =
  (uri: string, matchText: RegExpMatchArray) => () => {
    const name = decodeURIComponent(matchText[1]);
    return {
      contents: [
        {
          uri,
          text: `Hello, ${name}! Welcome to MCP.`,
        },
      ],
    };
  };
export const getResourceTemplate = (uri: string) => {
  const greetingMatch = uri.match(greetingExp);
  if (greetingMatch) return greetingMatchHandler(uri, greetingMatch);
};

更新我们的处理程序:

// src/handlers.ts
import {
  ListResourcesRequestSchema,
  ListResourceTemplatesRequestSchema,
  ReadResourceRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { type Server } from "@modelcontextprotocol/sdk/server/index.js";
import { resourceHandlers, resources } from "./resources.js";
import {
  getResourceTemplate,
  resourceTemplates,
} from "./resource-templates.js";

export const setupHandlers = (server: Server): void => {
  // List available resources when clients request them
  server.setRequestHandler(
    ListResourcesRequestSchema,
    () => ({ resources }),
  );
  // Resource Templates
  server.setRequestHandler(ListResourceTemplatesRequestSchema, () => ({
    resourceTemplates,
  }));
  // Return resource content when clients request it
  server.setRequestHandler(ReadResourceRequestSchema, (request) => {
    const { uri } = request.params ?? {};
    const resourceHandler =
      resourceHandlers[uri as keyof typeof resourceHandlers];
    if (resourceHandler) return resourceHandler();
    const resourceTemplateHandler = getResourceTemplate(uri);
    if (resourceTemplateHandler) return resourceTemplateHandler();
    throw new Error("Resource not found");
  });
};

添加提示

现在添加我们的新提示功能:

// src/prompts.ts
export const prompts = {
  "create-greeting": {
    name: "create-greeting",
    description: "Generate a customized greeting message",
    arguments: [
        { 
            name: "name",
            description: "Name of the person to greet",
            required: true,
        },
        {
            name: "style",
            description: "The style of greeting, such a formal, excited, or casual. If not specified casual will be used"
        }
    ],
  },
};

export const promptHandlers = {
  "create-greeting": ({ name, style = "casual" }: { name: string, style?: string }) => {
    return {
      messages: [
        {
          role: "user",
          content: {
            type: "text",
            text: `Please generate a greeting in ${style} style to ${name}.`,
          },
        },
      ],
    };
  },
};

将我们的新提示处理程序添加到处理程序文件中:

// src/handlers.ts
import {
  GetPromptRequestSchema,
  ListPromptsRequestSchema,
  // ... other imports
} from "@modelcontextprotocol/sdk/types.js";
// ... other imports
import { promptHandlers, prompts } from "./prompts.js";

export const setupHandlers = (server: Server): void => {
 
  // ... Other resource handlers here

  // Prompts
  server.setRequestHandler(ListPromptsRequestSchema, () => ({
    prompts: Object.values(prompts),
  }));
  server.setRequestHandler(GetPromptRequestSchema, (request) => {
    const { name, arguments: args } = request.params;
    const promptHandler = promptHandlers[name as keyof typeof promptHandlers];
    if (promptHandler) return promptHandler(args as { name: string, style?: string });
    throw new Error("Prompt not found");
  });
};

最后,我们需要更新服务器初始化:

// 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: {
      prompts: {}, // <-- Add prompts
      resources: {},
    },
  },
);

setupHandlers(server);

// ... remaining code

理解代码

模块组织

  • 资源和模板已放置在各自的模块中
  • 提示清晰地分开
  • 处理程序现在充当路由层

提示结构

  • 每个提示都有名称、描述和参数(如果需要)
  • 参数描述提示的预期输入
  • 处理程序生成结构化消息以提示目标 AI

消息序列

  • 提示返回消息数组
  • 消息具有角色(“用户”或“助手”)
  • 内容可以包括多步骤工作流的初始请求和后续响应(请注意,目前多步骤工作流的支持有限)

使用检查器进行测试

启动检查器:

npx @modelcontextprotocol/inspector node build/index.js

测试提示:

  • 点击“提示”选项卡
  • 找到“create-greeting”
  • 尝试不同的参数组合:
名字:“爱丽丝”,风格:“兴奋”
{
  "messages": [
    {
      "role": "user",
      "content": {
        "type": "text",
        "text": "Please generate a greeting in excited style to Alice."
      }
    }
  ]
}

使用 Claude Desktop 进行测试

尝试以下示例:

基本提示:

1:打开Claude桌面
假设:

  • 您已经构建了服务器(npx tsc)并设置了 Claude Desktop来使用它。

2:与我们添加资源的方式类似,点击“从 MCP 附加”

3:在模态弹出窗口中,点击“选择并集成”,然后从“hello-mcp”下的列表中选择“create-greeting”提示

4:现在,只需输入姓名即可进行测试。在姓名字段中输入类似“John”的内容,然后点击“提交”。

5:您将看到一个“create-greeting”附件。点击它查看其中的内容。

6:您将看到这里有一个给克劳德的提示,上面写着“请向约翰致以随意的问候”。

7:无需输入任何其他提示,只需单击聊天框右上角的提交箭头即可

8:您将看到类似“嗨,约翰!你今天过得怎么样?”的回复。

样式提示:

1:现在,尝试使用不同的特定样式创建问候语。打开“从 MCP 附加”对话框,然后再次选择“创建问候语”提示。这次,我们可以添加名称“Alice”和样式“正式”,然后提交聊天。再次使用箭头键,或者直接按 Enter 键也可以,我还没试过。

2:这一次,您可能会看到返回如下消息:

亲爱的爱丽丝,

祝您一切安好。谨致以最诚挚的问候。

谨致问候,
克劳德

下一步是什么?

在第 4 部分中,我们将:

  • 了解MCP 工具及其与提示的区别
  • 为我们的服务器添加工具功能
  • 了解工具如何提供动态功能
  • 使用所有主要的 MCP 功能完成我们的问候服务器

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

  • Prompts - Model Context Protocol
  • GitHub - amidabuddha/unichat-mcp-server
  • Prompt engineering overview - Anthropic
  • 10 Prompt Engineering Best Practices - DEV Community
  • https://promptingguide.ai

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

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

相关文章

基于React + FastAPI + LangChain + 通义千问的智能医疗问答系统

&#x1f4cc; 文章摘要&#xff1a; 本文详细介绍了如何在前端通过 Fetch 实现与 FastAPI 后端的 流式响应通信&#xff0c;并支持图文多模态数据上传。通过构建 multipart/form-data 请求&#xff0c;配合 ReadableStream 实时读取 AI 回复内容&#xff0c;实现类似 ChatGPT…

C# 中替换多层级数据的 Id 和 ParentId,保持主从或父子关系不变

在C#中替换多层级数据的Id和ParentId&#xff0c;同时保持父子关系不变&#xff0c;可以通过以下步骤实现&#xff1a; 创建旧Id到新Id的映射&#xff1a;遍历所有节点&#xff0c;为每个旧Id生成唯一的新Id&#xff0c;并存储在字典中。 替换节点的Id和ParentId&#xff1a;…

PG 分区表的缺陷

简介 好久没发文&#xff0c;是最近我实在不知道写点啥。随着国产化进程&#xff0c;很多 oracle 都在进行迁移&#xff0c;最近遇到了一个分区表迁移之后唯一性的问题。oracle 数据库中创建主键或者唯一索引&#xff0c;不需要引用分区键&#xff0c;但是 PG 就不行&#xff…

从Copilot到Agent,AI Coding是如何进化的?

编程原本是一项具有一定门槛的技能&#xff0c;但借助 AI Coding 产品&#xff0c;新手也能写出可运行的代码&#xff0c;非专业人员如业务分析师、产品经理&#xff0c;也能在 AI 帮助下直接生成简单应用。 这一演变对软件产业产生了深远影响。当 AI 逐步参与代码生成、调试乃…

Qt(part 2)1、Qwindow(菜单栏,工具栏,状态栏),铆接部件,核心部件 ,2、添加资源文件 3、对话框

1、Qwindow tips&#xff1a;1&#xff0c;首先为什么创建出的对象基本都是指针形式&#xff0c;个人觉得是对象树的原因&#xff08;自动释放内存&#xff09;&#xff0c;指针来访问成员函数->的形式。2&#xff0c;菜单栏只能一个的&#xff0c;放窗口基本Set&#xff0c…

【图片识别Excel】批量提取图片中的文字,图片设置识别区域,识别后将文字提取并保存Excel表格,基于WPF和OCR识别的应用

应用场景 在办公自动化、文档处理、数据录入等场景中&#xff0c;经常需要从大量图片中提取文字信息。例如&#xff1a; 批量处理扫描的表单、合同、发票等文档从图片集中提取特定区域的文字数据将纸质资料快速转换为电子文本并整理归档 通过设置识别区域&#xff0c;可以精…

Qt/C++学习系列之Excel使用记录

Qt/C学习系列之Excel使用记录 前言The process was ended forcefully.解决方式断点查语句问题 总结 前言 在项目中解析条目达50多条&#xff0c;并且都需要将对应的结果进行显示。为了将结果显示的更加清晰&#xff0c;考虑采用QTableWidget进行表格设置&#xff0c;而在使用过…

跳转指令四维全解:从【call/jmp 】的时空法则到内存迷宫导航术

一、核心概念&#xff1a;代码世界的空间定位法则 在汇编世界里&#xff0c;我们可以把内存想象成一栋巨大的图书馆&#xff1a; CS&#xff08;代码段寄存器&#xff09; 楼层编号 IP&#xff08;指令指针&#xff09; 房间编号 当前执行位置 CS:IP&#xff08;如3楼201…

LabVIEW实时系统数据监控与本地存储

基于LabVIEW Real-Time 模块&#xff0c;面向工业自动化、嵌入式测控等场景&#xff0c;提供实时数据采集、监控与本地存储的完整实现路径。通过分层任务调度、TDMS 文件格式应用及跨平台兼容性设计&#xff0c;确保系统在实时性、可靠性与数据管理效率间达到平衡。文中以 Comp…

从 Revit 到 3DTiles:GISBox RVT 切片器如何让建筑图元在 Web 端展示

在GIS&#xff08;地理信息系统&#xff09;行业蓬勃发展的当下&#xff0c;数据处理与展示的效率和精准度成为关键。GISBox作为一款功能强大的一站式三维GIS数据编辑、转换、发布平台&#xff0c;凭借其独特的“RVT切片器”功能&#xff0c;在RVT图元处理方面也有着不俗的表现…

Appium+python自动化(十二)- Android UIAutomator

Android团队在4.1版本&#xff08;API 16&#xff09;中推出了一款全新的UI自动化测试工具UiAutomator&#xff0c;用来帮助开发人员更有效率的完成App的Debug工作&#xff0c;同时对于测试人员也是一大福音&#xff0c;为什么这么说呢&#xff1f; UiAutomator提供了以下两种…

QQ邮箱发送验证码(Springboot)

一、邮箱发送服务准备 在qq邮箱的设置中选择账号下开启服务。 开启时可能会有短信验证&#xff0c;开启后显示验证码之类的一串英文&#xff0c;复制保存起来&#xff0c;在配置文件中会使用到。 二、后端依赖及配置 依赖 在pom.yml文件中添加相关依赖&#xff0c;redis的…

【EF Core】 EF Core并发控制:乐观锁与悲观锁的应用

文章目录 前言一、并发的风险二、EF Core中的并发控制方式2.1 开放式并发&#xff08;乐观锁&#xff09;2.1.1 应用程序管理的属性并发令牌2.1.2 数据库生成的并发令牌 2.2 悲观锁 总结 前言 实际的生产环境中&#xff0c;我们经常能遇到数据库由多个应用程序同时使用。每个程…

Harmony核心:动态方法修补与.NET游戏Mod开发

一、Harmony的核心定位与设计哲学 Harmony是一个运行时动态方法修补库&#xff0c;专为修改已编译的.NET/Mono应用程序而设计&#xff0c;尤其适用于游戏Mod开发。其核心创新在于&#xff1a; 非破坏性修改&#xff1a;保留原始方法完整性&#xff0c;避免直接替换或覆盖。多…

【Java开发日记】说一说 SpringBoot 中 CommandLineRunner

目录 1、CommandLineRunner SpringBoot中CommandLineRunner的作用 简单例子 多个类实现CommandLineRunner接口执行顺序的保证 通过实现Ordered接口实现控制执行顺序 通过Order注解实现控制执行顺序 Order 作用 2、ApplicationRunner 3、传递参数 4、源码跟踪 run()方…

全面理解 Linux 内核性能问题:分类、实战与调优策略

在 Linux 系统&#xff08;特别是嵌入式或服务器环境&#xff09;中&#xff0c;性能问题往往错综复杂、表象多变。只有对常见性能问题进行系统归类、理解其症状与根源&#xff0c;才能有效定位和解决。本文将围绕八大类核心性能问题&#xff0c;结合实战示例&#xff0c;逐类分…

算法-多条件排序

1、数对排序的使用 pair<ll,ll> a[31];//cmp为比较规则 ll cmp(pair<ll,ll>a,pair<ll,ll>b){if(a.first!b.first)return a.first>b.first;else return a.second<b.second; }//按照比较规则进行排序 sort(a1,a31,cmp); 2、具体例题 输入样例&#xff1…

固定ip和非固定ip的区别是什么?如何固定ip地址

在互联网中&#xff0c;我们常会接触到固定IP和非固定IP的概念。它们究竟有何不同&#xff1f;如何固定IP地址&#xff1f;让我们一起来探究这个问题。 一、固定IP和非固定IP的区别是什么 固定IP&#xff08;静态IP&#xff09;和非固定IP&#xff08;动态IP&#xff09;是两种…

使用矩阵乘法+线段树解决区间历史和问题的一种通用解法

文章目录 前言P8868 [NOIP2022] 比赛CF1824DP9990/2020 ICPC EcFinal G 前言 一般解决普通的区间历史和&#xff0c;只需要定义辅助 c h s − t ⋅ a chs-t\cdot a chs−t⋅a&#xff0c; h s hs hs是历史和&#xff0c; a a a是区间和&#xff0c; t t t是时间戳&#xff0c…

如何从浏览器中导出网站证书

以导出 GitHub 证书为例&#xff0c;点击 小锁 点击 导出 注意&#xff1a;这里需要根据你想要证书格式手动加上后缀名&#xff0c;我的是加 .crt 双击文件打开