|
|
--- |
|
|
title: WebHooks API |
|
|
description: 配置和管理 WebHook 事件通知的完整指南 |
|
|
--- |
|
|
|
|
|
|
|
|
|
|
|
WebHooks 允许 MCPHub 在特定事件发生时向您的应用程序发送实时通知。 |
|
|
|
|
|
|
|
|
|
|
|
MCPHub WebHooks 系统支持以下功能: |
|
|
|
|
|
- 实时事件通知 |
|
|
- 自定义过滤器 |
|
|
- 重试机制 |
|
|
- 签名验证 |
|
|
- 批量事件处理 |
|
|
|
|
|
|
|
|
|
|
|
| 事件类型 | 描述 | |
|
|
| ----------------------- | -------------- | |
|
|
| `server.created` | MCP 服务器创建 | |
|
|
| `server.updated` | MCP 服务器更新 | |
|
|
| `server.deleted` | MCP 服务器删除 | |
|
|
| `server.status_changed` | 服务器状态变更 | |
|
|
| `group.created` | 服务器组创建 | |
|
|
| `group.updated` | 服务器组更新 | |
|
|
| `group.deleted` | 服务器组删除 | |
|
|
| `user.login` | 用户登录 | |
|
|
| `user.logout` | 用户登出 | |
|
|
| `config.changed` | 配置变更 | |
|
|
| `system.error` | 系统错误 | |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```http |
|
|
POST /api/webhooks |
|
|
``` |
|
|
|
|
|
|
|
|
|
|
|
```json |
|
|
{ |
|
|
"url": "https://your-app.com/webhook", |
|
|
"events": ["server.created", "server.status_changed"], |
|
|
"secret": "your-webhook-secret", |
|
|
"active": true, |
|
|
"config": { |
|
|
"contentType": "application/json", |
|
|
"insecureSsl": false, |
|
|
"retryCount": 3, |
|
|
"timeout": 30 |
|
|
}, |
|
|
"filters": { |
|
|
"serverGroups": ["production", "staging"], |
|
|
"serverTypes": ["ai-assistant", "data-processor"] |
|
|
} |
|
|
} |
|
|
``` |
|
|
|
|
|
|
|
|
|
|
|
```json |
|
|
{ |
|
|
"success": true, |
|
|
"data": { |
|
|
"id": "webhook-123", |
|
|
"url": "https://your-app.com/webhook", |
|
|
"events": ["server.created", "server.status_changed"], |
|
|
"active": true, |
|
|
"secret": "your-webhook-secret", |
|
|
"config": { |
|
|
"contentType": "application/json", |
|
|
"insecureSsl": false, |
|
|
"retryCount": 3, |
|
|
"timeout": 30 |
|
|
}, |
|
|
"filters": { |
|
|
"serverGroups": ["production", "staging"], |
|
|
"serverTypes": ["ai-assistant", "data-processor"] |
|
|
}, |
|
|
"createdAt": "2024-01-15T10:30:00Z", |
|
|
"updatedAt": "2024-01-15T10:30:00Z" |
|
|
} |
|
|
} |
|
|
``` |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```http |
|
|
GET /api/webhooks |
|
|
``` |
|
|
|
|
|
|
|
|
|
|
|
| 参数名 | 类型 | 描述 | |
|
|
| ------ | ------- | -------------------- | |
|
|
| page | integer | 页码(默认:1) | |
|
|
| limit | integer | 每页数量(默认:20) | |
|
|
| active | boolean | 过滤活跃状态 | |
|
|
| event | string | 过滤事件类型 | |
|
|
|
|
|
|
|
|
|
|
|
```bash |
|
|
curl -X GET \ |
|
|
'https://api.mcphub.io/api/webhooks?active=true&limit=10' \ |
|
|
-H 'Authorization: Bearer YOUR_API_TOKEN' |
|
|
``` |
|
|
|
|
|
|
|
|
|
|
|
```json |
|
|
{ |
|
|
"success": true, |
|
|
"data": { |
|
|
"webhooks": [ |
|
|
{ |
|
|
"id": "webhook-123", |
|
|
"url": "https://your-app.com/webhook", |
|
|
"events": ["server.created", "server.status_changed"], |
|
|
"active": true, |
|
|
"lastDelivery": "2024-01-15T09:30:00Z", |
|
|
"deliveryCount": 145, |
|
|
"failureCount": 2, |
|
|
"createdAt": "2024-01-10T10:30:00Z" |
|
|
} |
|
|
], |
|
|
"pagination": { |
|
|
"page": 1, |
|
|
"limit": 10, |
|
|
"total": 25, |
|
|
"pages": 3 |
|
|
} |
|
|
} |
|
|
} |
|
|
``` |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```http |
|
|
GET /api/webhooks/{id} |
|
|
``` |
|
|
|
|
|
|
|
|
|
|
|
```json |
|
|
{ |
|
|
"success": true, |
|
|
"data": { |
|
|
"id": "webhook-123", |
|
|
"url": "https://your-app.com/webhook", |
|
|
"events": ["server.created", "server.status_changed"], |
|
|
"active": true, |
|
|
"secret": "your-webhook-secret", |
|
|
"config": { |
|
|
"contentType": "application/json", |
|
|
"insecureSsl": false, |
|
|
"retryCount": 3, |
|
|
"timeout": 30 |
|
|
}, |
|
|
"filters": { |
|
|
"serverGroups": ["production", "staging"], |
|
|
"serverTypes": ["ai-assistant", "data-processor"] |
|
|
}, |
|
|
"stats": { |
|
|
"totalDeliveries": 145, |
|
|
"successfulDeliveries": 143, |
|
|
"failedDeliveries": 2, |
|
|
"lastDelivery": "2024-01-15T09:30:00Z", |
|
|
"lastSuccess": "2024-01-15T09:30:00Z", |
|
|
"lastFailure": "2024-01-14T15:20:00Z" |
|
|
}, |
|
|
"createdAt": "2024-01-10T10:30:00Z", |
|
|
"updatedAt": "2024-01-15T10:30:00Z" |
|
|
} |
|
|
} |
|
|
``` |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```http |
|
|
PUT /api/webhooks/{id} |
|
|
``` |
|
|
|
|
|
|
|
|
|
|
|
```json |
|
|
{ |
|
|
"url": "https://your-app.com/new-webhook", |
|
|
"events": ["server.created", "server.updated", "server.deleted"], |
|
|
"active": true, |
|
|
"config": { |
|
|
"retryCount": 5, |
|
|
"timeout": 45 |
|
|
} |
|
|
} |
|
|
``` |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```http |
|
|
DELETE /api/webhooks/{id} |
|
|
``` |
|
|
|
|
|
|
|
|
|
|
|
```json |
|
|
{ |
|
|
"success": true, |
|
|
"message": "WebHook 已成功删除" |
|
|
} |
|
|
``` |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
所有 WebHook 事件都遵循以下基本结构: |
|
|
|
|
|
```json |
|
|
{ |
|
|
"id": "event-123", |
|
|
"type": "server.created", |
|
|
"timestamp": "2024-01-15T10:30:00Z", |
|
|
"version": "1.0", |
|
|
"data": { |
|
|
// 事件特定数据 |
|
|
}, |
|
|
"metadata": { |
|
|
"source": "mcphub", |
|
|
"environment": "production", |
|
|
"triggeredBy": "user-456" |
|
|
} |
|
|
} |
|
|
``` |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```json |
|
|
{ |
|
|
"id": "event-123", |
|
|
"type": "server.created", |
|
|
"timestamp": "2024-01-15T10:30:00Z", |
|
|
"version": "1.0", |
|
|
"data": { |
|
|
"server": { |
|
|
"id": "mcp-server-123", |
|
|
"name": "AI Assistant Server", |
|
|
"type": "ai-assistant", |
|
|
"endpoint": "https://ai-assistant.example.com", |
|
|
"group": "production", |
|
|
"status": "active", |
|
|
"capabilities": ["chat", "completion"], |
|
|
"createdAt": "2024-01-15T10:30:00Z" |
|
|
} |
|
|
}, |
|
|
"metadata": { |
|
|
"source": "mcphub", |
|
|
"environment": "production", |
|
|
"triggeredBy": "user-456" |
|
|
} |
|
|
} |
|
|
``` |
|
|
|
|
|
|
|
|
|
|
|
```json |
|
|
{ |
|
|
"id": "event-124", |
|
|
"type": "server.status_changed", |
|
|
"timestamp": "2024-01-15T11:30:00Z", |
|
|
"version": "1.0", |
|
|
"data": { |
|
|
"server": { |
|
|
"id": "mcp-server-123", |
|
|
"name": "AI Assistant Server", |
|
|
"previousStatus": "active", |
|
|
"currentStatus": "inactive", |
|
|
"reason": "Health check failed", |
|
|
"lastHealthCheck": "2024-01-15T11:25:00Z" |
|
|
} |
|
|
}, |
|
|
"metadata": { |
|
|
"source": "mcphub", |
|
|
"environment": "production", |
|
|
"triggeredBy": "system" |
|
|
} |
|
|
} |
|
|
``` |
|
|
|
|
|
|
|
|
|
|
|
MCPHub 使用 HMAC-SHA256 签名来验证 WebHook 的真实性。 |
|
|
|
|
|
|
|
|
|
|
|
签名在 `X-MCPHub-Signature-256` 头中发送: |
|
|
|
|
|
``` |
|
|
X-MCPHub-Signature-256: sha256=5757107ea39eca8e35d1e8... |
|
|
``` |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```javascript |
|
|
const crypto = require('crypto'); |
|
|
|
|
|
function verifySignature(payload, signature, secret) { |
|
|
const expectedSignature = crypto |
|
|
.createHmac('sha256', secret) |
|
|
.update(payload, 'utf8') |
|
|
.digest('hex'); |
|
|
|
|
|
const actualSignature = signature.replace('sha256=', ''); |
|
|
|
|
|
return crypto.timingSafeEqual( |
|
|
Buffer.from(expectedSignature, 'hex'), |
|
|
Buffer.from(actualSignature, 'hex'), |
|
|
); |
|
|
} |
|
|
|
|
|
// Express.js 中间件示例 |
|
|
app.use('/webhook', express.raw({ type: 'application/json' }), (req, res) => { |
|
|
const signature = req.headers['x-mcphub-signature-256']; |
|
|
const payload = req.body; |
|
|
|
|
|
if (!verifySignature(payload, signature, process.env.WEBHOOK_SECRET)) { |
|
|
return res.status(401).send('Unauthorized'); |
|
|
} |
|
|
|
|
|
// 处理 WebHook 事件 |
|
|
const event = JSON.parse(payload); |
|
|
console.log('收到事件:', event.type); |
|
|
|
|
|
res.status(200).send('OK'); |
|
|
}); |
|
|
``` |
|
|
|
|
|
|
|
|
|
|
|
```python |
|
|
import hmac |
|
|
import hashlib |
|
|
|
|
|
def verify_signature(payload, signature, secret): |
|
|
expected_signature = hmac.new( |
|
|
secret.encode('utf-8'), |
|
|
payload, |
|
|
hashlib.sha256 |
|
|
).hexdigest() |
|
|
|
|
|
actual_signature = signature.replace('sha256=', '') |
|
|
|
|
|
return hmac.compare_digest(expected_signature, actual_signature) |
|
|
|
|
|
|
|
|
from flask import Flask, request, jsonify |
|
|
import json |
|
|
|
|
|
app = Flask(__name__) |
|
|
|
|
|
@app.route('/webhook', methods=['POST']) |
|
|
def webhook(): |
|
|
signature = request.headers.get('X-MCPHub-Signature-256') |
|
|
payload = request.get_data() |
|
|
|
|
|
if not verify_signature(payload, signature, 'your-webhook-secret'): |
|
|
return jsonify({'error': 'Unauthorized'}), 401 |
|
|
|
|
|
event = json.loads(payload) |
|
|
print(f'收到事件: {event["type"]}') |
|
|
|
|
|
return jsonify({'status': 'success'}), 200 |
|
|
``` |
|
|
|
|
|
|
|
|
|
|
|
MCPHub 对失败的 WebHook 交付实施指数退避重试: |
|
|
|
|
|
- **重试次数**: 可配置(默认 3 次) |
|
|
- **重试间隔**: 2^n 秒(n 为重试次数) |
|
|
- **最大间隔**: 300 秒(5 分钟) |
|
|
- **超时设置**: 可配置(默认 30 秒) |
|
|
|
|
|
|
|
|
|
|
|
| 尝试次数 | 延迟时间 | |
|
|
| -------- | -------- | |
|
|
| 1 | 立即 | |
|
|
| 2 | 2 秒 | |
|
|
| 3 | 4 秒 | |
|
|
| 4 | 8 秒 | |
|
|
| 5 | 16 秒 | |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```http |
|
|
GET /api/webhooks/{id}/deliveries |
|
|
``` |
|
|
|
|
|
|
|
|
|
|
|
| 参数名 | 类型 | 描述 | |
|
|
| ---------- | ------- | ------------------------------------ | |
|
|
| page | integer | 页码 | |
|
|
| limit | integer | 每页数量 | |
|
|
| status | string | 过滤状态(success, failed, pending) | |
|
|
| event_type | string | 过滤事件类型 | |
|
|
|
|
|
|
|
|
|
|
|
```json |
|
|
{ |
|
|
"success": true, |
|
|
"data": { |
|
|
"deliveries": [ |
|
|
{ |
|
|
"id": "delivery-123", |
|
|
"eventId": "event-123", |
|
|
"eventType": "server.created", |
|
|
"url": "https://your-app.com/webhook", |
|
|
"status": "success", |
|
|
"responseCode": 200, |
|
|
"responseTime": 145, |
|
|
"attempts": 1, |
|
|
"deliveredAt": "2024-01-15T10:30:15Z", |
|
|
"nextRetry": null |
|
|
}, |
|
|
{ |
|
|
"id": "delivery-124", |
|
|
"eventId": "event-124", |
|
|
"eventType": "server.status_changed", |
|
|
"url": "https://your-app.com/webhook", |
|
|
"status": "failed", |
|
|
"responseCode": 500, |
|
|
"responseTime": 30000, |
|
|
"attempts": 3, |
|
|
"error": "Connection timeout", |
|
|
"deliveredAt": null, |
|
|
"nextRetry": "2024-01-15T11:45:00Z" |
|
|
} |
|
|
], |
|
|
"pagination": { |
|
|
"page": 1, |
|
|
"limit": 20, |
|
|
"total": 145, |
|
|
"pages": 8 |
|
|
} |
|
|
} |
|
|
} |
|
|
``` |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```http |
|
|
POST /api/webhooks/{id}/test |
|
|
``` |
|
|
|
|
|
|
|
|
|
|
|
```json |
|
|
{ |
|
|
"eventType": "server.created", |
|
|
"customData": { |
|
|
"test": true, |
|
|
"message": "这是一个测试事件" |
|
|
} |
|
|
} |
|
|
``` |
|
|
|
|
|
|
|
|
|
|
|
```json |
|
|
{ |
|
|
"success": true, |
|
|
"data": { |
|
|
"deliveryId": "delivery-test-123", |
|
|
"status": "delivered", |
|
|
"responseCode": 200, |
|
|
"responseTime": 124, |
|
|
"sentAt": "2024-01-15T10:30:00Z" |
|
|
} |
|
|
} |
|
|
``` |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
确保您的 WebHook 端点能够处理重复事件: |
|
|
|
|
|
```javascript |
|
|
const processedEvents = new Set(); |
|
|
|
|
|
app.post('/webhook', (req, res) => { |
|
|
const event = req.body; |
|
|
|
|
|
// 检查事件是否已处理 |
|
|
if (processedEvents.has(event.id)) { |
|
|
return res.status(200).send('Already processed'); |
|
|
} |
|
|
|
|
|
// 处理事件 |
|
|
processEvent(event); |
|
|
|
|
|
// 记录已处理的事件 |
|
|
processedEvents.add(event.id); |
|
|
|
|
|
res.status(200).send('OK'); |
|
|
}); |
|
|
``` |
|
|
|
|
|
|
|
|
|
|
|
对于复杂的处理逻辑,使用异步处理避免阻塞: |
|
|
|
|
|
```javascript |
|
|
app.post('/webhook', async (req, res) => { |
|
|
const event = req.body; |
|
|
|
|
|
// 立即响应 |
|
|
res.status(200).send('OK'); |
|
|
|
|
|
// 异步处理事件 |
|
|
setImmediate(() => { |
|
|
processEventAsync(event); |
|
|
}); |
|
|
}); |
|
|
``` |
|
|
|
|
|
|
|
|
|
|
|
实施适当的错误处理和日志记录: |
|
|
|
|
|
```javascript |
|
|
app.post('/webhook', (req, res) => { |
|
|
try { |
|
|
const event = req.body; |
|
|
processEvent(event); |
|
|
res.status(200).send('OK'); |
|
|
} catch (error) { |
|
|
console.error('WebHook 处理错误:', error); |
|
|
res.status(500).send('Internal Server Error'); |
|
|
} |
|
|
}); |
|
|
``` |
|
|
|
|
|
|
|
|
|
|
|
监控 WebHook 的交付状态: |
|
|
|
|
|
```bash |
|
|
|
|
|
curl -X GET \ |
|
|
'https://api.mcphub.io/api/webhooks/webhook-123/deliveries?status=failed' \ |
|
|
-H 'Authorization: Bearer YOUR_API_TOKEN' |
|
|
``` |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1. **签名验证失败** |
|
|
|
|
|
- 检查密钥是否正确 |
|
|
- 确保使用原始请求体进行验证 |
|
|
- 验证 HMAC 计算实现 |
|
|
|
|
|
2. **超时错误** |
|
|
|
|
|
- 增加 WebHook 超时设置 |
|
|
- 优化端点响应时间 |
|
|
- 使用异步处理 |
|
|
|
|
|
3. **重复事件** |
|
|
- 实施幂等性检查 |
|
|
- 使用事件 ID 去重 |
|
|
- 记录处理状态 |
|
|
|
|
|
|
|
|
|
|
|
使用 MCPHub 提供的调试工具: |
|
|
|
|
|
```bash |
|
|
|
|
|
curl -X GET \ |
|
|
'https://api.mcphub.io/api/webhooks/webhook-123/deliveries?limit=5' \ |
|
|
-H 'Authorization: Bearer YOUR_API_TOKEN' |
|
|
|
|
|
|
|
|
curl -X POST \ |
|
|
'https://api.mcphub.io/api/webhooks/delivery-124/redeliver' \ |
|
|
-H 'Authorization: Bearer YOUR_API_TOKEN' |
|
|
``` |
|
|
|