MCP工具开发套件

使用Resonate构建生产就绪的MCP工具,具备自动重试、状态管理和容错功能,适用于天气预测和发票处理等场景。

  • by resonatehq-examples
  • JavaScript
  • 本地部署
  • 更新于
在 GitHub 查看 resonatehq-examples/example-mcp-tools-ts

使用说明

用Resonate构建MCP工具

使用Resonate构建具有自动重试、状态管理和容错功能的生产就绪MCP(模型上下文协议)工具。

你将建造什么

此示例演示了两个真实世界的MCP工具:

  1. 天气预报工具 -通过自动重试和错误处理获取实时天气数据
  2. 发票处理 -使用人工在环审批工作流提交发票

这两个例子都展示了Resonate的持久执行保证如何使人工智能工具集成在没有复杂基础设施的情况下变得可靠。

为什么选择MCP工具?

自动检索
API调用在失败时自动重试,而不会丢失上下文。

状态持久性
长时间运行的操作(如等待人工批准)在重新启动时保持状态。

简单代码
编写常规异步函数。Resonate手柄经久耐用。

无基础设施
从本地开发开始,在不改变架构的情况下扩展到生产。

快速开始

安装

npm install

运行天气工具

npm run weather

使用Claude Desktop进行配置

添加 ~/Library/Application Support/Claude/claude_desktop_config.json:

{
  "mcpServers": {
    "resonate-weather": {
      "command": "npx",
      "args": [
        "tsx",
        "/absolute/path/to/example-mcp-tools-ts/src/weather-server.ts"
      ]
    }
  }
}

重启Claude Desktop,然后问:“旧金山的天气怎么样?”

运作原理

天气示例

天气工具从美国国家气象局API获取预报:

import { Resonate, Context } from '@resonatehq/sdk';

const resonate = new Resonate();
await resonate.start();

// Regular async function with automatic retry
async function fetchNWS(ctx: Context, url: string) {
  const response = await fetch(url, {
    headers: {
      'User-Agent': 'Resonate-MCP-Example',
      'Accept': 'application/geo+json'
    }
  });
  
  if (!response.ok) {
    throw new Error(`NWS API error: ${response.status}`);
  }
  
  return response.json();
}

// Register for durability
resonate.register('fetchNWS', fetchNWS);

// Use in your workflow
async function getForecast(ctx: Context, lat: number, lon: number) {
  // Durable API call - retries automatically on failure
  const pointsData = await ctx.run(fetchNWS, pointsUrl);
  const forecastData = await ctx.run(fetchNWS, forecastUrl);
  
  return formatForecast(forecastData);
}

resonate.register('getForecast', getForecast);

主要优势:

  • ctx.run() 使API调用持久-它们将在失败时重试
  • 如果您的进程崩溃,状态将被保留
  • 无需学习装饰器、任务队列或特殊模式

发票示例

发票工具演示了人在循环中的工作流程:

async function processInvoice(ctx: Context, invoice: Invoice) {
  // Submit for approval
  const submissionResult = await ctx.run(submitInvoice, invoice);
  
  // Wait for human decision (could be hours or days)
  const decision = await ctx.lfc(
    `invoice-approval-${invoice.id}`,
    (decision: string) => decision === 'approved' || decision === 'rejected'
  );
  
  if (decision === 'approved') {
    // Process payment
    const payment = await ctx.run(processPayment, invoice);
    return { status: 'paid', payment };
  }
  
  return { status: 'rejected' };
}

主要优势:

  • 流程可以无限期等待人工输入
  • 即使服务器重新启动,状态也会保持不变
  • 无轮询,无手动状态管理

项目结构

src/
├── weather-server.ts     # Weather forecast MCP tool
├── invoice-server.ts     # Invoice processing with human-in-the-loop
└── shared/
    └── types.ts          # Shared TypeScript types

演示的功能

✅ 持久的API调用 -具有指数回退的自动重试
✅ 错误处理 -API故障的优雅降级
✅ 状态持久性 -进程重启后仍能存活
✅ 循环中的人类 -无限期等待外部输入
✅ 类型安全 -完全支持TypeScript
✅ MCP集成 -适用于Claude Desktop和其他MCP客户端

运行示例

天气工具

npm run weather

问克劳德:“西雅图的天气预报是什么?”

发票工具

npm run invoice

询问克劳德:“向ACME公司提交150美元的发票INV-001”

然后批准/拒绝:

# In another terminal
curl -X POST http://localhost:3000/approve/INV-001
# or
curl -X POST http://localhost:3000/reject/INV-001

扩展示例

添加您自己的工具

  1. 在中创建新文件 src/ (例如。, my-tool-server.ts)
  2. 定义您的耐用功能:
async function myDurableOperation(ctx: Context, input: string) {
  const result = await ctx.run(externalAPI, input);
  return processResult(result);
}

resonate.register('myDurableOperation', myDurableOperation);
  1. 装入MCP服务器:
const server = new Server({
  name: 'my-tool',
  version: '1.0.0',
}, { capabilities: { tools: {} } });

server.setRequestHandler(CallToolRequestSchema, async (request) => {
  if (request.params.name === 'my_tool') {
    const result = await resonate.run(
      'my-op-1',
      myDurableOperation,
      request.params.arguments.input
    );
    return { content: [{ type: 'text', text: result }] };
  }
});
  1. 在中添加npm脚本 package.json:
{
  "scripts": {
    "my-tool": "tsx src/my-tool-server.ts"
  }
}

连接到真实服务

用真实的API调用替换模拟数据:

// Example: OpenWeatherMap instead of NWS
async function fetchWeather(ctx: Context, city: string) {
  const apiKey = process.env.OPENWEATHER_API_KEY;
  const url = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}`;
  
  const response = await fetch(url);
  return response.json();
}

resonate.register('fetchWeather', fetchWeather);

部署到生产

Resonate在本地和生产中的工作方式相同:

// Development (local)
const resonate = new Resonate();

// Production (with Resonate server)
const resonate = Resonate.remote({
  url: process.env.RESONATE_SERVER_URL
});

看 协调部署文档 生产模式。

了解更多

常见问题

我需要运行Resonate服务器吗?
不,你可以从 new Resonate() 为了地方发展。切换到 Resonate.remote() 当你需要分布式工人或生产耐久性时。

如果我的进程崩溃了怎么办?
Resonate保持执行状态。当您的流程重新启动时,工作流将从最后一个成功的步骤恢复。

重试是如何工作的?
ctx.run() 使用指数回退自动重试失败的操作。您可以使用选项自定义重试行为。

我可以将其与其他AI模型一起使用吗?
对!MCP工具适用于任何兼容MCP的客户端,而不仅仅是Claude。

贡献

发现错误或有改进?在以下网址打开问题或PR 共振质量示例.

许可证

Apache 2.0