@centrali-io/centrali-mcp 5.2.0 → 5.4.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/README.md +15 -0
- package/dist/index.js +2 -0
- package/dist/tools/_register.d.ts +24 -0
- package/dist/tools/_register.js +44 -0
- package/dist/tools/auth-providers.js +15 -30
- package/dist/tools/compute.js +48 -69
- package/dist/tools/describe.js +174 -21
- package/dist/tools/insights.js +13 -36
- package/dist/tools/orchestrations.js +21 -44
- package/dist/tools/pages.js +39 -61
- package/dist/tools/records.js +29 -50
- package/dist/tools/search.js +3 -24
- package/dist/tools/service-accounts.js +93 -108
- package/dist/tools/smart-queries.js +15 -36
- package/dist/tools/structures.js +23 -61
- package/dist/tools/validation.js +11 -34
- package/dist/tools/webhook-subscriptions.d.ts +3 -0
- package/dist/tools/webhook-subscriptions.js +292 -0
- package/package.json +5 -6
- package/src/index.ts +2 -0
- package/src/tools/_register.ts +53 -0
- package/src/tools/auth-providers.ts +8 -24
- package/src/tools/compute.ts +25 -46
- package/src/tools/describe.ts +200 -21
- package/src/tools/insights.ts +19 -28
- package/src/tools/orchestrations.ts +11 -34
- package/src/tools/pages.ts +20 -41
- package/src/tools/records.ts +15 -36
- package/src/tools/search.ts +7 -22
- package/src/tools/service-accounts.ts +47 -63
- package/src/tools/smart-queries.ts +8 -29
- package/src/tools/structures.ts +12 -50
- package/src/tools/validation.ts +21 -27
- package/src/tools/webhook-subscriptions.ts +363 -0
package/src/tools/validation.ts
CHANGED
|
@@ -1,32 +1,17 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { CentraliSDK, ListValidationSuggestionsOptions } from "@centrali-io/centrali-sdk";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
+
import { registerTool, formatError } from "./_register.js";
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const e = error as Record<string, any>;
|
|
8
|
-
if ("message" in e) {
|
|
9
|
-
let msg = `Error ${context}`;
|
|
10
|
-
if ("code" in e || "status" in e) {
|
|
11
|
-
msg += `: [${e.code ?? e.status ?? "ERROR"}] ${e.message}`;
|
|
12
|
-
} else {
|
|
13
|
-
msg += `: ${e.message}`;
|
|
14
|
-
}
|
|
15
|
-
if (Array.isArray(e.fieldErrors) && e.fieldErrors.length > 0) {
|
|
16
|
-
msg +=
|
|
17
|
-
"\nField errors:\n" +
|
|
18
|
-
(e.fieldErrors as Array<{ field: string; message: string }>)
|
|
19
|
-
.map((f) => ` ${f.field}: ${f.message}`)
|
|
20
|
-
.join("\n");
|
|
21
|
-
}
|
|
22
|
-
return msg;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
return `Error ${context}: ${error instanceof Error ? error.message : String(error)}`;
|
|
26
|
-
}
|
|
6
|
+
type ValidationIssueType = "typo" | "format" | "duplicate" | "semantic" | "type";
|
|
7
|
+
type SuggestionStatus = "pending" | "accepted" | "rejected" | "auto-applied";
|
|
27
8
|
|
|
28
9
|
export function registerValidationTools(server: McpServer, sdk: CentraliSDK) {
|
|
29
|
-
|
|
10
|
+
registerTool<{
|
|
11
|
+
recordSlug: string;
|
|
12
|
+
validationTypes?: ValidationIssueType[];
|
|
13
|
+
}>(
|
|
14
|
+
server,
|
|
30
15
|
"trigger_validation_scan",
|
|
31
16
|
"Trigger an AI-powered data quality scan on a collection. Detects typos, format inconsistencies, duplicates, and other data issues. Returns a batchId — use get_validation_summary or list_validation_suggestions to see results after the scan completes.",
|
|
32
17
|
{
|
|
@@ -60,7 +45,13 @@ export function registerValidationTools(server: McpServer, sdk: CentraliSDK) {
|
|
|
60
45
|
}
|
|
61
46
|
);
|
|
62
47
|
|
|
63
|
-
|
|
48
|
+
registerTool<{
|
|
49
|
+
recordSlug?: string;
|
|
50
|
+
status?: SuggestionStatus;
|
|
51
|
+
issueType?: ValidationIssueType;
|
|
52
|
+
minConfidence?: number;
|
|
53
|
+
}>(
|
|
54
|
+
server,
|
|
64
55
|
"list_validation_suggestions",
|
|
65
56
|
"List data quality suggestions generated by validation scans. Each suggestion identifies an issue in a record and proposes a fix.",
|
|
66
57
|
{
|
|
@@ -111,7 +102,8 @@ export function registerValidationTools(server: McpServer, sdk: CentraliSDK) {
|
|
|
111
102
|
}
|
|
112
103
|
);
|
|
113
104
|
|
|
114
|
-
|
|
105
|
+
registerTool<{ recordSlug?: string }>(
|
|
106
|
+
server,
|
|
115
107
|
"get_validation_summary",
|
|
116
108
|
"Get a summary of data quality across the workspace — counts of pending, accepted, and rejected suggestions. Optionally filter by collection.",
|
|
117
109
|
{
|
|
@@ -139,7 +131,8 @@ export function registerValidationTools(server: McpServer, sdk: CentraliSDK) {
|
|
|
139
131
|
}
|
|
140
132
|
);
|
|
141
133
|
|
|
142
|
-
|
|
134
|
+
registerTool<{ suggestionId: string }>(
|
|
135
|
+
server,
|
|
143
136
|
"accept_validation_suggestion",
|
|
144
137
|
"Accept a data quality suggestion and apply the fix to the record. The suggested correction is written to the record automatically.",
|
|
145
138
|
{
|
|
@@ -165,7 +158,8 @@ export function registerValidationTools(server: McpServer, sdk: CentraliSDK) {
|
|
|
165
158
|
}
|
|
166
159
|
);
|
|
167
160
|
|
|
168
|
-
|
|
161
|
+
registerTool<{ suggestionId: string }>(
|
|
162
|
+
server,
|
|
169
163
|
"reject_validation_suggestion",
|
|
170
164
|
"Reject a data quality suggestion, marking it as incorrect or not applicable. The record is left unchanged.",
|
|
171
165
|
{
|
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { CentraliSDK } from "@centrali-io/centrali-sdk";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { registerTool, formatError } from "./_register.js";
|
|
5
|
+
|
|
6
|
+
const RECORD_EVENT_VALUES = [
|
|
7
|
+
"record_created",
|
|
8
|
+
"record_updated",
|
|
9
|
+
"record_deleted",
|
|
10
|
+
"records_bulk_created",
|
|
11
|
+
] as const;
|
|
12
|
+
|
|
13
|
+
const DELIVERY_STATUS_VALUES = ["success", "failed", "retrying"] as const;
|
|
14
|
+
|
|
15
|
+
type RecordEventValue = (typeof RECORD_EVENT_VALUES)[number];
|
|
16
|
+
type DeliveryStatusValue = (typeof DELIVERY_STATUS_VALUES)[number];
|
|
17
|
+
|
|
18
|
+
export function registerWebhookSubscriptionTools(server: McpServer, sdk: CentraliSDK) {
|
|
19
|
+
// ── Subscription CRUD ──────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
registerTool<Record<string, never>>(
|
|
22
|
+
server,
|
|
23
|
+
"list_webhook_subscriptions",
|
|
24
|
+
"List all webhook subscriptions in the workspace. Subscriptions deliver record events to an external URL, signed with HMAC-SHA256 under the subscription's `whsec_` secret.",
|
|
25
|
+
{},
|
|
26
|
+
async () => {
|
|
27
|
+
try {
|
|
28
|
+
const result = await sdk.webhookSubscriptions.list();
|
|
29
|
+
return {
|
|
30
|
+
content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }],
|
|
31
|
+
};
|
|
32
|
+
} catch (error: unknown) {
|
|
33
|
+
return {
|
|
34
|
+
content: [{ type: "text", text: formatError(error, "listing webhook subscriptions") }],
|
|
35
|
+
isError: true,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
registerTool<{ subscriptionId: string }>(
|
|
42
|
+
server,
|
|
43
|
+
"get_webhook_subscription",
|
|
44
|
+
"Get a single webhook subscription by ID. The signing secret is not returned on reads — use rotate_webhook_subscription_secret to regenerate it.",
|
|
45
|
+
{
|
|
46
|
+
subscriptionId: z.string().describe("The webhook subscription UUID"),
|
|
47
|
+
},
|
|
48
|
+
async ({ subscriptionId }) => {
|
|
49
|
+
try {
|
|
50
|
+
const result = await sdk.webhookSubscriptions.get(subscriptionId);
|
|
51
|
+
return {
|
|
52
|
+
content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }],
|
|
53
|
+
};
|
|
54
|
+
} catch (error: unknown) {
|
|
55
|
+
return {
|
|
56
|
+
content: [
|
|
57
|
+
{ type: "text", text: formatError(error, `getting webhook subscription '${subscriptionId}'`) },
|
|
58
|
+
],
|
|
59
|
+
isError: true,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
registerTool<{
|
|
66
|
+
name: string;
|
|
67
|
+
url: string;
|
|
68
|
+
events: RecordEventValue[];
|
|
69
|
+
recordSlugs?: string[];
|
|
70
|
+
active?: boolean;
|
|
71
|
+
}>(
|
|
72
|
+
server,
|
|
73
|
+
"create_webhook_subscription",
|
|
74
|
+
"Create a webhook subscription. The response includes the signing secret (whsec_...) — capture it immediately, it is not returned on subsequent reads.",
|
|
75
|
+
{
|
|
76
|
+
name: z.string().describe("Display name for the subscription"),
|
|
77
|
+
url: z.string().describe("HTTPS URL that will receive POSTed event payloads"),
|
|
78
|
+
events: z
|
|
79
|
+
.array(z.enum(RECORD_EVENT_VALUES))
|
|
80
|
+
.describe("Record events to subscribe to: record_created, record_updated, record_deleted, records_bulk_created"),
|
|
81
|
+
recordSlugs: z
|
|
82
|
+
.array(z.string())
|
|
83
|
+
.optional()
|
|
84
|
+
.describe("Optional list of collection record slugs to scope the subscription to. Omit for all collections."),
|
|
85
|
+
active: z
|
|
86
|
+
.boolean()
|
|
87
|
+
.optional()
|
|
88
|
+
.describe("Whether the subscription is active on creation. Defaults to true."),
|
|
89
|
+
},
|
|
90
|
+
async ({ name, url, events, recordSlugs, active }) => {
|
|
91
|
+
try {
|
|
92
|
+
const input: Record<string, any> = { name, url, events };
|
|
93
|
+
if (recordSlugs !== undefined) input.recordSlugs = recordSlugs;
|
|
94
|
+
if (active !== undefined) input.active = active;
|
|
95
|
+
|
|
96
|
+
const result = await sdk.webhookSubscriptions.create(input as any);
|
|
97
|
+
return {
|
|
98
|
+
content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }],
|
|
99
|
+
};
|
|
100
|
+
} catch (error: unknown) {
|
|
101
|
+
return {
|
|
102
|
+
content: [
|
|
103
|
+
{ type: "text", text: formatError(error, `creating webhook subscription '${name}'`) },
|
|
104
|
+
],
|
|
105
|
+
isError: true,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
registerTool<{
|
|
112
|
+
subscriptionId: string;
|
|
113
|
+
name?: string;
|
|
114
|
+
url?: string;
|
|
115
|
+
events?: RecordEventValue[];
|
|
116
|
+
recordSlugs?: string[];
|
|
117
|
+
active?: boolean;
|
|
118
|
+
}>(
|
|
119
|
+
server,
|
|
120
|
+
"update_webhook_subscription",
|
|
121
|
+
"Update fields on a webhook subscription. The signing secret is server-managed — use rotate_webhook_subscription_secret to change it.",
|
|
122
|
+
{
|
|
123
|
+
subscriptionId: z.string().describe("The webhook subscription UUID"),
|
|
124
|
+
name: z.string().optional().describe("Updated display name"),
|
|
125
|
+
url: z.string().optional().describe("Updated HTTPS URL"),
|
|
126
|
+
events: z
|
|
127
|
+
.array(z.enum(RECORD_EVENT_VALUES))
|
|
128
|
+
.optional()
|
|
129
|
+
.describe("Updated list of subscribed events"),
|
|
130
|
+
recordSlugs: z
|
|
131
|
+
.array(z.string())
|
|
132
|
+
.optional()
|
|
133
|
+
.describe("Updated collection scope. Pass [] to clear the restriction (subscribe to all collections). Omit to leave scope unchanged."),
|
|
134
|
+
active: z.boolean().optional().describe("Enable or disable the subscription"),
|
|
135
|
+
},
|
|
136
|
+
async ({ subscriptionId, name, url, events, recordSlugs, active }) => {
|
|
137
|
+
try {
|
|
138
|
+
const patch: Record<string, any> = {};
|
|
139
|
+
if (name !== undefined) patch.name = name;
|
|
140
|
+
if (url !== undefined) patch.url = url;
|
|
141
|
+
if (events !== undefined) patch.events = events;
|
|
142
|
+
if (recordSlugs !== undefined) patch.recordSlugs = recordSlugs;
|
|
143
|
+
if (active !== undefined) patch.active = active;
|
|
144
|
+
|
|
145
|
+
const result = await sdk.webhookSubscriptions.update(subscriptionId, patch);
|
|
146
|
+
return {
|
|
147
|
+
content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }],
|
|
148
|
+
};
|
|
149
|
+
} catch (error: unknown) {
|
|
150
|
+
return {
|
|
151
|
+
content: [
|
|
152
|
+
{
|
|
153
|
+
type: "text",
|
|
154
|
+
text: formatError(error, `updating webhook subscription '${subscriptionId}'`),
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
isError: true,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
registerTool<{ subscriptionId: string }>(
|
|
164
|
+
server,
|
|
165
|
+
"delete_webhook_subscription",
|
|
166
|
+
"Delete a webhook subscription. Historical delivery records are retained but no new deliveries will be dispatched.",
|
|
167
|
+
{
|
|
168
|
+
subscriptionId: z.string().describe("The webhook subscription UUID"),
|
|
169
|
+
},
|
|
170
|
+
async ({ subscriptionId }) => {
|
|
171
|
+
try {
|
|
172
|
+
await sdk.webhookSubscriptions.delete(subscriptionId);
|
|
173
|
+
return {
|
|
174
|
+
content: [
|
|
175
|
+
{
|
|
176
|
+
type: "text",
|
|
177
|
+
text: `Webhook subscription '${subscriptionId}' deleted successfully.`,
|
|
178
|
+
},
|
|
179
|
+
],
|
|
180
|
+
};
|
|
181
|
+
} catch (error: unknown) {
|
|
182
|
+
return {
|
|
183
|
+
content: [
|
|
184
|
+
{
|
|
185
|
+
type: "text",
|
|
186
|
+
text: formatError(error, `deleting webhook subscription '${subscriptionId}'`),
|
|
187
|
+
},
|
|
188
|
+
],
|
|
189
|
+
isError: true,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
registerTool<{ subscriptionId: string }>(
|
|
196
|
+
server,
|
|
197
|
+
"rotate_webhook_subscription_secret",
|
|
198
|
+
"Rotate the signing secret on a webhook subscription. Immediate cutover — the old secret stops signing on the next dispatch. The response includes the new secret; capture it before closing.",
|
|
199
|
+
{
|
|
200
|
+
subscriptionId: z.string().describe("The webhook subscription UUID"),
|
|
201
|
+
},
|
|
202
|
+
async ({ subscriptionId }) => {
|
|
203
|
+
try {
|
|
204
|
+
const result = await sdk.webhookSubscriptions.rotateSecret(subscriptionId);
|
|
205
|
+
return {
|
|
206
|
+
content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }],
|
|
207
|
+
};
|
|
208
|
+
} catch (error: unknown) {
|
|
209
|
+
return {
|
|
210
|
+
content: [
|
|
211
|
+
{
|
|
212
|
+
type: "text",
|
|
213
|
+
text: formatError(error, `rotating secret for webhook subscription '${subscriptionId}'`),
|
|
214
|
+
},
|
|
215
|
+
],
|
|
216
|
+
isError: true,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
// ── Delivery history + replay ──────────────────────────────────
|
|
223
|
+
|
|
224
|
+
registerTool<{
|
|
225
|
+
subscriptionId: string;
|
|
226
|
+
status?: DeliveryStatusValue;
|
|
227
|
+
since?: string;
|
|
228
|
+
until?: string;
|
|
229
|
+
limit?: number;
|
|
230
|
+
offset?: number;
|
|
231
|
+
}>(
|
|
232
|
+
server,
|
|
233
|
+
"list_webhook_deliveries",
|
|
234
|
+
"List delivery history for a webhook subscription. Rows omit requestPayload and responseBody — use get_webhook_delivery to fetch full payload and response body for a specific delivery.",
|
|
235
|
+
{
|
|
236
|
+
subscriptionId: z.string().describe("The webhook subscription UUID"),
|
|
237
|
+
status: z
|
|
238
|
+
.enum(DELIVERY_STATUS_VALUES)
|
|
239
|
+
.optional()
|
|
240
|
+
.describe("Filter deliveries by status"),
|
|
241
|
+
since: z
|
|
242
|
+
.string()
|
|
243
|
+
.optional()
|
|
244
|
+
.describe("ISO 8601 datetime — include deliveries created at or after this time"),
|
|
245
|
+
until: z
|
|
246
|
+
.string()
|
|
247
|
+
.optional()
|
|
248
|
+
.describe("ISO 8601 datetime — include deliveries created at or before this time"),
|
|
249
|
+
limit: z.number().optional().describe("Max rows to return (default 50)"),
|
|
250
|
+
offset: z.number().optional().describe("Number of rows to skip for pagination"),
|
|
251
|
+
},
|
|
252
|
+
async ({ subscriptionId, status, since, until, limit, offset }) => {
|
|
253
|
+
try {
|
|
254
|
+
const options: Record<string, any> = {};
|
|
255
|
+
if (status) options.status = status;
|
|
256
|
+
if (since) options.since = since;
|
|
257
|
+
if (until) options.until = until;
|
|
258
|
+
if (limit !== undefined) options.limit = limit;
|
|
259
|
+
if (offset !== undefined) options.offset = offset;
|
|
260
|
+
|
|
261
|
+
const result = await sdk.webhookSubscriptions.deliveries.list(
|
|
262
|
+
subscriptionId,
|
|
263
|
+
Object.keys(options).length > 0 ? options : undefined
|
|
264
|
+
);
|
|
265
|
+
return {
|
|
266
|
+
content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }],
|
|
267
|
+
};
|
|
268
|
+
} catch (error: unknown) {
|
|
269
|
+
return {
|
|
270
|
+
content: [
|
|
271
|
+
{
|
|
272
|
+
type: "text",
|
|
273
|
+
text: formatError(error, `listing deliveries for webhook subscription '${subscriptionId}'`),
|
|
274
|
+
},
|
|
275
|
+
],
|
|
276
|
+
isError: true,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
registerTool<{ subscriptionId: string; deliveryId: string }>(
|
|
283
|
+
server,
|
|
284
|
+
"get_webhook_delivery",
|
|
285
|
+
"Get a single webhook delivery including the full request payload and response body.",
|
|
286
|
+
{
|
|
287
|
+
subscriptionId: z.string().describe("The webhook subscription UUID"),
|
|
288
|
+
deliveryId: z.string().describe("The delivery UUID"),
|
|
289
|
+
},
|
|
290
|
+
async ({ subscriptionId, deliveryId }) => {
|
|
291
|
+
try {
|
|
292
|
+
const result = await sdk.webhookSubscriptions.deliveries.get(subscriptionId, deliveryId);
|
|
293
|
+
return {
|
|
294
|
+
content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }],
|
|
295
|
+
};
|
|
296
|
+
} catch (error: unknown) {
|
|
297
|
+
return {
|
|
298
|
+
content: [
|
|
299
|
+
{
|
|
300
|
+
type: "text",
|
|
301
|
+
text: formatError(error, `getting webhook delivery '${deliveryId}'`),
|
|
302
|
+
},
|
|
303
|
+
],
|
|
304
|
+
isError: true,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
);
|
|
309
|
+
|
|
310
|
+
registerTool<{ deliveryId: string }>(
|
|
311
|
+
server,
|
|
312
|
+
"retry_webhook_delivery",
|
|
313
|
+
"Replay a previously recorded webhook delivery. Queues a new delivery row pointing at the original via replayedFrom; the retry worker picks it up within ~1 second. The original payload and signature are reused.",
|
|
314
|
+
{
|
|
315
|
+
deliveryId: z.string().describe("The delivery UUID to replay"),
|
|
316
|
+
},
|
|
317
|
+
async ({ deliveryId }) => {
|
|
318
|
+
try {
|
|
319
|
+
const result = await sdk.webhookSubscriptions.deliveries.retry(deliveryId);
|
|
320
|
+
return {
|
|
321
|
+
content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }],
|
|
322
|
+
};
|
|
323
|
+
} catch (error: unknown) {
|
|
324
|
+
return {
|
|
325
|
+
content: [
|
|
326
|
+
{
|
|
327
|
+
type: "text",
|
|
328
|
+
text: formatError(error, `replaying webhook delivery '${deliveryId}'`),
|
|
329
|
+
},
|
|
330
|
+
],
|
|
331
|
+
isError: true,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
);
|
|
336
|
+
|
|
337
|
+
registerTool<{ deliveryId: string }>(
|
|
338
|
+
server,
|
|
339
|
+
"cancel_webhook_delivery",
|
|
340
|
+
"Cancel a webhook delivery that is currently in 'retrying' status. Flips the row to 'failed' with lastError set to 'Cancelled by user'. Returns 409 if the delivery is not retrying or is mid-dispatch.",
|
|
341
|
+
{
|
|
342
|
+
deliveryId: z.string().describe("The delivery UUID to cancel"),
|
|
343
|
+
},
|
|
344
|
+
async ({ deliveryId }) => {
|
|
345
|
+
try {
|
|
346
|
+
const result = await sdk.webhookSubscriptions.deliveries.cancel(deliveryId);
|
|
347
|
+
return {
|
|
348
|
+
content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }],
|
|
349
|
+
};
|
|
350
|
+
} catch (error: unknown) {
|
|
351
|
+
return {
|
|
352
|
+
content: [
|
|
353
|
+
{
|
|
354
|
+
type: "text",
|
|
355
|
+
text: formatError(error, `cancelling webhook delivery '${deliveryId}'`),
|
|
356
|
+
},
|
|
357
|
+
],
|
|
358
|
+
isError: true,
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
);
|
|
363
|
+
}
|