mcp-event-intelligence 0.4.0 → 0.6.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 (64) hide show
  1. package/CHANGELOG.md +100 -0
  2. package/README.md +107 -26
  3. package/SECURITY.md +4 -4
  4. package/bin/mcp-event-intelligence.mjs +3 -3
  5. package/dist/src/composite/engine.d.ts +14 -4
  6. package/dist/src/composite/engine.js +533 -208
  7. package/dist/src/composite/matching.d.ts +1 -5
  8. package/dist/src/composite/matching.js +45 -54
  9. package/dist/src/composite/runtimeSupport.js +10 -6
  10. package/dist/src/composite/store.d.ts +38 -4
  11. package/dist/src/composite/temporal.d.ts +0 -17
  12. package/dist/src/composite/temporal.js +0 -266
  13. package/dist/src/intelligenceProtocol/activationSchemas.d.ts +38 -3
  14. package/dist/src/intelligenceProtocol/activationSchemas.js +5 -3
  15. package/dist/src/intelligenceProtocol/index.d.ts +12 -9
  16. package/dist/src/intelligenceProtocol/index.js +6 -5
  17. package/dist/src/intelligenceProtocol/lifecycle.js +2 -10
  18. package/dist/src/intelligenceProtocol/operatorRegistry.d.ts +300 -0
  19. package/dist/src/intelligenceProtocol/operatorRegistry.js +338 -0
  20. package/dist/src/intelligenceProtocol/patternV2Schemas.d.ts +188 -0
  21. package/dist/src/intelligenceProtocol/patternV2Schemas.js +801 -0
  22. package/dist/src/intelligenceProtocol/schemas.d.ts +4 -189
  23. package/dist/src/intelligenceProtocol/schemas.js +3 -97
  24. package/dist/src/intelligenceProtocol/triggerPlanSchemas.d.ts +114 -38
  25. package/dist/src/intelligenceProtocol/triggerPlanSchemas.js +27 -18
  26. package/dist/src/intelligenceProtocol/triggerSchemas.d.ts +268 -213
  27. package/dist/src/intelligenceProtocol/triggerSchemas.js +155 -169
  28. package/dist/src/intelligenceProtocol/version.d.ts +2 -2
  29. package/dist/src/intelligenceProtocol/version.js +2 -2
  30. package/dist/src/mcpEvents/consumer.d.ts +1 -0
  31. package/dist/src/mcpEvents/consumer.js +1 -0
  32. package/dist/src/patternV2/evaluator.d.ts +51 -0
  33. package/dist/src/patternV2/evaluator.js +986 -0
  34. package/dist/src/semantic/types.d.ts +6 -0
  35. package/dist/src/semantic/typesafeJevEvaluator.d.ts +1 -0
  36. package/dist/src/semantic/typesafeJevEvaluator.js +6 -0
  37. package/package.json +9 -3
  38. package/scripts/host-integration.d.mts +54 -4
  39. package/scripts/host-integration.mjs +25 -4
  40. package/scripts/lib/activation-hydrator.mjs +13 -12
  41. package/scripts/lib/composite-event-consumer.mjs +10 -28
  42. package/scripts/lib/composite-wake-coordinator.mjs +19 -19
  43. package/scripts/lib/derived-contract-registry.mjs +4 -4
  44. package/scripts/lib/derived-event-coordinator.mjs +69 -6
  45. package/scripts/lib/local-event-intelligence-runtime.mjs +35 -3
  46. package/scripts/lib/mcp-events-client.mjs +97 -55
  47. package/scripts/lib/mcp-events-compatibility.mjs +54 -0
  48. package/scripts/lib/persistent-event-store.mjs +228 -61
  49. package/scripts/lib/store-capabilities.mjs +88 -0
  50. package/scripts/lib/store-conformance.mjs +127 -0
  51. package/scripts/lib/temporal-deadline-scheduler.mjs +3 -3
  52. package/scripts/lib/trigger-control-plane.mjs +202 -53
  53. package/scripts/lib/trigger-inspector.mjs +50 -147
  54. package/scripts/lib/trigger-planner.mjs +141 -52
  55. package/scripts/lib/wake-retry-scheduler.mjs +2 -19
  56. package/scripts/mcp-stdio-server.mjs +46 -61
  57. package/scripts/service.mjs +19 -138
  58. package/server.json +4 -4
  59. package/dist/src/intelligenceProtocol/invariants.d.ts +0 -3
  60. package/dist/src/intelligenceProtocol/invariants.js +0 -36
  61. package/dist/src/intelligenceProtocol/mapper.d.ts +0 -14
  62. package/dist/src/intelligenceProtocol/mapper.js +0 -28
  63. package/scripts/lib/event-processor.mjs +0 -624
  64. package/scripts/lib/http-wake-deliverer.mjs +0 -31
