> ## Content Index
> Fetch the complete content index at: https://www.hubwiz.com/blog/llms.txt
> Use this file to discover other available public pages before exploring further.

# MCP服务器开发指南 (4)
- URL: https://www.hubwiz.com/blog/mcp-server-development-guide-4/
- Published: 2025-02-22T21:27:52.000Z
- Updated: 2025-02-22T21:27:52.000Z
- Description: 工具是可以调用以执行操作或检索动态信息的可执行函数。与只读的资源和结构化LLM交互的提示不同，工具允许LLM主动执行一些事情，如计算值、进行API调用或修改数据。
- Author: admin
- Tags: APPLICATION, MCP, #Import 2026-10-14 14:41

这是关于构建MCP服务器的四部分教程的最后一部分。在[第一部分](mcp-server-development-guide-1)中，我们创建了一个具有基本资源的MCP服务器。在[第二部分](mcp-server-development-guide-2)中，我们添加了资源模板并改进了代码组织，在[第三部分](mcp-server-development-guide-3)中，我们添加了提示并进一步优化了结构。现在我们将通过添加[工具](https://modelcontextprotocol.io/docs/concepts/tools?ref=hubwiz.com)来完成我们的服务器。

## 1、什么是MCP工具？

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

## 2、为什么要使用工具？

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

文件操作：

```
name: "write-file"  
arguments: {  
  path: "/logs/report.txt",  
  content: "每日总结..."  
}

```

用户：

> *将此报告保存到文件中*

AI：

> *我将使用write-file工具……文件已成功保存。*

API交互：

```
name: "fetch-weather"  
arguments: {  
  location: "旧金山",  
  units: "摄氏度"  
}

```

用户：

> *旧金山的天气如何？*

AI：

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

数据处理：

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

```

用户：

> *计算第一季度销售的汇总统计*

AI：

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

## 3、添加工具

为我们的新工具创建一个新文件，并添加一个“create-message”工具：

```
// src/tools.ts  
  
// 允许的值  
const messageTypes = ['greeting', 'farewell', 'thank-you'] as const;  
const tones = ['formal', 'casual', 'playful'] as const;  
  
// 工具定义  
export const tools = {  
  'create-message': {  
    name: 'create-message',  
    description: '生成带有各种选项的自定义消息',  
    inputSchema: {  
      type: 'object',  
      properties: {  
        messageType: {  
          type: 'string',  
          enum: messageTypes,  
          description: '要生成的消息类型',  
        },  
        recipient: {  
          type: 'string',  
          description: '消息的接收者姓名',  
        },  
        tone: {  
          type: 'string',  
          enum: tones,  
          description: '消息的语气',  
        },  
      },  
      required: ['messageType', 'recipient'],  
    },  
  },  
};

```

到目前为止，我们添加的只是一个描述，它将使使用它的模型理解其功能以及期望的信息类型。

现在让我们添加实际的处理器：

```
// src/tools.ts  
  
// 允许的值  
const messageTypes = ['greeting', 'farewell', 'thank-you'] as const;  
const tones = ['formal', 'casual', 'playful'] as const;  
  
// 工具定义  
export const tools = {  
  // ... 现有的定义  
};  
  
type CreateMessageArgs = {  
  messageType: typeof messageTypes[number];  
  recipient: string;  
  tone?: typeof tones[number];  
};  
  
// 各种消息组合的简单模板  
const messageFns = {  
  greeting: {  
    formal: (recipient: string) =>  
      `亲爱的${recipient}，希望这封信能让你一切安好`,  
    playful: (recipient: string) => `嘿嘿 ${recipient}！🎉 有什么新鲜事吗？`,  
    casual: (recipient: string) => `嗨 ${recipient}！你好吗？`,  
  },  
  farewell: {  
    formal: (recipient: string) =>  
      `祝好，${recipient}。期待再次相见。`,  
    playful: (recipient: string) =>  
      `再见啦，${recipient}！👋 保持精彩！`,  
    casual: (recipient: string) => `再见 ${recipient}，保重！`,  
  },  
  "thank-you": {  
    formal: (recipient: string) =>  
      `亲爱的${recipient}，我真诚地感谢你的帮助。`,  
    playful: (recipient: string) =>  
      `你是最棒的，${recipient}！🌟 谢谢你一百万次！`,  
    casual: (recipient: string) =>  
      `非常感谢你，${recipient}！真的很感激！`,  
  },  
};  
  
const createMessage = (args: CreateMessageArgs) => {  
  if (!args.messageType) throw new Error("必须提供消息类型。");  
  if (!args.recipient) throw new Error("必须提供接收者。");  
  
  const { messageType, recipient } = args;  
  const tone = args.tone || "casual";  
  if (!messageTypes.includes(messageType)) {  
    throw new Error(  
      `消息类型必须是以下之一：${messageTypes.join(", ")}`,  
    );  
  }  
  if (!tones.includes(tone)) {  
    throw new Error(  
      `如果提供了语气，则必须是以下之一：${  
        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, // <-- 添加这个  
  GetPromptRequestSchema,  
  ListPromptsRequestSchema,  
  ListResourcesRequestSchema,  
  ListResourceTemplatesRequestSchema,  
  ListToolsRequestSchema, // <-- 并添加这个  
  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 type { Server } from "@modelcontextprotocol/sdk/server/index.js";  
  
export const setupHandlers = (server: Server): void => {  
  // 当客户端请求时列出可用资源
  
  // ... 之前创建的处理器在这里
  
  // 工具  
  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("未找到工具");  
  
    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: {}, // <-- 添加工具能力  
    },  
  },  
);  
  
setupHandlers(server);  
  
// 使用stdio传输启动服务器  
const transport = new StdioServerTransport();  
await server.connect(transport);  
  
console.info(  
  '{"jsonrpc": "2.0", "method": "log", "params": { "message": "服务器运行中..." }}',  
);

```

工具结构：

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

错误处理：

- 必需参数验证
- 特定错误消息
- 类型安全的处理器访问

## 4、使用Inspector测试

记得先构建输出：

```
npm run build

```

然后启动Inspector：

```
npx @modelcontextprotocol/inspector node build/index.js

```

测试工具：

- 点击“工具”标签
- 查找“create-message”
- 尝试不同的组合：

```
{  
  "messageType": "thank-you",  
  "recipient": "Alice",  
  "tone": "playful"  
}

```

## 5、使用Claude桌面测试

尝试以下示例

*注意：您可能会收到授权工具使用的请求：*

基本消息：

用户：

> *为Bob创建一条问候消息*

Claude：

> *我将使用消息工具……‘嗨Bob！你好吗？’*

样式化消息：

用户：

> *给Alice发送一条有趣的感谢*

Claude：

> *使用消息工具……‘你是最棒的，Alice！🌟 谢谢你一百万次！’*

不同消息类型：

用户：

> *你能创建哪些类型的消息？*

Claude：

> *我可以帮助你使用create-message函数创建不同类型的消息。你可以生成：* 
>  
> *1\. 问候* 
> *2\. 再见* 
> *3\. 感谢消息* 
>  
> *对于每种类型，你可以指定接收者，并且可以设置语气为正式、随意或有趣。你想让我演示创建特定类型的消息吗？*

*您的结果可能与此不同，特别是如果您使用的是不同于Claude的不同工具或不同于Sonnet 3.5的不同模型*

## 6、结束语

恭喜！你现在构建了一个完整的MCP服务器，包括：

- 静态和基于模板的资源
- 可定制的提示
- 动态工具
- 结构良好、类型安全的代码

**你已经学会了如何：**

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

**从这里开始，你可以：**

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

请记住，MCP是一个不断发展的协议，因此请密切关注官方文档中的新功能和最佳实践。

---

原文链接：[Building MCP Servers: Part 4 — Creating Tools](https://medium.com/@cstroliadavis/building-mcp-servers-f9ce29814f1f?ref=hubwiz.com)

汇智网翻译整理，转载请标明出处