mcp-http-webhook 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +16 -0
- package/.prettierrc.json +8 -0
- package/ARCHITECTURE.md +269 -0
- package/CONTRIBUTING.md +136 -0
- package/GETTING_STARTED.md +310 -0
- package/IMPLEMENTATION.md +294 -0
- package/LICENSE +21 -0
- package/MIGRATION_TO_SDK.md +263 -0
- package/README.md +496 -0
- package/SDK_INTEGRATION_COMPLETE.md +300 -0
- package/STANDARD_SUBSCRIPTIONS.md +268 -0
- package/STANDARD_SUBSCRIPTIONS_COMPLETE.md +309 -0
- package/SUMMARY.md +272 -0
- package/Spec.md +2778 -0
- package/dist/errors/index.d.ts +52 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +81 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol/ProtocolHandler.d.ts +37 -0
- package/dist/protocol/ProtocolHandler.d.ts.map +1 -0
- package/dist/protocol/ProtocolHandler.js +172 -0
- package/dist/protocol/ProtocolHandler.js.map +1 -0
- package/dist/server.d.ts +6 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +502 -0
- package/dist/server.js.map +1 -0
- package/dist/stores/InMemoryStore.d.ts +27 -0
- package/dist/stores/InMemoryStore.d.ts.map +1 -0
- package/dist/stores/InMemoryStore.js +73 -0
- package/dist/stores/InMemoryStore.js.map +1 -0
- package/dist/stores/RedisStore.d.ts +18 -0
- package/dist/stores/RedisStore.d.ts.map +1 -0
- package/dist/stores/RedisStore.js +45 -0
- package/dist/stores/RedisStore.js.map +1 -0
- package/dist/stores/index.d.ts +3 -0
- package/dist/stores/index.d.ts.map +1 -0
- package/dist/stores/index.js +9 -0
- package/dist/stores/index.js.map +1 -0
- package/dist/subscriptions/SubscriptionManager.d.ts +49 -0
- package/dist/subscriptions/SubscriptionManager.d.ts.map +1 -0
- package/dist/subscriptions/SubscriptionManager.js +181 -0
- package/dist/subscriptions/SubscriptionManager.js.map +1 -0
- package/dist/types/index.d.ts +271 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +16 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/index.d.ts +51 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +154 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/webhooks/WebhookManager.d.ts +27 -0
- package/dist/webhooks/WebhookManager.d.ts.map +1 -0
- package/dist/webhooks/WebhookManager.js +174 -0
- package/dist/webhooks/WebhookManager.js.map +1 -0
- package/examples/GITHUB_LIVE_EXAMPLE.md +308 -0
- package/examples/GITHUB_LIVE_SETUP.md +253 -0
- package/examples/QUICKSTART.md +130 -0
- package/examples/basic-setup.ts +142 -0
- package/examples/github-server-live.ts +690 -0
- package/examples/github-server.ts +223 -0
- package/examples/google-drive-server-live.ts +773 -0
- package/examples/start-github-live.sh +53 -0
- package/jest.config.js +20 -0
- package/package.json +58 -0
- package/src/errors/index.ts +81 -0
- package/src/index.ts +19 -0
- package/src/server.ts +595 -0
- package/src/stores/InMemoryStore.ts +87 -0
- package/src/stores/RedisStore.ts +51 -0
- package/src/stores/index.ts +2 -0
- package/src/subscriptions/SubscriptionManager.ts +240 -0
- package/src/types/index.ts +341 -0
- package/src/utils/index.ts +156 -0
- package/src/webhooks/WebhookManager.ts +230 -0
- package/test-sdk-integration.sh +157 -0
- package/tsconfig.json +21 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAE/C;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CAGpF;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAuBtF;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAOvE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,GAAG,EACZ,MAAM,EAAE,MAAM,EACd,SAAS,GAAE,QAAQ,GAAG,MAAiB,GACtC,MAAM,CAKR;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,GAAG,EACZ,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,SAAS,GAAE,QAAQ,GAAG,MAAiB,GACtC,OAAO,CAOT;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/C;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAE3E;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAU/C;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAQ1D;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAO/C;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAO/C"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.generateSubscriptionId = generateSubscriptionId;
|
|
7
|
+
exports.generateWebhookUrl = generateWebhookUrl;
|
|
8
|
+
exports.parseUriTemplate = parseUriTemplate;
|
|
9
|
+
exports.matchUriTemplate = matchUriTemplate;
|
|
10
|
+
exports.createHmacSignature = createHmacSignature;
|
|
11
|
+
exports.verifyHmacSignature = verifyHmacSignature;
|
|
12
|
+
exports.sleep = sleep;
|
|
13
|
+
exports.calculateBackoff = calculateBackoff;
|
|
14
|
+
exports.sanitizeUrl = sanitizeUrl;
|
|
15
|
+
exports.extractErrorMessage = extractErrorMessage;
|
|
16
|
+
exports.isValidUrl = isValidUrl;
|
|
17
|
+
exports.isHttpsUrl = isHttpsUrl;
|
|
18
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
19
|
+
const nanoid_1 = require("nanoid");
|
|
20
|
+
/**
|
|
21
|
+
* Generate a unique subscription ID
|
|
22
|
+
*/
|
|
23
|
+
function generateSubscriptionId() {
|
|
24
|
+
return `sub_${(0, nanoid_1.nanoid)(16)}`;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Generate webhook URL for third-party services
|
|
28
|
+
*/
|
|
29
|
+
function generateWebhookUrl(publicUrl, subscriptionId) {
|
|
30
|
+
const baseUrl = publicUrl.endsWith('/') ? publicUrl.slice(0, -1) : publicUrl;
|
|
31
|
+
return `${baseUrl}/webhooks/incoming/${subscriptionId}`;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Parse URI template
|
|
35
|
+
* Example: parseUriTemplate('github://repo/{owner}/{repo}', 'github://repo/octocat/hello')
|
|
36
|
+
* Returns: { owner: 'octocat', repo: 'hello' }
|
|
37
|
+
*/
|
|
38
|
+
function parseUriTemplate(template, uri) {
|
|
39
|
+
const templateParts = template.split('/');
|
|
40
|
+
const uriParts = uri.split('/');
|
|
41
|
+
if (templateParts.length !== uriParts.length) {
|
|
42
|
+
throw new Error(`URI ${uri} does not match template ${template}`);
|
|
43
|
+
}
|
|
44
|
+
const params = {};
|
|
45
|
+
for (let i = 0; i < templateParts.length; i++) {
|
|
46
|
+
const templatePart = templateParts[i];
|
|
47
|
+
const uriPart = uriParts[i];
|
|
48
|
+
if (templatePart.startsWith('{') && templatePart.endsWith('}')) {
|
|
49
|
+
const paramName = templatePart.slice(1, -1);
|
|
50
|
+
params[paramName] = uriPart;
|
|
51
|
+
}
|
|
52
|
+
else if (templatePart !== uriPart) {
|
|
53
|
+
throw new Error(`URI ${uri} does not match template ${template}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return params;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Match URI against template
|
|
60
|
+
*/
|
|
61
|
+
function matchUriTemplate(template, uri) {
|
|
62
|
+
try {
|
|
63
|
+
parseUriTemplate(template, uri);
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Create HMAC signature
|
|
72
|
+
*/
|
|
73
|
+
function createHmacSignature(payload, secret, algorithm = 'sha256') {
|
|
74
|
+
const hmac = crypto_1.default.createHmac(algorithm, secret);
|
|
75
|
+
const data = typeof payload === 'string' ? payload : JSON.stringify(payload);
|
|
76
|
+
hmac.update(data);
|
|
77
|
+
return `${algorithm}=${hmac.digest('hex')}`;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Verify HMAC signature (timing-safe comparison)
|
|
81
|
+
*/
|
|
82
|
+
function verifyHmacSignature(payload, signature, secret, algorithm = 'sha256') {
|
|
83
|
+
try {
|
|
84
|
+
const expected = createHmacSignature(payload, secret, algorithm);
|
|
85
|
+
return crypto_1.default.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Sleep utility for retry delays
|
|
93
|
+
*/
|
|
94
|
+
function sleep(ms) {
|
|
95
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Exponential backoff calculator
|
|
99
|
+
*/
|
|
100
|
+
function calculateBackoff(attempt, baseDelay) {
|
|
101
|
+
return baseDelay * Math.pow(2, attempt);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Sanitize URL for logging (remove sensitive query params)
|
|
105
|
+
*/
|
|
106
|
+
function sanitizeUrl(url) {
|
|
107
|
+
try {
|
|
108
|
+
const urlObj = new URL(url);
|
|
109
|
+
urlObj.searchParams.delete('token');
|
|
110
|
+
urlObj.searchParams.delete('secret');
|
|
111
|
+
urlObj.searchParams.delete('api_key');
|
|
112
|
+
return urlObj.toString();
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
return url;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Extract error message safely
|
|
120
|
+
*/
|
|
121
|
+
function extractErrorMessage(error) {
|
|
122
|
+
if (error instanceof Error) {
|
|
123
|
+
return error.message;
|
|
124
|
+
}
|
|
125
|
+
if (typeof error === 'string') {
|
|
126
|
+
return error;
|
|
127
|
+
}
|
|
128
|
+
return 'Unknown error';
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Validate URL format
|
|
132
|
+
*/
|
|
133
|
+
function isValidUrl(url) {
|
|
134
|
+
try {
|
|
135
|
+
new URL(url);
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Validate HTTPS URL
|
|
144
|
+
*/
|
|
145
|
+
function isHttpsUrl(url) {
|
|
146
|
+
try {
|
|
147
|
+
const urlObj = new URL(url);
|
|
148
|
+
return urlObj.protocol === 'https:';
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { WebhookConfig, StoredSubscription, ResourceDefinition, WebhookChangeInfo, Logger } from '../types';
|
|
2
|
+
export declare class WebhookManager {
|
|
3
|
+
private config;
|
|
4
|
+
private resources;
|
|
5
|
+
private logger?;
|
|
6
|
+
private readonly timeout;
|
|
7
|
+
private readonly retries;
|
|
8
|
+
private readonly retryDelay;
|
|
9
|
+
constructor(config: WebhookConfig | undefined, resources: ResourceDefinition[], logger?: Logger | undefined);
|
|
10
|
+
/**
|
|
11
|
+
* Process incoming webhook from third-party service
|
|
12
|
+
*/
|
|
13
|
+
processIncomingWebhook(subscriptionId: string, payload: any, headers: Record<string, string>, subscription: StoredSubscription): Promise<WebhookChangeInfo | null>;
|
|
14
|
+
/**
|
|
15
|
+
* Send notification to client webhook in standard MCP format
|
|
16
|
+
*/
|
|
17
|
+
notifyClient(subscription: StoredSubscription, changeInfo: WebhookChangeInfo): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Call webhook URL
|
|
20
|
+
*/
|
|
21
|
+
private callWebhook;
|
|
22
|
+
/**
|
|
23
|
+
* Find resource by name
|
|
24
|
+
*/
|
|
25
|
+
private findResourceByName;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=WebhookManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WebhookManager.d.ts","sourceRoot":"","sources":["../../src/webhooks/WebhookManager.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,EACjB,MAAM,EACP,MAAM,UAAU,CAAC;AAIlB,qBAAa,cAAc;IAMvB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,MAAM,CAAC;IAPjB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;gBAG1B,MAAM,EAAE,aAAa,GAAG,SAAS,EACjC,SAAS,EAAE,kBAAkB,EAAE,EAC/B,MAAM,CAAC,EAAE,MAAM,YAAA;IAOzB;;OAEG;IACG,sBAAsB,CAC1B,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,GAAG,EACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,YAAY,EAAE,kBAAkB,GAC/B,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IAgDpC;;OAEG;IACG,YAAY,CAChB,YAAY,EAAE,kBAAkB,EAChC,UAAU,EAAE,iBAAiB,GAC5B,OAAO,CAAC,IAAI,CAAC;IAsFhB;;OAEG;YACW,WAAW;IA+CzB;;OAEG;IACH,OAAO,CAAC,kBAAkB;CAG3B"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WebhookManager = void 0;
|
|
4
|
+
const errors_1 = require("../errors");
|
|
5
|
+
const utils_1 = require("../utils");
|
|
6
|
+
class WebhookManager {
|
|
7
|
+
constructor(config, resources, logger) {
|
|
8
|
+
this.config = config;
|
|
9
|
+
this.resources = resources;
|
|
10
|
+
this.logger = logger;
|
|
11
|
+
this.timeout = config?.outgoing?.timeout ?? 5000;
|
|
12
|
+
this.retries = config?.outgoing?.retries ?? 3;
|
|
13
|
+
this.retryDelay = config?.outgoing?.retryDelay ?? 1000;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Process incoming webhook from third-party service
|
|
17
|
+
*/
|
|
18
|
+
async processIncomingWebhook(subscriptionId, payload, headers, subscription) {
|
|
19
|
+
this.logger?.debug('Processing incoming webhook', {
|
|
20
|
+
subscriptionId,
|
|
21
|
+
resourceType: subscription.resourceType,
|
|
22
|
+
});
|
|
23
|
+
// Find resource definition
|
|
24
|
+
const resource = this.findResourceByName(subscription.resourceType);
|
|
25
|
+
if (!resource?.subscription) {
|
|
26
|
+
throw new errors_1.WebhookError('Resource subscription handler not found');
|
|
27
|
+
}
|
|
28
|
+
// Verify signature if configured
|
|
29
|
+
if (this.config?.verifyIncomingSignature) {
|
|
30
|
+
const signature = headers['x-hub-signature-256'] || headers['x-signature'];
|
|
31
|
+
if (signature && this.config.incomingSecret) {
|
|
32
|
+
const isValid = this.config.verifyIncomingSignature(payload, signature, this.config.incomingSecret);
|
|
33
|
+
if (!isValid) {
|
|
34
|
+
this.logger?.warn('Invalid webhook signature', { subscriptionId });
|
|
35
|
+
throw new errors_1.WebhookError('Invalid webhook signature');
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
// Call resource's onWebhook handler
|
|
41
|
+
const changeInfo = await resource.subscription.onWebhook(subscriptionId, payload, headers);
|
|
42
|
+
this.logger?.debug('Webhook processed', {
|
|
43
|
+
subscriptionId,
|
|
44
|
+
changeType: changeInfo?.changeType,
|
|
45
|
+
});
|
|
46
|
+
return changeInfo;
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
this.logger?.error('Failed to process webhook', {
|
|
50
|
+
subscriptionId,
|
|
51
|
+
error: (0, utils_1.extractErrorMessage)(error),
|
|
52
|
+
});
|
|
53
|
+
throw new errors_1.WebhookError('Failed to process webhook', { cause: error });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Send notification to client webhook in standard MCP format
|
|
58
|
+
*/
|
|
59
|
+
async notifyClient(subscription, changeInfo) {
|
|
60
|
+
// Standard MCP notification format
|
|
61
|
+
const payload = {
|
|
62
|
+
jsonrpc: '2.0',
|
|
63
|
+
method: 'notifications/resources/updated',
|
|
64
|
+
params: {
|
|
65
|
+
uri: changeInfo.resourceUri,
|
|
66
|
+
title: changeInfo.data?.title,
|
|
67
|
+
_meta: {
|
|
68
|
+
changeType: changeInfo.changeType,
|
|
69
|
+
subscriptionId: subscription.uri,
|
|
70
|
+
timestamp: new Date().toISOString(),
|
|
71
|
+
...changeInfo.data
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
this.logger?.info('Notifying client (MCP format)', {
|
|
76
|
+
url: subscription.clientCallbackUrl,
|
|
77
|
+
uri: changeInfo.resourceUri,
|
|
78
|
+
changeType: changeInfo.changeType,
|
|
79
|
+
});
|
|
80
|
+
// Sign payload if secret is provided
|
|
81
|
+
let signature;
|
|
82
|
+
if (subscription.clientCallbackSecret && this.config?.outgoing?.signPayload) {
|
|
83
|
+
signature = this.config.outgoing.signPayload(payload, subscription.clientCallbackSecret);
|
|
84
|
+
}
|
|
85
|
+
// Call before hook
|
|
86
|
+
this.config?.outgoing?.onBeforeCall?.(subscription.clientCallbackUrl, payload);
|
|
87
|
+
// Attempt delivery with retries
|
|
88
|
+
let lastError;
|
|
89
|
+
for (let attempt = 0; attempt < this.retries; attempt++) {
|
|
90
|
+
try {
|
|
91
|
+
const response = await this.callWebhook(subscription.clientCallbackUrl, payload, signature);
|
|
92
|
+
// Call after hook
|
|
93
|
+
this.config?.outgoing?.onAfterCall?.(subscription.clientCallbackUrl, response);
|
|
94
|
+
this.logger?.info('Client notified successfully (MCP format)', {
|
|
95
|
+
url: subscription.clientCallbackUrl,
|
|
96
|
+
attempt: attempt + 1,
|
|
97
|
+
});
|
|
98
|
+
return; // Success
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
102
|
+
this.logger?.warn('Webhook delivery failed', {
|
|
103
|
+
url: subscription.clientCallbackUrl,
|
|
104
|
+
attempt: attempt + 1,
|
|
105
|
+
error: (0, utils_1.extractErrorMessage)(error),
|
|
106
|
+
});
|
|
107
|
+
// Don't retry on client errors (4xx)
|
|
108
|
+
if (error instanceof Error && 'status' in error) {
|
|
109
|
+
const status = error.status;
|
|
110
|
+
if (status >= 400 && status < 500) {
|
|
111
|
+
throw new errors_1.WebhookError('Client error, not retrying', { status });
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// Wait before retry (exponential backoff)
|
|
115
|
+
if (attempt < this.retries - 1) {
|
|
116
|
+
const delay = (0, utils_1.calculateBackoff)(attempt, this.retryDelay);
|
|
117
|
+
await (0, utils_1.sleep)(delay);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// All retries failed
|
|
122
|
+
this.config?.outgoing?.onAfterCall?.(subscription.clientCallbackUrl, null, lastError);
|
|
123
|
+
throw new errors_1.WebhookError('Failed to notify client after all retries', {
|
|
124
|
+
attempts: this.retries,
|
|
125
|
+
lastError: (0, utils_1.extractErrorMessage)(lastError),
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Call webhook URL
|
|
130
|
+
*/
|
|
131
|
+
async callWebhook(url, payload, signature) {
|
|
132
|
+
const controller = new AbortController();
|
|
133
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
134
|
+
try {
|
|
135
|
+
const headers = {
|
|
136
|
+
'Content-Type': 'application/json',
|
|
137
|
+
};
|
|
138
|
+
if (signature) {
|
|
139
|
+
headers['X-MCP-Signature'] = signature;
|
|
140
|
+
}
|
|
141
|
+
const response = await fetch(url, {
|
|
142
|
+
method: 'POST',
|
|
143
|
+
headers,
|
|
144
|
+
body: JSON.stringify(payload),
|
|
145
|
+
signal: controller.signal,
|
|
146
|
+
});
|
|
147
|
+
clearTimeout(timeoutId);
|
|
148
|
+
if (!response.ok) {
|
|
149
|
+
const error = new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
150
|
+
error.status = response.status;
|
|
151
|
+
throw error;
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
ok: true,
|
|
155
|
+
status: response.status,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
clearTimeout(timeoutId);
|
|
160
|
+
if (error instanceof Error && error.name === 'AbortError') {
|
|
161
|
+
throw new Error(`Request timeout after ${this.timeout}ms`);
|
|
162
|
+
}
|
|
163
|
+
throw error;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Find resource by name
|
|
168
|
+
*/
|
|
169
|
+
findResourceByName(name) {
|
|
170
|
+
return this.resources.find((r) => r.name === name);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
exports.WebhookManager = WebhookManager;
|
|
174
|
+
//# sourceMappingURL=WebhookManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WebhookManager.js","sourceRoot":"","sources":["../../src/webhooks/WebhookManager.ts"],"names":[],"mappings":"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"}
|