@@ -67,6 +67,47 @@ function scalarSchemaForConstant(value) {
67
67
  return {};
68
68
  }
69
69
 
70
+ function walkPatternValue(value, visitor) {
71
+ if (!value || typeof value !== 'object') return;
72
+ if (value.kind === 'field') {
73
+ visitor(value.ref, value.path, 'pattern value');
74
+ return;
75
+ }
76
+ if (value.kind === 'arithmetic') {
77
+ for (const arg of value.args ?? []) walkPatternValue(arg, visitor);
78
+ }
79
+ }
80
+
81
+ function walkPatternNode(node, visitor) {
82
+ if (!node || typeof node !== 'object') return;
83
+
84
+ if (node.kind === 'compare') {
85
+ walkPatternValue(node.left, visitor);
86
+ if (node.right) walkPatternValue(node.right, visitor);
87
+ }
88
+ if (
89
+ node.kind === 'aggregate' ||
90
+ node.kind === 'state' ||
91
+ node.kind === 'distinct'
92
+ ) {
93
+ if (node.path) visitor(node.ref, node.path, `pattern ${node.kind}`);
94
+ }
95
+ if (node.kind === 'semantic') {
96
+ for (const input of node.input ?? []) {
97
+ const [ref, ...parts] = String(input).split('.');
98
+ if (ref && parts.length) {
99
+ visitor(ref, parts.join('.'), 'pattern semantic input');
100
+ }
101
+ }
102
+ }
103
+
104
+ if (Array.isArray(node.children)) {
105
+ for (const child of node.children) walkPatternNode(child, visitor);
106
+ }
107
+ if (node.child) walkPatternNode(node.child, visitor);
108
+ if (node.forbidden) walkPatternNode(node.forbidden, visitor);
109
+ }
110
+
70
111
  function assertSubscriptionArguments(source, args, purpose) {
71
112
  const inputSchema = source?.inputSchema;
72
113
  if (
@@ -122,23 +163,24 @@ export class TriggerControlPlane {
122
163
  });
123
164
  }
124
165
 
