mcp-event-intelligence 0.3.1 → 0.5.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.
Files changed (51) hide show
  1. package/CHANGELOG.md +81 -0
  2. package/README.md +123 -23
  3. package/SECURITY.md +4 -4
  4. package/bin/mcp-event-intelligence.mjs +3 -3
  5. package/dist/src/composite/engine.d.ts +6 -0
  6. package/dist/src/composite/engine.js +599 -10
  7. package/dist/src/composite/matching.d.ts +1 -1
  8. package/dist/src/composite/matching.js +66 -1
  9. package/dist/src/intelligenceProtocol/activationSchemas.d.ts +2 -0
  10. package/dist/src/intelligenceProtocol/activationSchemas.js +1 -0
  11. package/dist/src/intelligenceProtocol/index.d.ts +9 -2
  12. package/dist/src/intelligenceProtocol/index.js +5 -1
  13. package/dist/src/intelligenceProtocol/operatorRegistry.d.ts +567 -0
  14. package/dist/src/intelligenceProtocol/operatorRegistry.js +477 -0
  15. package/dist/src/intelligenceProtocol/patternV2Schemas.d.ts +188 -0
  16. package/dist/src/intelligenceProtocol/patternV2Schemas.js +801 -0
  17. package/dist/src/intelligenceProtocol/schemas.d.ts +6 -0
  18. package/dist/src/intelligenceProtocol/schemas.js +3 -0
  19. package/dist/src/intelligenceProtocol/triggerPlanSchemas.d.ts +179 -4
  20. package/dist/src/intelligenceProtocol/triggerPlanSchemas.js +38 -4
  21. package/dist/src/intelligenceProtocol/triggerSchemas.d.ts +262 -0
  22. package/dist/src/intelligenceProtocol/triggerSchemas.js +164 -2
  23. package/dist/src/mcpEvents/consumer.d.ts +2 -0
  24. package/dist/src/mcpEvents/consumer.js +2 -0
  25. package/dist/src/mcpEvents/erpnextProvider.d.ts +0 -2
  26. package/dist/src/mcpEvents/erpnextProvider.js +0 -3
  27. package/dist/src/mcpEvents/provider.d.ts +24 -10
  28. package/dist/src/mcpEvents/provider.js +124 -55
  29. package/dist/src/mcpEvents/server.d.ts +3 -1
  30. package/dist/src/mcpEvents/server.js +20 -19
  31. package/dist/src/patternV2/evaluator.d.ts +43 -0
  32. package/dist/src/patternV2/evaluator.js +927 -0
  33. package/dist/src/patternV2/legacyCompiler.d.ts +3 -0
  34. package/dist/src/patternV2/legacyCompiler.js +184 -0
  35. package/dist/src/protocol/types.d.ts +1 -0
  36. package/package.json +5 -2
  37. package/scripts/host-integration.d.mts +47 -0
  38. package/scripts/host-integration.mjs +9 -0
  39. package/scripts/lib/activation-hydrator.mjs +1 -0
  40. package/scripts/lib/composite-event-consumer.mjs +4 -0
  41. package/scripts/lib/derived-event-coordinator.mjs +75 -1
  42. package/scripts/lib/json-schema.mjs +134 -0
  43. package/scripts/lib/local-event-intelligence-runtime.mjs +1 -0
  44. package/scripts/lib/mcp-events-client.mjs +798 -253
  45. package/scripts/lib/mcp-events-compatibility.mjs +54 -0
  46. package/scripts/lib/persistent-event-store.mjs +68 -9
  47. package/scripts/lib/trigger-control-plane.mjs +180 -0
  48. package/scripts/lib/trigger-inspector.mjs +15 -0
  49. package/scripts/lib/trigger-planner.mjs +213 -12
  50. package/scripts/mcp-stdio-server.mjs +38 -52
  51. package/server.json +4 -4
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import { RuntimeTargetSchema } from './schemas.js';
3
+ import { PatternAstV2DefinitionSchema, PatternMeasureSchema, collectPatternDurationsMs, collectGuaranteedPatternBindings, collectPatternMeasureRefs, collectPatternRefs, } from './patternV2Schemas.js';
3
4
  export const COMPOSITE_TRIGGER_PROTOCOL_VERSION = '0.1.0';
