@decocms/bindings 1.0.1-alpha.3 → 1.0.1
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 +3 -3
- package/package.json +9 -11
- package/src/core/binder.ts +15 -77
- package/src/core/client/index.ts +10 -0
- package/src/core/client/mcp-client.ts +18 -5
- package/src/core/client/mcp.ts +48 -11
- package/src/core/client/proxy.ts +64 -48
- package/src/index.ts +57 -0
- package/src/well-known/assistant.ts +87 -0
- package/src/well-known/collections.ts +105 -84
- package/src/well-known/event-bus.ts +454 -0
- package/src/well-known/event-subscriber.ts +259 -0
- package/src/well-known/language-model.ts +217 -5
- package/src/well-known/mcp.ts +36 -0
- package/src/well-known/prompt.ts +110 -0
- package/src/well-known/registry.ts +128 -0
- package/src/well-known/workflow.ts +297 -0
- package/test/index.test.ts +3 -2
- package/test/mcp.test.ts +40 -0
- package/src/core/subset.ts +0 -514
- package/src/well-known/agent.ts +0 -60
- package/vitest.config.ts +0 -8
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event Bus Well-Known Binding
|
|
3
|
+
*
|
|
4
|
+
* Defines the interface for interacting with an event bus via MCP.
|
|
5
|
+
* Any MCP that implements this binding can publish events and manage subscriptions.
|
|
6
|
+
*
|
|
7
|
+
* This binding includes:
|
|
8
|
+
* - EVENT_PUBLISH: Publish an event to the bus
|
|
9
|
+
* - EVENT_SUBSCRIBE: Subscribe to events of a specific type
|
|
10
|
+
* - EVENT_UNSUBSCRIBE: Remove a subscription
|
|
11
|
+
*
|
|
12
|
+
* Events follow the CloudEvents v1.0 specification.
|
|
13
|
+
* @see https://cloudevents.io/
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { z } from "zod";
|
|
17
|
+
import { bindingClient, type ToolBinder } from "../core/binder";
|
|
18
|
+
|
|
19
|
+
// ============================================================================
|
|
20
|
+
// Publish Schemas
|
|
21
|
+
// ============================================================================
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* EVENT_PUBLISH Input Schema
|
|
25
|
+
*
|
|
26
|
+
* Input for publishing an event.
|
|
27
|
+
* Note: `source` is automatically set by the event bus from the caller's connection ID.
|
|
28
|
+
*/
|
|
29
|
+
export const EventPublishInputSchema = z.object({
|
|
30
|
+
/** Event type (e.g., "order.created", "user.signup") */
|
|
31
|
+
type: z.string().min(1).max(255).describe("Event type identifier"),
|
|
32
|
+
|
|
33
|
+
/** Optional subject/resource identifier */
|
|
34
|
+
subject: z
|
|
35
|
+
.string()
|
|
36
|
+
.max(255)
|
|
37
|
+
.optional()
|
|
38
|
+
.describe("Subject/resource identifier (e.g., order ID)"),
|
|
39
|
+
|
|
40
|
+
/** Event payload (any JSON value) */
|
|
41
|
+
data: z.unknown().optional().describe("Event payload"),
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Optional scheduled delivery time (ISO 8601 timestamp).
|
|
45
|
+
* If provided, the event will not be delivered until this time.
|
|
46
|
+
* If omitted, the event is delivered immediately.
|
|
47
|
+
* Cannot be used together with `cron`.
|
|
48
|
+
*/
|
|
49
|
+
deliverAt: z
|
|
50
|
+
.string()
|
|
51
|
+
.datetime()
|
|
52
|
+
.optional()
|
|
53
|
+
.describe(
|
|
54
|
+
"Scheduled delivery time (ISO 8601). Omit for immediate delivery.",
|
|
55
|
+
),
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Optional cron expression for recurring events.
|
|
59
|
+
* If provided, the event will be delivered repeatedly according to the schedule.
|
|
60
|
+
* Uses standard cron syntax (5 or 6 fields).
|
|
61
|
+
* Cannot be used together with `deliverAt`.
|
|
62
|
+
*
|
|
63
|
+
* Examples:
|
|
64
|
+
* - "0 9 * * 1" - Every Monday at 9:00 AM
|
|
65
|
+
* - "0 0 1 * *" - First day of every month at midnight
|
|
66
|
+
* - "0/15 * * * *" - Every 15 minutes
|
|
67
|
+
*/
|
|
68
|
+
cron: z
|
|
69
|
+
.string()
|
|
70
|
+
.max(100)
|
|
71
|
+
.optional()
|
|
72
|
+
.describe(
|
|
73
|
+
"Cron expression for recurring delivery. Use EVENT_CANCEL to stop.",
|
|
74
|
+
),
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
export type EventPublishInput = z.infer<typeof EventPublishInputSchema>;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* EVENT_PUBLISH Output Schema
|
|
81
|
+
*/
|
|
82
|
+
export const EventPublishOutputSchema = z.object({
|
|
83
|
+
/** Created event ID */
|
|
84
|
+
id: z.string().describe("Unique event ID"),
|
|
85
|
+
|
|
86
|
+
/** Event type */
|
|
87
|
+
type: z.string().describe("Event type"),
|
|
88
|
+
|
|
89
|
+
/** Source connection ID */
|
|
90
|
+
source: z.string().describe("Source connection ID"),
|
|
91
|
+
|
|
92
|
+
/** Event timestamp (ISO 8601) */
|
|
93
|
+
time: z.string().describe("Event timestamp"),
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
export type EventPublishOutput = z.infer<typeof EventPublishOutputSchema>;
|
|
97
|
+
|
|
98
|
+
// ============================================================================
|
|
99
|
+
// Subscribe Schemas
|
|
100
|
+
// ============================================================================
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* EVENT_SUBSCRIBE Input Schema
|
|
104
|
+
*
|
|
105
|
+
* Input for subscribing to events.
|
|
106
|
+
* The subscriber connection ID is automatically set from the caller's token.
|
|
107
|
+
*/
|
|
108
|
+
export const EventSubscribeInputSchema = z.object({
|
|
109
|
+
/** Event type pattern to match */
|
|
110
|
+
eventType: z.string().min(1).max(255).describe("Event type to subscribe to"),
|
|
111
|
+
|
|
112
|
+
/** Optional: Only receive events from this publisher connection */
|
|
113
|
+
publisher: z
|
|
114
|
+
.string()
|
|
115
|
+
.optional()
|
|
116
|
+
.describe("Filter events by publisher connection ID"),
|
|
117
|
+
|
|
118
|
+
/** Optional: JSONPath filter expression on event data */
|
|
119
|
+
filter: z
|
|
120
|
+
.string()
|
|
121
|
+
.max(1000)
|
|
122
|
+
.optional()
|
|
123
|
+
.describe("JSONPath filter expression on event data"),
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
export type EventSubscribeInput = z.infer<typeof EventSubscribeInputSchema>;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* EVENT_SUBSCRIBE Output Schema
|
|
130
|
+
*/
|
|
131
|
+
export const EventSubscribeOutputSchema = z.object({
|
|
132
|
+
/** Created subscription */
|
|
133
|
+
subscription: z.object({
|
|
134
|
+
/** Subscription ID */
|
|
135
|
+
id: z.string().describe("Subscription ID"),
|
|
136
|
+
|
|
137
|
+
/** Subscriber connection ID */
|
|
138
|
+
connectionId: z.string().describe("Subscriber connection ID"),
|
|
139
|
+
|
|
140
|
+
/** Event type pattern */
|
|
141
|
+
eventType: z.string().describe("Event type pattern"),
|
|
142
|
+
|
|
143
|
+
/** Publisher connection filter */
|
|
144
|
+
publisher: z.string().nullable().describe("Publisher connection filter"),
|
|
145
|
+
|
|
146
|
+
/** JSONPath filter */
|
|
147
|
+
filter: z.string().nullable().describe("JSONPath filter expression"),
|
|
148
|
+
|
|
149
|
+
/** Whether subscription is enabled */
|
|
150
|
+
enabled: z.boolean().describe("Whether subscription is enabled"),
|
|
151
|
+
|
|
152
|
+
/** Created timestamp */
|
|
153
|
+
createdAt: z.union([z.string(), z.date()]).describe("Created timestamp"),
|
|
154
|
+
|
|
155
|
+
/** Updated timestamp */
|
|
156
|
+
updatedAt: z.union([z.string(), z.date()]).describe("Updated timestamp"),
|
|
157
|
+
}),
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
export type EventSubscribeOutput = z.infer<typeof EventSubscribeOutputSchema>;
|
|
161
|
+
|
|
162
|
+
// ============================================================================
|
|
163
|
+
// Sync Subscriptions Schemas
|
|
164
|
+
// ============================================================================
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Subscription item schema for sync operations
|
|
168
|
+
*/
|
|
169
|
+
export const SubscriptionItemSchema = z.object({
|
|
170
|
+
/** Event type pattern to match */
|
|
171
|
+
eventType: z.string().min(1).max(255).describe("Event type to subscribe to"),
|
|
172
|
+
|
|
173
|
+
/** Optional: Only receive events from this publisher connection */
|
|
174
|
+
publisher: z
|
|
175
|
+
.string()
|
|
176
|
+
.optional()
|
|
177
|
+
.describe("Filter events by publisher connection ID"),
|
|
178
|
+
|
|
179
|
+
/** Optional: JSONPath filter expression on event data */
|
|
180
|
+
filter: z
|
|
181
|
+
.string()
|
|
182
|
+
.max(1000)
|
|
183
|
+
.optional()
|
|
184
|
+
.describe("JSONPath filter expression on event data"),
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
export type SubscriptionItem = z.infer<typeof SubscriptionItemSchema>;
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Subscription detail schema (returned in responses)
|
|
191
|
+
*/
|
|
192
|
+
export const SubscriptionDetailSchema = z.object({
|
|
193
|
+
/** Subscription ID */
|
|
194
|
+
id: z.string().describe("Subscription ID"),
|
|
195
|
+
|
|
196
|
+
/** Subscriber connection ID */
|
|
197
|
+
connectionId: z.string().describe("Subscriber connection ID"),
|
|
198
|
+
|
|
199
|
+
/** Event type pattern */
|
|
200
|
+
eventType: z.string().describe("Event type pattern"),
|
|
201
|
+
|
|
202
|
+
/** Publisher connection filter */
|
|
203
|
+
publisher: z.string().nullable().describe("Publisher connection filter"),
|
|
204
|
+
|
|
205
|
+
/** JSONPath filter */
|
|
206
|
+
filter: z.string().nullable().describe("JSONPath filter expression"),
|
|
207
|
+
|
|
208
|
+
/** Whether subscription is enabled */
|
|
209
|
+
enabled: z.boolean().describe("Whether subscription is enabled"),
|
|
210
|
+
|
|
211
|
+
/** Created timestamp */
|
|
212
|
+
createdAt: z.union([z.string(), z.date()]).describe("Created timestamp"),
|
|
213
|
+
|
|
214
|
+
/** Updated timestamp */
|
|
215
|
+
updatedAt: z.union([z.string(), z.date()]).describe("Updated timestamp"),
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
export type SubscriptionDetail = z.infer<typeof SubscriptionDetailSchema>;
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* EVENT_SYNC_SUBSCRIPTIONS Input Schema
|
|
222
|
+
*
|
|
223
|
+
* Input for syncing subscriptions to a desired state.
|
|
224
|
+
* The system will create new, delete removed, and update changed subscriptions.
|
|
225
|
+
* Subscriptions are identified by (eventType, publisher) - only one subscription
|
|
226
|
+
* per combination is allowed.
|
|
227
|
+
*/
|
|
228
|
+
export const EventSyncSubscriptionsInputSchema = z.object({
|
|
229
|
+
/** Desired subscriptions - system will create/update/delete to match */
|
|
230
|
+
subscriptions: z
|
|
231
|
+
.array(SubscriptionItemSchema)
|
|
232
|
+
.describe(
|
|
233
|
+
"Desired subscriptions - system will create/update/delete to match",
|
|
234
|
+
),
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
export type EventSyncSubscriptionsInput = z.infer<
|
|
238
|
+
typeof EventSyncSubscriptionsInputSchema
|
|
239
|
+
>;
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* EVENT_SYNC_SUBSCRIPTIONS Output Schema
|
|
243
|
+
*/
|
|
244
|
+
export const EventSyncSubscriptionsOutputSchema = z.object({
|
|
245
|
+
/** Number of new subscriptions created */
|
|
246
|
+
created: z.number().int().min(0).describe("Number of subscriptions created"),
|
|
247
|
+
|
|
248
|
+
/** Number of subscriptions with filter updated */
|
|
249
|
+
updated: z
|
|
250
|
+
.number()
|
|
251
|
+
.int()
|
|
252
|
+
.min(0)
|
|
253
|
+
.describe("Number of subscriptions with filter updated"),
|
|
254
|
+
|
|
255
|
+
/** Number of old subscriptions removed */
|
|
256
|
+
deleted: z.number().int().min(0).describe("Number of subscriptions removed"),
|
|
257
|
+
|
|
258
|
+
/** Number of subscriptions unchanged */
|
|
259
|
+
unchanged: z
|
|
260
|
+
.number()
|
|
261
|
+
.int()
|
|
262
|
+
.min(0)
|
|
263
|
+
.describe("Number of subscriptions unchanged"),
|
|
264
|
+
|
|
265
|
+
/** Current subscriptions after sync */
|
|
266
|
+
subscriptions: z
|
|
267
|
+
.array(SubscriptionDetailSchema)
|
|
268
|
+
.describe("Current subscriptions after sync"),
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
export type EventSyncSubscriptionsOutput = z.infer<
|
|
272
|
+
typeof EventSyncSubscriptionsOutputSchema
|
|
273
|
+
>;
|
|
274
|
+
|
|
275
|
+
// ============================================================================
|
|
276
|
+
// Unsubscribe Schemas
|
|
277
|
+
// ============================================================================
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* EVENT_UNSUBSCRIBE Input Schema
|
|
281
|
+
*/
|
|
282
|
+
export const EventUnsubscribeInputSchema = z.object({
|
|
283
|
+
/** Subscription ID to remove */
|
|
284
|
+
subscriptionId: z.string().describe("Subscription ID to remove"),
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
export type EventUnsubscribeInput = z.infer<typeof EventUnsubscribeInputSchema>;
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* EVENT_UNSUBSCRIBE Output Schema
|
|
291
|
+
*/
|
|
292
|
+
export const EventUnsubscribeOutputSchema = z.object({
|
|
293
|
+
/** Success status */
|
|
294
|
+
success: z.boolean().describe("Whether unsubscribe was successful"),
|
|
295
|
+
|
|
296
|
+
/** Subscription ID that was removed */
|
|
297
|
+
subscriptionId: z.string().describe("Subscription ID that was removed"),
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
export type EventUnsubscribeOutput = z.infer<
|
|
301
|
+
typeof EventUnsubscribeOutputSchema
|
|
302
|
+
>;
|
|
303
|
+
|
|
304
|
+
// ============================================================================
|
|
305
|
+
// Cancel Schemas (for stopping recurring events)
|
|
306
|
+
// ============================================================================
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* EVENT_CANCEL Input Schema
|
|
310
|
+
*
|
|
311
|
+
* Input for cancelling a recurring event.
|
|
312
|
+
* Only the publisher connection can cancel its own events.
|
|
313
|
+
*/
|
|
314
|
+
export const EventCancelInputSchema = z.object({
|
|
315
|
+
/** Event ID to cancel */
|
|
316
|
+
eventId: z.string().describe("Event ID to cancel"),
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
export type EventCancelInput = z.infer<typeof EventCancelInputSchema>;
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* EVENT_CANCEL Output Schema
|
|
323
|
+
*/
|
|
324
|
+
export const EventCancelOutputSchema = z.object({
|
|
325
|
+
/** Success status */
|
|
326
|
+
success: z.boolean().describe("Whether cancellation was successful"),
|
|
327
|
+
|
|
328
|
+
/** Event ID that was cancelled */
|
|
329
|
+
eventId: z.string().describe("Event ID that was cancelled"),
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
export type EventCancelOutput = z.infer<typeof EventCancelOutputSchema>;
|
|
333
|
+
|
|
334
|
+
// ============================================================================
|
|
335
|
+
// Ack Schemas (for acknowledging async event processing)
|
|
336
|
+
// ============================================================================
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* EVENT_ACK Input Schema
|
|
340
|
+
*
|
|
341
|
+
* Input for acknowledging an event delivery.
|
|
342
|
+
* Used when ON_EVENTS returns retryAfter - the subscriber must call EVENT_ACK
|
|
343
|
+
* to confirm successful processing, otherwise the event will be re-delivered.
|
|
344
|
+
*
|
|
345
|
+
* The subscriber connection ID is determined from the caller's token.
|
|
346
|
+
*/
|
|
347
|
+
export const EventAckInputSchema = z.object({
|
|
348
|
+
/** Event ID to acknowledge */
|
|
349
|
+
eventId: z.string().describe("Event ID to acknowledge"),
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
export type EventAckInput = z.infer<typeof EventAckInputSchema>;
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* EVENT_ACK Output Schema
|
|
356
|
+
*/
|
|
357
|
+
export const EventAckOutputSchema = z.object({
|
|
358
|
+
/** Success status */
|
|
359
|
+
success: z.boolean().describe("Whether ACK was successful"),
|
|
360
|
+
|
|
361
|
+
/** Event ID that was acknowledged */
|
|
362
|
+
eventId: z.string().describe("Event ID that was acknowledged"),
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
export type EventAckOutput = z.infer<typeof EventAckOutputSchema>;
|
|
366
|
+
|
|
367
|
+
// ============================================================================
|
|
368
|
+
// Event Bus Binding
|
|
369
|
+
// ============================================================================
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Event Bus Binding
|
|
373
|
+
*
|
|
374
|
+
* Defines the interface for interacting with an event bus.
|
|
375
|
+
* Implementations must provide PUBLISH, SUBSCRIBE, UNSUBSCRIBE, CANCEL, ACK, and SYNC_SUBSCRIPTIONS tools.
|
|
376
|
+
*
|
|
377
|
+
* Required tools:
|
|
378
|
+
* - EVENT_PUBLISH: Publish an event (supports one-time, scheduled, and recurring via cron)
|
|
379
|
+
* - EVENT_SUBSCRIBE: Subscribe to events
|
|
380
|
+
* - EVENT_UNSUBSCRIBE: Remove a subscription
|
|
381
|
+
* - EVENT_CANCEL: Cancel a recurring event (stops future deliveries)
|
|
382
|
+
* - EVENT_ACK: Acknowledge event delivery (for async processing with retryAfter)
|
|
383
|
+
* - EVENT_SYNC_SUBSCRIPTIONS: Sync subscriptions to desired state
|
|
384
|
+
*/
|
|
385
|
+
export const EVENT_BUS_BINDING = [
|
|
386
|
+
{
|
|
387
|
+
name: "EVENT_PUBLISH" as const,
|
|
388
|
+
inputSchema: EventPublishInputSchema,
|
|
389
|
+
outputSchema: EventPublishOutputSchema,
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
name: "EVENT_SUBSCRIBE" as const,
|
|
393
|
+
inputSchema: EventSubscribeInputSchema,
|
|
394
|
+
outputSchema: EventSubscribeOutputSchema,
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
name: "EVENT_UNSUBSCRIBE" as const,
|
|
398
|
+
inputSchema: EventUnsubscribeInputSchema,
|
|
399
|
+
outputSchema: EventUnsubscribeOutputSchema,
|
|
400
|
+
},
|
|
401
|
+
{
|
|
402
|
+
name: "EVENT_CANCEL" as const,
|
|
403
|
+
inputSchema: EventCancelInputSchema,
|
|
404
|
+
outputSchema: EventCancelOutputSchema,
|
|
405
|
+
},
|
|
406
|
+
{
|
|
407
|
+
name: "EVENT_ACK" as const,
|
|
408
|
+
inputSchema: EventAckInputSchema,
|
|
409
|
+
outputSchema: EventAckOutputSchema,
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
name: "EVENT_SYNC_SUBSCRIPTIONS" as const,
|
|
413
|
+
inputSchema: EventSyncSubscriptionsInputSchema,
|
|
414
|
+
outputSchema: EventSyncSubscriptionsOutputSchema,
|
|
415
|
+
},
|
|
416
|
+
] satisfies ToolBinder[];
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Event Bus Binding Client
|
|
420
|
+
*
|
|
421
|
+
* Use this to create a client for interacting with an event bus.
|
|
422
|
+
*
|
|
423
|
+
* @example
|
|
424
|
+
* ```typescript
|
|
425
|
+
* import { EventBusBinding } from "@decocms/bindings/event-bus";
|
|
426
|
+
*
|
|
427
|
+
* // For a connection
|
|
428
|
+
* const client = EventBusBinding.forConnection(connection);
|
|
429
|
+
*
|
|
430
|
+
* // Publish an event
|
|
431
|
+
* const event = await client.EVENT_PUBLISH({
|
|
432
|
+
* type: "order.created",
|
|
433
|
+
* data: { orderId: "123" }
|
|
434
|
+
* });
|
|
435
|
+
*
|
|
436
|
+
* // Subscribe to events
|
|
437
|
+
* const sub = await client.EVENT_SUBSCRIBE({
|
|
438
|
+
* eventType: "order.created"
|
|
439
|
+
* });
|
|
440
|
+
*
|
|
441
|
+
* // Unsubscribe
|
|
442
|
+
* await client.EVENT_UNSUBSCRIBE({
|
|
443
|
+
* subscriptionId: sub.subscription.id
|
|
444
|
+
* });
|
|
445
|
+
* ```
|
|
446
|
+
*/
|
|
447
|
+
export const EventBusBinding = bindingClient(EVENT_BUS_BINDING);
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Type helper for the Event Bus binding client
|
|
451
|
+
*/
|
|
452
|
+
export type EventBusBindingClient = ReturnType<
|
|
453
|
+
typeof EventBusBinding.forConnection
|
|
454
|
+
>;
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event Subscriber Well-Known Binding
|
|
3
|
+
*
|
|
4
|
+
* Defines the interface for MCP connections that can receive events.
|
|
5
|
+
* Any MCP that implements this binding can receive batched CloudEvents
|
|
6
|
+
* from the MCP Mesh event bus.
|
|
7
|
+
*
|
|
8
|
+
* This binding includes:
|
|
9
|
+
* - ON_EVENTS: Receive a batch of CloudEvents
|
|
10
|
+
*
|
|
11
|
+
* Events follow the CloudEvents v1.0 specification.
|
|
12
|
+
* @see https://cloudevents.io/
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { z } from "zod";
|
|
16
|
+
import { bindingClient, type ToolBinder } from "../core/binder";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* CloudEvent Schema
|
|
20
|
+
*
|
|
21
|
+
* Follows CloudEvents v1.0 specification.
|
|
22
|
+
* Required attributes: id, source, type, specversion
|
|
23
|
+
* Optional attributes: time, subject, datacontenttype, dataschema, data
|
|
24
|
+
*
|
|
25
|
+
* @see https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md
|
|
26
|
+
*/
|
|
27
|
+
export const CloudEventSchema = z.object({
|
|
28
|
+
/** CloudEvents specification version (always "1.0") */
|
|
29
|
+
specversion: z.literal("1.0").describe("CloudEvents specification version"),
|
|
30
|
+
|
|
31
|
+
/** Unique identifier for this event */
|
|
32
|
+
id: z
|
|
33
|
+
.string()
|
|
34
|
+
.describe("Unique identifier for this event (UUID recommended)"),
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Source of the event - in MCP Mesh, this is the connection ID of the publisher.
|
|
38
|
+
* Format: URI-reference identifying the context in which an event happened.
|
|
39
|
+
*/
|
|
40
|
+
source: z.string().describe("Connection ID of the event publisher"),
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Event type identifier.
|
|
44
|
+
* Should be a reverse-DNS name like "com.example.order.created"
|
|
45
|
+
*/
|
|
46
|
+
type: z
|
|
47
|
+
.string()
|
|
48
|
+
.describe("Event type (e.g., 'order.created', 'user.signup')"),
|
|
49
|
+
|
|
50
|
+
/** Timestamp of when the event occurred (ISO 8601 format) */
|
|
51
|
+
time: z
|
|
52
|
+
.string()
|
|
53
|
+
.datetime()
|
|
54
|
+
.optional()
|
|
55
|
+
.describe("Timestamp of when the event occurred (ISO 8601)"),
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Subject of the event in the context of the event producer.
|
|
59
|
+
* Can be used to identify the resource the event is about.
|
|
60
|
+
*/
|
|
61
|
+
subject: z
|
|
62
|
+
.string()
|
|
63
|
+
.optional()
|
|
64
|
+
.describe("Subject/resource identifier (e.g., order ID, user ID)"),
|
|
65
|
+
|
|
66
|
+
/** Content type of the data attribute (e.g., "application/json") */
|
|
67
|
+
datacontenttype: z
|
|
68
|
+
.string()
|
|
69
|
+
.optional()
|
|
70
|
+
.default("application/json")
|
|
71
|
+
.describe("Content type of the data attribute"),
|
|
72
|
+
|
|
73
|
+
/** Schema URI for the data attribute */
|
|
74
|
+
dataschema: z
|
|
75
|
+
.string()
|
|
76
|
+
.url()
|
|
77
|
+
.optional()
|
|
78
|
+
.describe("URI to the schema for the data attribute"),
|
|
79
|
+
|
|
80
|
+
/** Event payload - can be any JSON value */
|
|
81
|
+
data: z.unknown().optional().describe("Event payload (any JSON value)"),
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* CloudEvent type - inferred from schema
|
|
86
|
+
*/
|
|
87
|
+
export type CloudEvent = z.infer<typeof CloudEventSchema>;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* ON_EVENTS Input Schema
|
|
91
|
+
*
|
|
92
|
+
* Accepts a batch of CloudEvents for processing.
|
|
93
|
+
*/
|
|
94
|
+
export const OnEventsInputSchema = z.object({
|
|
95
|
+
/** Array of CloudEvents to process */
|
|
96
|
+
events: z
|
|
97
|
+
.array(CloudEventSchema)
|
|
98
|
+
.min(1)
|
|
99
|
+
.describe("Batch of CloudEvents to process"),
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* ON_EVENTS Input type
|
|
104
|
+
*/
|
|
105
|
+
export type OnEventsInput = z.infer<typeof OnEventsInputSchema>;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Per-event result schema
|
|
109
|
+
* Allows granular control over each event in a batch
|
|
110
|
+
*
|
|
111
|
+
* Three modes:
|
|
112
|
+
* - `{ success: true }` - Event processed successfully
|
|
113
|
+
* - `{ success: false, error: "..." }` - Event failed permanently
|
|
114
|
+
* - `{ retryAfter: 60000 }` - Retry later (success not yet determined)
|
|
115
|
+
*/
|
|
116
|
+
export const EventResultSchema = z.object({
|
|
117
|
+
/** Whether this specific event was processed successfully */
|
|
118
|
+
success: z
|
|
119
|
+
.boolean()
|
|
120
|
+
.optional()
|
|
121
|
+
.describe("Whether this event was processed successfully"),
|
|
122
|
+
|
|
123
|
+
/** Error message if success=false */
|
|
124
|
+
error: z.string().optional().describe("Error message for this event"),
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Request re-delivery of this event after this many milliseconds.
|
|
128
|
+
* Does not count toward max retry attempts.
|
|
129
|
+
* When present without success, indicates the event should be retried.
|
|
130
|
+
*/
|
|
131
|
+
retryAfter: z
|
|
132
|
+
.number()
|
|
133
|
+
.int()
|
|
134
|
+
.positive()
|
|
135
|
+
.optional()
|
|
136
|
+
.describe("Re-deliver this event after this many ms"),
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Per-event result type
|
|
141
|
+
*/
|
|
142
|
+
export type EventResult = z.infer<typeof EventResultSchema>;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* ON_EVENTS Output Schema
|
|
146
|
+
*
|
|
147
|
+
* Two modes of operation:
|
|
148
|
+
*
|
|
149
|
+
* 1. **Batch mode** (backward compatible): Use `success`, `error`, `retryAfter`
|
|
150
|
+
* to apply the same result to all events in the batch.
|
|
151
|
+
*
|
|
152
|
+
* 2. **Per-event mode**: Use `results` to specify individual outcomes for each event.
|
|
153
|
+
* Keys are event IDs, values are per-event results.
|
|
154
|
+
* Events not in `results` will use the batch-level fields as fallback.
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* // Batch mode - all events succeeded
|
|
158
|
+
* { success: true }
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* // Per-event mode - mixed results
|
|
162
|
+
* {
|
|
163
|
+
* results: {
|
|
164
|
+
* "event-1": { success: true },
|
|
165
|
+
* "event-2": { success: false, error: "Validation failed" },
|
|
166
|
+
* "event-3": { retryAfter: 60000 }
|
|
167
|
+
* }
|
|
168
|
+
* }
|
|
169
|
+
*/
|
|
170
|
+
export const OnEventsOutputSchema = z.object({
|
|
171
|
+
/** Batch-level success (applies to events not in `results`) */
|
|
172
|
+
success: z
|
|
173
|
+
.boolean()
|
|
174
|
+
.optional()
|
|
175
|
+
.describe("Batch success - applies to events not in results"),
|
|
176
|
+
|
|
177
|
+
/** Batch-level error message */
|
|
178
|
+
error: z
|
|
179
|
+
.string()
|
|
180
|
+
.optional()
|
|
181
|
+
.describe("Batch error message - applies to events not in results"),
|
|
182
|
+
|
|
183
|
+
/** Optional count of successfully processed events */
|
|
184
|
+
processedCount: z
|
|
185
|
+
.number()
|
|
186
|
+
.int()
|
|
187
|
+
.min(0)
|
|
188
|
+
.optional()
|
|
189
|
+
.describe("Number of events successfully processed"),
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Batch-level re-delivery request (applies to events not in `results`)
|
|
193
|
+
*/
|
|
194
|
+
retryAfter: z
|
|
195
|
+
.number()
|
|
196
|
+
.int()
|
|
197
|
+
.positive()
|
|
198
|
+
.optional()
|
|
199
|
+
.describe("Batch retryAfter - applies to events not in results"),
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Per-event results, keyed by event ID.
|
|
203
|
+
* Allows different handling for each event in the batch.
|
|
204
|
+
* Events not specified here use batch-level fields as fallback.
|
|
205
|
+
*/
|
|
206
|
+
results: z
|
|
207
|
+
.record(z.string(), EventResultSchema)
|
|
208
|
+
.optional()
|
|
209
|
+
.describe("Per-event results keyed by event ID"),
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* ON_EVENTS Output type
|
|
214
|
+
*/
|
|
215
|
+
export type OnEventsOutput = z.infer<typeof OnEventsOutputSchema>;
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Event Subscriber Binding
|
|
219
|
+
*
|
|
220
|
+
* Defines the interface for MCP connections that can receive events.
|
|
221
|
+
* Implementations must provide the ON_EVENTS tool to receive batched CloudEvents.
|
|
222
|
+
*
|
|
223
|
+
* Required tools:
|
|
224
|
+
* - ON_EVENTS: Receive and process a batch of CloudEvents
|
|
225
|
+
*/
|
|
226
|
+
export const EVENT_SUBSCRIBER_BINDING = [
|
|
227
|
+
{
|
|
228
|
+
name: "ON_EVENTS" as const,
|
|
229
|
+
inputSchema: OnEventsInputSchema,
|
|
230
|
+
outputSchema: OnEventsOutputSchema,
|
|
231
|
+
},
|
|
232
|
+
] satisfies ToolBinder[];
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Event Subscriber Binding Client
|
|
236
|
+
*
|
|
237
|
+
* Use this to create a client for calling ON_EVENTS on subscriber connections.
|
|
238
|
+
*
|
|
239
|
+
* @example
|
|
240
|
+
* ```typescript
|
|
241
|
+
* import { EventSubscriberBinding } from "@decocms/bindings/event-subscriber";
|
|
242
|
+
*
|
|
243
|
+
* // For a connection
|
|
244
|
+
* const client = EventSubscriberBinding.forConnection(connection);
|
|
245
|
+
* const result = await client.ON_EVENTS({ events: [...] });
|
|
246
|
+
*
|
|
247
|
+
* // For an MCP client
|
|
248
|
+
* const client = EventSubscriberBinding.forClient(mcpClient);
|
|
249
|
+
* const result = await client.ON_EVENTS({ events: [...] });
|
|
250
|
+
* ```
|
|
251
|
+
*/
|
|
252
|
+
export const EventSubscriberBinding = bindingClient(EVENT_SUBSCRIBER_BINDING);
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Type helper for the Event Subscriber binding client
|
|
256
|
+
*/
|
|
257
|
+
export type EventSubscriberBindingClient = ReturnType<
|
|
258
|
+
typeof EventSubscriberBinding.forConnection
|
|
259
|
+
>;
|