125
- listEventSources({ connectionIds } = {}) {
166
+ async listEventSources({ connectionIds } = {}) {
126
167
  return this.store.listEventSources({
127
168
  connectionIds,
128
169
  enabledOnly: true,
129
170
  });
130
171
  }
131
172
 
132
- listTriggers({ owner: ownerInput } = {}) {
173
+ async listTriggers({ owner: ownerInput } = {}) {
133
174
  const owner = ownerInput ? actorIdentity(ownerInput) : null;
134
- return this.store.listTriggers()
135
- .map((definition) => ({
136
- definition,
137
- state: this.store.getTriggerState(
138
- definition.triggerId,
139
- definition.version,
140
- ),
141
- }))
175
+ const definitions = await this.store.listTriggers();
176
+ const entries = await Promise.all(definitions.map(async (definition) => ({
177
+ definition,
178
+ state: await this.store.getTriggerState(
179
+ definition.triggerId,
180
+ definition.version,
181
+ ),
182
+ })));
183
+ return entries
142
184
  .filter((entry) => !owner || samePrincipal(entry.state.owner, owner));
143
185
  }
144
186
 
@@ -195,9 +237,9 @@ export class TriggerControlPlane {
195
237
  throw error;
196
238
  }
197
239
 
198
- this.assertSourcesAvailable(definition, allowedConnectionIds);
240
+ await this.assertSourcesAvailable(definition, allowedConnectionIds);
199
241
 
200
- if (this.store.listTriggers().some((candidate) =>
242
+ if ((await this.store.listTriggers()).some((candidate) =>
201
243
  candidate.triggerId === definition.triggerId &&
202
244
  candidate.version === definition.version
203
245
  )) {
@@ -280,7 +322,7 @@ export class TriggerControlPlane {
280
322
  throw error;
281
323
  }
282
324
 
283
- const current = this.store.listTriggers().find(
325
+ const current = (await this.store.listTriggers()).find(
284
326
  (candidate) =>
285
327
  candidate.triggerId === triggerId &&
286
328
  candidate.version === expectedVersion,
@@ -293,7 +335,7 @@ export class TriggerControlPlane {
293
335
  throw error;
294
336
  }
295
337
 
296
- const currentState = this.store.getTriggerState(
338
+ const currentState = await this.store.getTriggerState(
297
339
  triggerId,
298
340
  expectedVersion,
299
341
  );
@@ -310,16 +352,19 @@ export class TriggerControlPlane {
310
352
  throw error;
311
353
  }
312
354
 
313
- const competing = this.store.listTriggers()
314
- .filter((candidate) => candidate.triggerId === triggerId)
315
- .find((candidate) => {
316
- if (candidate.version === expectedVersion) return false;
317
- const state = this.store.getTriggerState(
318
- candidate.triggerId,
319
- candidate.version,
320
- );
321
- return state.status === 'active' || state.status === 'paused';
322
- });
355
+ let competing = null;
356
+ for (const candidate of (await this.store.listTriggers())
357
+ .filter((item) => item.triggerId === triggerId)) {
358
+ if (candidate.version === expectedVersion) continue;
359
+ const state = await this.store.getTriggerState(
360
+ candidate.triggerId,
361
+ candidate.version,
362
+ );
363
+ if (state.status === 'active' || state.status === 'paused') {
364
+ competing = candidate;
365
+ break;
366
+ }
367
+ }
323
368
  if (competing) {
324
369
  const error = new Error(
325
370
  `Another trigger version is current: ${triggerId}@${competing.version}`,
@@ -339,7 +384,7 @@ export class TriggerControlPlane {
339
384
  error.code = 'TRIGGER_NEW_VERSION_REQUIRED';
340
385
  throw error;
341
386
  }
342
- if (this.store.listTriggers().some((candidate) =>
387
+ if ((await this.store.listTriggers()).some((candidate) =>
343
388
  candidate.triggerId === triggerId &&
344
389
  candidate.version === definition.version
345
390
  )) {
@@ -354,7 +399,7 @@ export class TriggerControlPlane {
354
399
  ? [...new Set(connectionIds.map(String).filter(Boolean))]
355
400
  : currentState.connectionIds;
356
401
 
357
- this.assertSourcesAvailable(definition, allowedConnectionIds);
402
+ await this.assertSourcesAvailable(definition, allowedConnectionIds);
358
403
 
359
404
  await this.retireMatches(
360
405
  triggerId,
@@ -445,7 +490,7 @@ export class TriggerControlPlane {
445
490
  throw error;
446
491
  }
447
492
 
448
- const definition = this.store.listTriggers().find(
493
+ const definition = (await this.store.listTriggers()).find(
449
494
  (candidate) =>
450
495
  candidate.triggerId === triggerId &&
451
496
  candidate.version === version,
@@ -456,7 +501,7 @@ export class TriggerControlPlane {
456
501
  throw error;
457
502
  }
458
503
 
459
- const currentState = this.store.getTriggerState(triggerId, version);
504
+ const currentState = await this.store.getTriggerState(triggerId, version);
460
505
  if (!samePrincipal(currentState.owner, owner)) {
461
506
  const error = new Error('Trigger owner mismatch');
462
507
  error.code = 'TRIGGER_OWNER_MISMATCH';
@@ -565,7 +610,7 @@ export class TriggerControlPlane {
565
610
  throw error;
566
611
  }
567
612
 
568
- const definition = this.store.listTriggers().find(
613
+ const definition = (await this.store.listTriggers()).find(
569
614
  (candidate) =>
570
615
  candidate.triggerId === triggerId &&
571
616
  candidate.version === version,
@@ -576,7 +621,7 @@ export class TriggerControlPlane {
576
621
  throw error;
577
622
  }
578
623
 
579
- const currentState = this.store.getTriggerState(triggerId, version);
624
+ const currentState = await this.store.getTriggerState(triggerId, version);
580
625
  if (!samePrincipal(currentState.owner, owner)) {
581
626
  const error = new Error('Trigger owner mismatch');
582
627
  error.code = 'TRIGGER_OWNER_MISMATCH';
@@ -632,7 +677,7 @@ export class TriggerControlPlane {
632
677
  };
633
678
  }
634
679
 
635
- assertSourcesAvailable(definition, connectionIds) {
680
+ async assertSourcesAvailable(definition, connectionIds) {
636
681
  const allowedConnectionIds = Array.isArray(connectionIds)
637
682
  ? [...new Set(connectionIds.map(String).filter(Boolean))]
638
683
  : [];
@@ -644,7 +689,7 @@ export class TriggerControlPlane {
644
689
  throw error;
645
690
  }
646
691
 
647
- const sources = this.listEventSources({
692
+ const sources = await this.listEventSources({
648
693
  connectionIds: allowedConnectionIds,
649
694
  });
650
695
 
@@ -730,16 +775,58 @@ export class TriggerControlPlane {
730
775
  }
731
776
  }
732
777
 
733
- for (const field of definition.correlation?.deterministic?.fields ?? []) {
734
- const clause = definition.clauses.find(
735
- (candidate) => candidate.id === field.ref,
736
- );
737
- const source = clause ? resolveSource(clause) : null;
738
- assertAdvertisedPath(
739
- source,
740
- field.path,
741
- `Correlation ${field.ref}`,
742
- );
778
+
779
+ {
780
+ const clauseForRef = (ref) =>
781
+ definition.clauses.find((candidate) => candidate.id === ref);
782
+ const sourceForRef = (ref) => {
783
+ const clause = clauseForRef(ref);
784
+ return clause ? resolveSource(clause) : null;
785
+ };
786
+
787
+ for (const dimension of definition.pattern.partitionBy ?? []) {
788
+ for (const field of dimension.fields ?? []) {
789
+ assertAdvertisedPath(
790
+ sourceForRef(field.ref),
791
+ field.path,
792
+ `Pattern partition ${dimension.key}`,
793
+ );
794
+ }
795
+ }
796
+
797
+ walkPatternNode(definition.pattern.root, (ref, fieldPath, purpose) => {
798
+ assertAdvertisedPath(
799
+ sourceForRef(ref),
800
+ fieldPath,
801
+ purpose,
802
+ );
803
+ });
804
+ }
805
+
806
+ for (const measure of definition.derivedEvent?.measures ?? []) {
807
+ const expression = measure.expression;
808
+ const clauseForRef = (ref) =>
809
+ definition.clauses.find((candidate) => candidate.id === ref);
810
+ const sourceForRef = (ref) => {
811
+ const clause = clauseForRef(ref);
812
+ return clause ? resolveSource(clause) : null;
813
+ };
814
+
815
+ if (expression.kind === 'aggregate' && expression.path) {
816
+ assertAdvertisedPath(
817
+ sourceForRef(expression.ref),
818
+ expression.path,
819
+ `Derived measure ${measure.key}`,
820
+ );
821
+ } else {
822
+ walkPatternValue(expression, (ref, fieldPath) => {
823
+ assertAdvertisedPath(
824
+ sourceForRef(ref),
825
+ fieldPath,
826
+ `Derived measure ${measure.key}`,
827
+ );
828
+ });
829
+ }
743
830
  }
744
831
 
745
832
  for (const projection of definition.derivedEvent?.projections ?? []) {
@@ -775,7 +862,7 @@ export class TriggerControlPlane {
775
862
  return allowedConnectionIds;
776
863
  }
777
864
 
778
- buildDerivedOutputPayloadSchema(definition) {
865
+ async buildDerivedOutputPayloadSchema(definition) {
779
866
  if (!definition.derivedEvent) return null;
780
867
 
781
868
  const properties = {
@@ -785,7 +872,7 @@ export class TriggerControlPlane {
785
872
  triggerId: { type: 'string' },
786
873
  triggerVersion: { type: 'string' },
787
874
  matchId: { type: 'string' },
788
- correlationKey: { type: ['string', 'null'] },
875
+ partitionKey: { type: ['string', 'null'] },
789
876
  contractVersion: { type: 'string' },
790
877
  schemaFingerprint: { type: 'string' },
791
878
  },
@@ -805,7 +892,66 @@ export class TriggerControlPlane {
805
892
  properties[key] = scalarSchemaForConstant(value);
806
893
  }
807
894
 
808
- const allSources = this.store.listEventSources();
895
+ const allSources = await this.store.listEventSources();
896
+ const sourceSchemaForRefPath = (ref, fieldPath) => {
897
+ const clause = definition.clauses.find(
898
+ (candidate) => candidate.id === ref,
899
+ );
900
+ const source = clause
901
+ ? allSources.find(
902
+ (candidate) =>
903
+ candidate.eventName === clause.event &&
904
+ candidate.serverId === clause.serverId &&
905
+ (
906
+ !clause.contractVersion ||
907
+ String(candidate.metadata?.contractVersion || '') ===
908
+ String(clause.contractVersion)
909
+ ),
910
+ )
911
+ : null;
912
+ return getSchemaNode(source?.payloadSchema, fieldPath) ?? {};
913
+ };
914
+
915
+ const schemaForMeasureExpression = (expression) => {
916
+ if (!expression || typeof expression !== 'object') return {};
917
+ if (expression.kind === 'literal') {
918
+ if (expression.value === null) return { type: 'null' };
919
+ return scalarSchemaForConstant(expression.value);
920
+ }
921
+ if (expression.kind === 'field') {
922
+ return sourceSchemaForRefPath(expression.ref, expression.path);
923
+ }
924
+ if (
925
+ expression.kind === 'occurredAt' ||
926
+ expression.kind === 'arithmetic'
927
+ ) {
928
+ return { type: 'number' };
929
+ }
930
+ if (expression.kind === 'aggregate') {
931
+ if (
932
+ ['count', 'countDistinct'].includes(expression.function)
933
+ ) {
934
+ return { type: 'integer' };
935
+ }
936
+ if (
937
+ ['sum', 'avg', 'min', 'max', 'stddev', 'percentile']
938
+ .includes(expression.function)
939
+ ) {
940
+ return { type: 'number' };
941
+ }
942
+ return sourceSchemaForRefPath(
943
+ expression.ref,
944
+ expression.path,
945
+ );
946
+ }
947
+ return {};
948
+ };
949
+
950
+ for (const measure of definition.derivedEvent.measures ?? []) {
951
+ properties[measure.key] =
952
+ schemaForMeasureExpression(measure.expression);
953
+ }
954
+
809
955
  for (const projection of definition.derivedEvent.projections ?? []) {
810
956
  const clause = definition.clauses.find(
811
957
  (candidate) => candidate.id === projection.ref,
@@ -832,13 +978,14 @@ export class TriggerControlPlane {
832
978
  required: [
833
979
  '_derived',
834
980
  ...Object.keys(definition.derivedEvent.constants ?? {}),
981
+ ...(definition.derivedEvent.measures ?? []).map((measure) => measure.key),
835
982
  ],
836
983
  };
837
984
  }
838
985
 
839
986
  async assertDerivedOutputContract(definition) {
840
987
  if (!definition.derivedEvent) return null;
841
- const payloadSchema = this.buildDerivedOutputPayloadSchema(definition);
988
+ const payloadSchema = await this.buildDerivedOutputPayloadSchema(definition);
842
989
  try {
843
990
  return await this.contractRegistry.assertCompatible({
844
991
  eventName: definition.derivedEvent.name,
@@ -869,7 +1016,7 @@ export class TriggerControlPlane {
869
1016
  if (!definition.derivedEvent) return null;
870
1017
 
871
1018
  const contractVersion = String(definition.derivedEvent.contractVersion);
872
- const payloadSchema = this.buildDerivedOutputPayloadSchema(definition);
1019
+ const payloadSchema = await this.buildDerivedOutputPayloadSchema(definition);
873
1020
 
874
1021
  const contract = await this.contractRegistry.registerProducer({
875
1022
  eventName: definition.derivedEvent.name,
@@ -912,7 +1059,7 @@ export class TriggerControlPlane {
912
1059
 
913
1060
  async retireMatches(triggerId, version, reason) {
914
1061
  const now = this.now().toISOString();
915
- const matches = this.store.listTriggerMatches(triggerId)
1062
+ const matches = (await this.store.listTriggerMatches(triggerId))
916
1063
  .filter((record) =>
917
1064
  record.triggerVersion === version &&
918
1065
  (record.status === 'partial' || record.status === 'matched')
@@ -924,10 +1071,12 @@ export class TriggerControlPlane {
924
1071
  status: 'expired',
925
1072
  updatedAt: now,
926
1073
  });
927
- for (const deadline of this.store.listTemporalDeadlines?.({
928
- matchId: record.matchId,
929
- status: 'pending',
930
- }) ?? []) {
1074
+ for (const deadline of this.store.listTemporalDeadlines
1075
+ ? await this.store.listTemporalDeadlines({
1076
+ matchId: record.matchId,
1077
+ status: 'pending',
1078
+ })
1079
+ : []) {
931
1080
  await this.store.setTemporalDeadlineStatus?.(
932
1081
  deadline.deadlineId,
933
1082
  'cancelled',
@@ -952,7 +1101,7 @@ export class TriggerControlPlane {
952
1101
  await this.store.appendAudit({
953
1102
  auditId: await stableId(
954
1103
  'audit',
955
- `${kind}:${entityType}:${entityId}:${timestamp}:${this.store.auditLength()}`,
1104
+ `${kind}:${entityType}:${entityId}:${timestamp}:${await this.store.auditLength()}`,
956
1105
  ),
957
1106
  traceId,
958
1107
  timestamp,