4
5
  export const COMPOSITE_TRIGGER_SCHEMA_VERSION = 'trigger.v0.1';
5
6
  const Id = z.string().min(1).max(200);
@@ -11,8 +12,28 @@ export const StructuredPredicateSchema = z.discriminatedUnion('op', [
11
12
  z.object({ path: z.string().min(1), op: z.literal('eq'), value: Scalar }),
12
13
  z.object({ path: z.string().min(1), op: z.literal('neq'), value: Scalar }),
13
14
  z.object({ path: z.string().min(1), op: z.literal('contains'), value: Scalar }),
15
+ z.object({ path: z.string().min(1), op: z.literal('startsWith'), value: z.string() }),
16
+ z.object({ path: z.string().min(1), op: z.literal('endsWith'), value: z.string() }),
17
+ z.object({
18
+ path: z.string().min(1),
19
+ op: z.literal('regex'),
20
+ value: z.string(),
21
+ flags: z.string().max(10).optional(),
22
+ }),
14
23
  z.object({ path: z.string().min(1), op: z.literal('in'), value: z.array(Scalar).min(1) }),
24
+ z.object({ path: z.string().min(1), op: z.literal('notIn'), value: z.array(Scalar).min(1) }),
25
+ z.object({
26
+ path: z.string().min(1),
27
+ op: z.literal('between'),
28
+ value: z.tuple([Scalar, Scalar]),
29
+ }),
15
30
  z.object({ path: z.string().min(1), op: z.literal('exists'), value: z.boolean().default(true) }),
31
+ z.object({ path: z.string().min(1), op: z.literal('isNull'), value: z.boolean().default(true) }),
32
+ z.object({
33
+ path: z.string().min(1),
34
+ op: z.literal('type'),
35
+ value: z.enum(['string', 'number', 'boolean', 'null', 'object', 'array']),
36
+ }),
16
37
  z.object({ path: z.string().min(1), op: z.literal('gt'), value: z.number() }),
17
38
  z.object({ path: z.string().min(1), op: z.literal('gte'), value: z.number() }),
18
39
  z.object({ path: z.string().min(1), op: z.literal('lt'), value: z.number() }),
@@ -23,6 +44,7 @@ export const TriggerClauseSchema = z.object({
23
44
  event: z.string().min(1),
24
45
  serverId: Id.optional(),
25
46
  contractVersion: z.string().min(1).max(50).optional(),
47
+ arguments: z.record(z.string(), z.unknown()).default({}),
26
48
  where: z.array(StructuredPredicateSchema).default([]),
27
49
  });
28
50
  const TriggerExpressionSchema = z.discriminatedUnion('kind', [
@@ -112,6 +134,7 @@ export const DerivedEventDefinitionSchema = z.object({
112
134
  contractVersion: z.string().min(1).max(50),
113
135
  projections: z.array(DerivedEventProjectionSchema).max(32).default([]),
114
136
  constants: z.record(z.string(), Scalar).default({}),
137
+ measures: z.array(PatternMeasureSchema).max(32).default([]),
115
138
  }).strict();
116
139
  export const DerivedEventEvidenceRefSchema = z.object({
117
140
  serverId: Id.nullable(),
@@ -119,10 +142,15 @@ export const DerivedEventEvidenceRefSchema = z.object({
119
142
  sourceEventId: Id,
120
143
  traceId: Id,
121
144
  occurredAt: Timestamp,
145
+ receivedAt: Timestamp.optional(),
122
146
  payloadHash: z.string().regex(/^[a-f0-9]{64}$/).nullable(),
123
147
  contractVersion: z.string().min(1).nullable().optional(),
124
148
  schemaFingerprint: z.string().regex(/^[a-f0-9]{64}$/).nullable().optional(),
125
149
  }).strict();
150
+ export const EventTimePolicySchema = z.object({
151
+ allowedLatenessMs: z.number().int().nonnegative()
152
+ .max(1000 * 60 * 60 * 24 * 30),
153
+ }).strict();
126
154
  export const TriggerLifecyclePolicySchema = z.object({
127
155
  oneShot: z.boolean().default(false),
128
156
  maxFirings: z.number().int().min(1).optional(),
@@ -181,10 +209,13 @@ export const CompositeTriggerDefinitionSchema = z.object({
181
209
  triggerId: Id,
182
210
  version: z.string().min(1),
183
211
  description: z.string().max(500).optional(),
212
+ conditionOnly: z.boolean().default(false),
184
213
  continuation: ContinuationContractSchema.optional(),
185
214
  clauses: z.array(TriggerClauseSchema).min(1),
186
215
  expression: TriggerExpressionSchema,
187
216
  temporal: z.array(TemporalConditionSchema).default([]),
217
+ patternV2: PatternAstV2DefinitionSchema.optional(),
218
+ eventTime: EventTimePolicySchema.optional(),
188
219
  lifecycle: TriggerLifecyclePolicySchema.default({
189
220
  oneShot: false,
190
221
  cooldownMs: 0,
@@ -198,10 +229,27 @@ export const CompositeTriggerDefinitionSchema = z.object({
198
229
  target: RuntimeTargetSchema.optional(),
199
230
  derivedEvent: DerivedEventDefinitionSchema.optional(),
200
231
  }).superRefine((value, ctx) => {
201
- if (!value.target && !value.derivedEvent) {
232
+ if (!value.target && !value.derivedEvent && !value.conditionOnly) {
202
233
  ctx.addIssue({
203
234
  code: 'custom',
204
- message: 'Trigger requires target, derivedEvent, or both',
235
+ message: 'Trigger requires target, derivedEvent, or conditionOnly=true',
236
+ });
237
+ }
238
+ if (value.conditionOnly &&
239
+ (value.target || value.derivedEvent || value.continuation)) {
240
+ ctx.addIssue({
241
+ code: 'custom',
242
+ message: 'conditionOnly triggers cannot declare target, derivedEvent, or continuation',
243
+ });
244
+ }
245
+ if (value.conditionOnly &&
246
+ (value.lifecycle.oneShot ||
247
+ value.lifecycle.maxFirings !== undefined ||
248
+ value.lifecycle.cooldownMs > 0 ||
249
+ value.lifecycle.completeOnGoal)) {
250
+ ctx.addIssue({
251
+ code: 'custom',
252
+ message: 'conditionOnly triggers cannot use effect-based lifecycle fields',
205
253
  });
206
254
  }
207
255
  if (value.continuation && !value.target) {
@@ -235,6 +283,36 @@ export const CompositeTriggerDefinitionSchema = z.object({
235
283
  }
236
284
  if (value.derivedEvent) {
237
285
  const projectionKeys = new Set();
286
+ const measureKeys = new Set();
287
+ for (const measure of value.derivedEvent.measures ?? []) {
288
+ if (measure.key === '_derived') {
289
+ ctx.addIssue({
290
+ code: 'custom',
291
+ message: 'Derived event measure key _derived is reserved',
292
+ });
293
+ }
294
+ if (measureKeys.has(measure.key)) {
295
+ ctx.addIssue({
296
+ code: 'custom',
297
+ message: `Derived event measure key must be unique: ${measure.key}`,
298
+ });
299
+ }
300
+ if (Object.prototype.hasOwnProperty.call(value.derivedEvent.constants, measure.key)) {
301
+ ctx.addIssue({
302
+ code: 'custom',
303
+ message: `Derived event key cannot be both constant and measure: ${measure.key}`,
304
+ });
305
+ }
306
+ for (const ref of collectPatternMeasureRefs(measure)) {
307
+ if (!ids.has(ref)) {
308
+ ctx.addIssue({
309
+ code: 'custom',
310
+ message: `Derived event measure ${measure.key} references unknown clause: ${ref}`,
311
+ });
312
+ }
313
+ }
314
+ measureKeys.add(measure.key);
315
+ }
238
316
  for (const projection of value.derivedEvent.projections) {
239
317
  if (!ids.has(projection.ref)) {
240
318
  ctx.addIssue({
@@ -242,6 +320,12 @@ export const CompositeTriggerDefinitionSchema = z.object({
242
320
  message: `Derived event projection references unknown clause: ${projection.ref}`,
243
321
  });
244
322
  }
323
+ if (measureKeys.has(projection.key)) {
324
+ ctx.addIssue({
325
+ code: 'custom',
326
+ message: `Derived event key cannot be both measure and projection: ${projection.key}`,
327
+ });
328
+ }
245
329
  if (projectionKeys.has(projection.key)) {
246
330
  ctx.addIssue({
247
331
  code: 'custom',
@@ -269,6 +353,69 @@ export const CompositeTriggerDefinitionSchema = z.object({
269
353
  });
270
354
  }
271
355
  }
356
+ if (value.patternV2) {
357
+ const patternRefs = collectPatternRefs(value.patternV2.root);
358
+ for (const ref of patternRefs) {
359
+ if (!ids.has(ref)) {
360
+ ctx.addIssue({
361
+ code: 'custom',
362
+ message: `Pattern AST v2 references unknown clause: ${ref}`,
363
+ });
364
+ }
365
+ }
366
+ for (const dimension of value.patternV2.partitionBy) {
367
+ const dimensionRefs = new Set();
368
+ for (const field of dimension.fields) {
369
+ if (!ids.has(field.ref)) {
370
+ ctx.addIssue({
371
+ code: 'custom',
372
+ message: `Pattern partition ${dimension.key} references unknown clause: ${field.ref}`,
373
+ });
374
+ }
375
+ if (dimensionRefs.has(field.ref)) {
376
+ ctx.addIssue({
377
+ code: 'custom',
378
+ message: `Pattern partition ${dimension.key} has duplicate field mapping for ${field.ref}`,
379
+ });
380
+ }
381
+ dimensionRefs.add(field.ref);
382
+ }
383
+ for (const ref of patternRefs) {
384
+ if (!dimensionRefs.has(ref)) {
385
+ ctx.addIssue({
386
+ code: 'custom',
387
+ message: `Pattern partition ${dimension.key} does not map pattern ref: ${ref}`,
388
+ });
389
+ }
390
+ }
391
+ }
392
+ const guaranteedPatternRefs = collectGuaranteedPatternBindings(value.patternV2.root);
393
+ if (typeof value.patternV2.selection.afterMatch === 'object' &&
394
+ !guaranteedPatternRefs.includes(value.patternV2.selection.afterMatch.ref)) {
395
+ ctx.addIssue({
396
+ code: 'custom',
397
+ message: `Pattern afterMatch ref is not guaranteed by every match: ${value.patternV2.selection.afterMatch.ref}`,
398
+ });
399
+ }
400
+ for (const measure of value.derivedEvent?.measures ?? []) {
401
+ for (const ref of collectPatternMeasureRefs(measure)) {
402
+ if (!guaranteedPatternRefs.includes(ref)) {
403
+ ctx.addIssue({
404
+ code: 'custom',
405
+ message: `Derived measure ${measure.key} references non-guaranteed pattern ref: ${ref}`,
406
+ });
407
+ }
408
+ }
409
+ }
410
+ const oversized = collectPatternDurationsMs(value.patternV2.root)
411
+ .filter((duration) => duration > value.withinMs);
412
+ if (oversized.length) {
413
+ ctx.addIssue({
414
+ code: 'custom',
415
+ message: `Pattern AST v2 duration exceeds trigger withinMs: ${Math.max(...oversized)} > ${value.withinMs}`,
416
+ });
417
+ }
418
+ }
272
419
  const conditionIds = new Set();
273
420
  for (const condition of value.temporal) {
274
421
  if (conditionIds.has(condition.id)) {
@@ -331,8 +478,10 @@ export const CorrelatableEventSchema = z.object({
331
478
  sourceEventId: Id,
332
479
  name: z.string().min(1),
333
480
  occurredAt: Timestamp,
481
+ receivedAt: Timestamp.optional(),
334
482
  provider: z.string().min(1).optional(),
335
483
  serverId: Id.optional(),
484
+ subscriptionArguments: z.record(z.string(), z.unknown()).default({}),
336
485
  payloadHash: z.string().regex(/^[a-f0-9]{64}$/).optional(),
337
486
  data: z.record(z.string(), z.unknown()),
338
487
  });
@@ -351,8 +500,10 @@ export const TriggerSourceEventSchema = z.object({
351
500
  sourceEventId: Id,
352
501
  eventName: z.string().min(1),
353
502
  occurredAt: Timestamp,
503
+ receivedAt: Timestamp.optional(),
354
504
  provider: z.string().min(1).optional(),
355
505
  serverId: Id.optional(),
506
+ subscriptionArguments: z.record(z.string(), z.unknown()).default({}),
356
507
  payloadHash: z.string().regex(/^[a-f0-9]{64}$/).optional(),
357
508
  data: z.record(z.string(), z.unknown()),
358
509
  });
@@ -373,6 +524,16 @@ export const TriggerMatchStatusSchema = z.enum([
373
524
  'fired',
374
525
  'expired',
375
526
  ]);
527
+ export const PatternMatchStateSchema = z.object({
528
+ version: z.literal('2'),
529
+ role: z.enum(['buffer', 'match']),
530
+ signature: z.string().min(1).optional(),
531
+ maxObservedOccurredAt: Timestamp.optional(),
532
+ semanticDecisions: z.array(z.object({
533
+ nodeId: Id,
534
+ decision: CompositeCorrelationDecisionSchema,
535
+ }).strict()).default([]),
536
+ }).strict();
376
537
  export const TriggerMatchRecordSchema = z.object({
377
538
  protocolVersion: z.literal(COMPOSITE_TRIGGER_PROTOCOL_VERSION),
378
539
  schemaVersion: z.literal(COMPOSITE_TRIGGER_SCHEMA_VERSION),
@@ -386,6 +547,7 @@ export const TriggerMatchRecordSchema = z.object({
386
547
  updatedAt: Timestamp,
387
548
  sourceEvents: z.array(TriggerSourceEventSchema),
388
549
  correlationDecision: CompositeCorrelationDecisionSchema.nullable(),
550
+ patternState: PatternMatchStateSchema.optional(),
389
551
  firedWakeId: Id.nullable(),
390
552
  derivedEventIds: z.array(Id).default([]),
391
553
  });
@@ -4,4 +4,6 @@ export declare function mcpOccurrenceToCorrelatableEvent(event: EventOccurrence,
4
4
  traceId: string;
5
5
  provider?: string;
6
6
  serverId?: string;
7
+ subscriptionArguments?: Record<string, unknown>;
8
+ receivedAt?: string;
7
9
  }): Promise<CorrelatableEvent>;
@@ -6,8 +6,10 @@ export async function mcpOccurrenceToCorrelatableEvent(event, input) {
6
6
  sourceEventId: event.eventId,
7
7
  name: event.name,
8
8
  occurredAt: event.timestamp,
9
+ ...(input.receivedAt ? { receivedAt: input.receivedAt } : {}),
9
10
  ...(input.provider ? { provider: input.provider } : {}),
10
11
  ...(input.serverId ? { serverId: input.serverId } : {}),
12
+ subscriptionArguments: input.subscriptionArguments ?? {},
11
13
  payloadHash: await sha256Hex(event.data),
12
14
  data: event.data,
13
15
  });
@@ -14,7 +14,5 @@ export type ErpNextEventPoller<TContext = unknown> = (request: ProviderPollReque
14
14
  export interface CreateErpNextEventsProviderOptions<TContext = unknown> {
15
15
  pollSalesInvoices: ErpNextEventPoller<TContext>;
16
16
  pollSalesOrders: ErpNextEventPoller<TContext>;
17
- supportedVersions?: string[];
18
- instructions?: string;
19
17
  }
20
18
  export declare function createErpNextEventsProvider<TContext = unknown>(options: CreateErpNextEventsProviderOptions<TContext>): McpEventsProvider<TContext>;
@@ -15,9 +15,6 @@ export function createErpNextEventsProvider(options) {
15
15
  throw new Error('ERPNext Events provider requires pollSalesOrders()');
16
16
  }
17
17
  return createMcpEventsProvider({
18
- supportedVersions: options.supportedVersions,
19
- instructions: options.instructions ??
20
- 'ERPNext MCP Events adapter exposing host-owned Sales Invoice and Sales Order changes.',
21
18
  events: [
22
19
  {
23
20
  descriptor: {
@@ -1,11 +1,24 @@
1
1
  import type { EventDescriptor, EventOccurrence, JsonRpcRequest, JsonRpcResponse } from '../protocol/types.js';
2
- export declare const MCP_EVENTS_CAPABILITY_KEY: "io.modelcontextprotocol.experimental/events";
2
+ export declare const MCP_EVENTS_EXTENSION_ID: "io.modelcontextprotocol/events";
3
+ export declare const MCP_EVENTS_CAPABILITY_KEY: "io.modelcontextprotocol/events";
3
4
  export declare const MCP_EVENTS_CAPABILITY: Readonly<{
4
- status: "draft";
5
- designDate: "2026-02-19";
6
- methods: readonly ["events/list", "events/poll"];
7
5
  listChanged: false;
8
6
  }>;
7
+ export declare const MCP_EVENTS_ERROR: Readonly<{
8
+ INVALID_PARAMS: -32602;
9
+ METHOD_NOT_FOUND: -32601;
10
+ INTERNAL_ERROR: -32603;
11
+ NOT_FOUND: -32011;
12
+ FORBIDDEN: -32012;
13
+ RESOURCE_EXHAUSTED: -32013;
14
+ UNSUPPORTED: -32014;
15
+ CALLBACK_ENDPOINT_ERROR: -32015;
16
+ }>;
17
+ export declare class McpEventsProtocolError extends Error {
18
+ readonly code: number;
19
+ readonly data?: unknown;
20
+ constructor(code: number, message: string, data?: unknown);
21
+ }
9
22
  export interface ProviderPollRequest<TContext = unknown> {
10
23
  name: string;
11
24
  arguments: Record<string, unknown>;
@@ -16,7 +29,7 @@ export interface ProviderPollRequest<TContext = unknown> {
16
29
  }
17
30
  export interface ProviderPollResult {
18
31
  events: EventOccurrence[];
19
- cursor: string;
32
+ cursor: string | null;
20
33
  truncated?: boolean;
21
34
  hasMore?: boolean;
22
35
  nextPollMs?: number;
@@ -27,17 +40,18 @@ export interface McpProviderEvent<TContext = unknown> {
27
40
  }
28
41
  export interface CreateMcpEventsProviderOptions<TContext = unknown> {
29
42
  events: McpProviderEvent<TContext>[];
30
- supportedVersions?: string[];
31
- instructions?: string;
43
+ listPageSize?: number;
32
44
  }
33
45
  export declare function assertJsonSchemaValue(schemaInput: Record<string, unknown>, value: unknown, path?: string): void;
34
46
  export declare class McpEventsProvider<TContext = unknown> {
35
47
  private readonly events;
36
- readonly supportedVersions: string[];
37
- readonly instructions: string;
48
+ private readonly listPageSize;
38
49
  constructor(options: CreateMcpEventsProviderOptions<TContext>);
39
50
  canHandle(method: string): boolean;
40
- listEvents(): EventDescriptor[];
51
+ listEvents(paramsInput?: unknown): {
52
+ events: EventDescriptor[];
53
+ nextCursor?: string;
54
+ };
41
55
  handleRequest(request: JsonRpcRequest, context?: TContext): Promise<JsonRpcResponse>;
42
56
  private poll;
43
57
  private ok;