mcp-event-intelligence 0.4.0 → 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 (40) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/README.md +102 -14
  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.js +45 -0
  8. package/dist/src/intelligenceProtocol/activationSchemas.d.ts +2 -0
  9. package/dist/src/intelligenceProtocol/activationSchemas.js +1 -0
  10. package/dist/src/intelligenceProtocol/index.d.ts +9 -2
  11. package/dist/src/intelligenceProtocol/index.js +5 -1
  12. package/dist/src/intelligenceProtocol/operatorRegistry.d.ts +567 -0
  13. package/dist/src/intelligenceProtocol/operatorRegistry.js +477 -0
  14. package/dist/src/intelligenceProtocol/patternV2Schemas.d.ts +188 -0
  15. package/dist/src/intelligenceProtocol/patternV2Schemas.js +801 -0
  16. package/dist/src/intelligenceProtocol/schemas.d.ts +4 -0
  17. package/dist/src/intelligenceProtocol/schemas.js +2 -0
  18. package/dist/src/intelligenceProtocol/triggerPlanSchemas.d.ts +177 -4
  19. package/dist/src/intelligenceProtocol/triggerPlanSchemas.js +37 -4
  20. package/dist/src/intelligenceProtocol/triggerSchemas.d.ts +256 -0
  21. package/dist/src/intelligenceProtocol/triggerSchemas.js +161 -2
  22. package/dist/src/mcpEvents/consumer.d.ts +1 -0
  23. package/dist/src/mcpEvents/consumer.js +1 -0
  24. package/dist/src/patternV2/evaluator.d.ts +43 -0
  25. package/dist/src/patternV2/evaluator.js +927 -0
  26. package/dist/src/patternV2/legacyCompiler.d.ts +3 -0
  27. package/dist/src/patternV2/legacyCompiler.js +184 -0
  28. package/package.json +5 -2
  29. package/scripts/host-integration.d.mts +7 -0
  30. package/scripts/host-integration.mjs +9 -0
  31. package/scripts/lib/activation-hydrator.mjs +1 -0
  32. package/scripts/lib/composite-event-consumer.mjs +2 -0
  33. package/scripts/lib/derived-event-coordinator.mjs +75 -1
  34. package/scripts/lib/mcp-events-client.mjs +62 -24
  35. package/scripts/lib/mcp-events-compatibility.mjs +54 -0
  36. package/scripts/lib/trigger-control-plane.mjs +154 -0
  37. package/scripts/lib/trigger-inspector.mjs +15 -0
  38. package/scripts/lib/trigger-planner.mjs +190 -12
  39. package/scripts/mcp-stdio-server.mjs +38 -53
  40. package/server.json +4 -4
@@ -0,0 +1,54 @@
1
+ export const MCP_EVENTS_EXTENSION_ID = 'io.modelcontextprotocol/events';
2
+
3
+ export const MCP_EVENTS_COMPATIBILITY_PROFILES = Object.freeze({
4
+ 'experimental-extension-2026-09-25': Object.freeze({
5
+ id: 'experimental-extension-2026-09-25',
6
+ capabilityStyle: 'extension',
7
+ extensionId: MCP_EVENTS_EXTENSION_ID,
8
+ listMethod: 'events/list',
9
+ pollMethod: 'events/poll',
10
+ deliveryModes: Object.freeze(['poll', 'push', 'webhook']),
11
+ experimental: true,
12
+ }),
13
+ });
14
+
15
+ export const DEFAULT_MCP_EVENTS_COMPATIBILITY_PROFILE =
16
+ 'experimental-extension-2026-09-25';
17
+
18
+ export function getMcpEventsCompatibilityProfile(profileId) {
19
+ const id =
20
+ !profileId || profileId === 'auto'
21
+ ? DEFAULT_MCP_EVENTS_COMPATIBILITY_PROFILE
22
+ : String(profileId);
23
+ const profile = MCP_EVENTS_COMPATIBILITY_PROFILES[id];
24
+ if (!profile) {
25
+ const error = new Error(`Unknown MCP Events compatibility profile: ${id}`);
26
+ error.code = 'MCP_EVENTS_PROFILE_UNKNOWN';
27
+ throw error;
28
+ }
29
+ return profile;
30
+ }
31
+
32
+ export function profileAdvertisedByCapabilities(profile, capabilities = {}) {
33
+ if (profile.capabilityStyle !== 'extension') return false;
34
+ const extensions =
35
+ capabilities?.extensions &&
36
+ !Array.isArray(capabilities.extensions) &&
37
+ typeof capabilities.extensions === 'object'
38
+ ? capabilities.extensions
39
+ : {};
40
+ return Object.prototype.hasOwnProperty.call(
41
+ extensions,
42
+ profile.extensionId,
43
+ );
44
+ }
45
+
46
+ export function resolveMcpEventsCompatibilityProfile({
47
+ capabilities,
48
+ requested = 'auto',
49
+ } = {}) {
50
+ const profile = getMcpEventsCompatibilityProfile(requested);
51
+ return profileAdvertisedByCapabilities(profile, capabilities)
52
+ ? profile
53
+ : null;
54
+ }
@@ -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 (
@@ -742,6 +783,59 @@ export class TriggerControlPlane {
742
783
  );
743
784
  }
744
785
 
786
+ if (definition.patternV2) {
787
+ const clauseForRef = (ref) =>
788
+ definition.clauses.find((candidate) => candidate.id === ref);
789
+ const sourceForRef = (ref) => {
790
+ const clause = clauseForRef(ref);
791
+ return clause ? resolveSource(clause) : null;
792
+ };
793
+
794
+ for (const dimension of definition.patternV2.partitionBy ?? []) {
795
+ for (const field of dimension.fields ?? []) {
796
+ assertAdvertisedPath(
797
+ sourceForRef(field.ref),
798
+ field.path,
799
+ `Pattern partition ${dimension.key}`,
800
+ );
801
+ }
802
+ }
803
+
804
+ walkPatternNode(definition.patternV2.root, (ref, fieldPath, purpose) => {
805
+ assertAdvertisedPath(
806
+ sourceForRef(ref),
807
+ fieldPath,
808
+ purpose,
809
+ );
810
+ });
811
+ }
812
+
813
+ for (const measure of definition.derivedEvent?.measures ?? []) {
814
+ const expression = measure.expression;
815
+ const clauseForRef = (ref) =>
816
+ definition.clauses.find((candidate) => candidate.id === ref);
817
+ const sourceForRef = (ref) => {
818
+ const clause = clauseForRef(ref);
819
+ return clause ? resolveSource(clause) : null;
820
+ };
821
+
822
+ if (expression.kind === 'aggregate' && expression.path) {
823
+ assertAdvertisedPath(
824
+ sourceForRef(expression.ref),
825
+ expression.path,
826
+ `Derived measure ${measure.key}`,
827
+ );
828
+ } else {
829
+ walkPatternValue(expression, (ref, fieldPath) => {
830
+ assertAdvertisedPath(
831
+ sourceForRef(ref),
832
+ fieldPath,
833
+ `Derived measure ${measure.key}`,
834
+ );
835
+ });
836
+ }
837
+ }
838
+
745
839
  for (const projection of definition.derivedEvent?.projections ?? []) {
746
840
  const clause = definition.clauses.find(
747
841
  (candidate) => candidate.id === projection.ref,
@@ -806,6 +900,65 @@ export class TriggerControlPlane {
806
900
  }
807
901
 
808
902
  const allSources = this.store.listEventSources();
903
+ const sourceSchemaForRefPath = (ref, fieldPath) => {
904
+ const clause = definition.clauses.find(
905
+ (candidate) => candidate.id === ref,
906
+ );
907
+ const source = clause
908
+ ? allSources.find(
909
+ (candidate) =>
910
+ candidate.eventName === clause.event &&
911
+ candidate.serverId === clause.serverId &&
912
+ (
913
+ !clause.contractVersion ||
914
+ String(candidate.metadata?.contractVersion || '') ===
915
+ String(clause.contractVersion)
916
+ ),
917
+ )
918
+ : null;
919
+ return getSchemaNode(source?.payloadSchema, fieldPath) ?? {};
920
+ };
921
+
922
+ const schemaForMeasureExpression = (expression) => {
923
+ if (!expression || typeof expression !== 'object') return {};
924
+ if (expression.kind === 'literal') {
925
+ if (expression.value === null) return { type: 'null' };
926
+ return scalarSchemaForConstant(expression.value);
927
+ }
928
+ if (expression.kind === 'field') {
929
+ return sourceSchemaForRefPath(expression.ref, expression.path);
930
+ }
931
+ if (
932
+ expression.kind === 'occurredAt' ||
933
+ expression.kind === 'arithmetic'
934
+ ) {
935
+ return { type: 'number' };
936
+ }
937
+ if (expression.kind === 'aggregate') {
938
+ if (
939
+ ['count', 'countDistinct'].includes(expression.function)
940
+ ) {
941
+ return { type: 'integer' };
942
+ }
943
+ if (
944
+ ['sum', 'avg', 'min', 'max', 'stddev', 'percentile']
945
+ .includes(expression.function)
946
+ ) {
947
+ return { type: 'number' };
948
+ }
949
+ return sourceSchemaForRefPath(
950
+ expression.ref,
951
+ expression.path,
952
+ );
953
+ }
954
+ return {};
955
+ };
956
+
957
+ for (const measure of definition.derivedEvent.measures ?? []) {
958
+ properties[measure.key] =
959
+ schemaForMeasureExpression(measure.expression);
960
+ }
961
+
809
962
  for (const projection of definition.derivedEvent.projections ?? []) {
810
963
  const clause = definition.clauses.find(
811
964
  (candidate) => candidate.id === projection.ref,
@@ -832,6 +985,7 @@ export class TriggerControlPlane {
832
985
  required: [
833
986
  '_derived',
834
987
  ...Object.keys(definition.derivedEvent.constants ?? {}),
988
+ ...(definition.derivedEvent.measures ?? []).map((measure) => measure.key),
835
989
  ],
836
990
  };
837
991
  }
@@ -166,6 +166,18 @@ function explain({
166
166
  };
167
167
  }
168
168
 
169
+ if (definition.patternV2) {
170
+ return {
171
+ code: 'pattern_v2_waiting',
172
+ summary:
173
+ 'Pattern AST v2 has buffered evidence but no new complete match is currently eligible.',
174
+ patternVersion: '2',
175
+ bufferRole: match.patternState?.role ?? null,
176
+ semanticDecisions:
177
+ match.patternState?.semanticDecisions ?? [],
178
+ };
179
+ }
180
+
169
181
  const missing = clauses
170
182
  .filter((clause) => clause.status !== 'satisfied')
171
183
  .map((clause) => clause.clauseId);
@@ -312,6 +324,7 @@ export class TriggerInspector {
312
324
  derivedEvent: definition.derivedEvent ?? null,
313
325
  expression: definition.expression,
314
326
  temporal: definition.temporal ?? [],
327
+ patternV2: definition.patternV2 ?? null,
315
328
  lifecycle: definition.lifecycle ?? {},
316
329
  },
317
330
  lifecycle: state,
@@ -323,6 +336,7 @@ export class TriggerInspector {
323
336
  openedAt: match.openedAt,
324
337
  expiresAt: match.expiresAt,
325
338
  updatedAt: match.updatedAt,
339
+ patternState: match.patternState ?? null,
326
340
  }
327
341
  : null,
328
342
  clauses,
@@ -360,6 +374,7 @@ export class TriggerInspector {
360
374
  rootEvidence: record.rootEvidence,
361
375
  })),
362
376
  matchHistory,
377
+ patternState: match?.patternState ?? null,
363
378
  },
364
379
  why: explain({
365
380
  definition,
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  canonicalJson,
3
+ collectPatternRefs,
3
4
  parseCompositeTriggerDefinition,
4
5
  parseTriggerPlanInput,
5
6
  sha256Hex,
@@ -88,6 +89,104 @@ function validatePredicateAgainstSource(source, predicate, warnings) {
88
89
  [source],
89
90
  );
90
91
  }
92
+
93
+ if (
94
+ ['startsWith', 'endsWith', 'regex'].includes(predicate.op) &&
95
+ node?.type &&
96
+ node.type !== 'string'
97
+ ) {
98
+ throw sourceError(
99
+ 'TRIGGER_PLAN_STRING_FIELD_REQUIRED',
100
+ `Predicate ${predicate.op} requires a string field, but ${predicate.path} is ${node.type}`,
101
+ [source],
102
+ );
103
+ }
104
+ }
105
+
106
+ function clauseSourceForRef(ref, clauses, resolvedSources) {
107
+ const index = clauses.findIndex((clause) => clause.id === ref);
108
+ if (index < 0) {
109
+ const error = new Error(`Temporal condition references unknown event id: ${ref}`);
110
+ error.code = 'TRIGGER_PLAN_EVENT_REF_UNKNOWN';
111
+ throw error;
112
+ }
113
+ return resolvedSources[index];
114
+ }
115
+
116
+ function validateTemporalConditions(conditions, clauses, resolvedSources, warnings) {
117
+ const refs = new Set(clauses.map((clause) => clause.id));
118
+
119
+ for (const condition of conditions) {
120
+ const referenced = [];
121
+ if ('ref' in condition) referenced.push(condition.ref);
122
+ if ('afterRef' in condition) referenced.push(condition.afterRef);
123
+ if ('beforeRef' in condition) referenced.push(condition.beforeRef);
124
+
125
+ for (const ref of referenced) {
126
+ if (!refs.has(ref)) {
127
+ const error = new Error(
128
+ `Temporal condition ${condition.id} references unknown event id: ${ref}`,
129
+ );
130
+ error.code = 'TRIGGER_PLAN_EVENT_REF_UNKNOWN';
131
+ throw error;
132
+ }
133
+ }
134
+
135
+ if (condition.kind === 'distinct') {
136
+ const source = clauseSourceForRef(
137
+ condition.ref,
138
+ clauses,
139
+ resolvedSources,
140
+ );
141
+ validatePredicateAgainstSource(
142
+ source,
143
+ { path: condition.path, op: 'exists', value: true },
144
+ warnings,
145
+ );
146
+ }
147
+ }
148
+ }
149
+
150
+ function walkPatternValue(value, visitor) {
151
+ if (!value || typeof value !== 'object') return;
152
+ if (value.kind === 'field') {
153
+ visitor(value.ref, value.path, 'pattern value');
154
+ return;
155
+ }
156
+ if (value.kind === 'arithmetic') {
157
+ for (const arg of value.args ?? []) walkPatternValue(arg, visitor);
158
+ }
159
+ }
160
+
161
+ function walkPatternNode(node, visitor) {
162
+ if (!node || typeof node !== 'object') return;
163
+
164
+ if (node.kind === 'compare') {
165
+ walkPatternValue(node.left, visitor);
166
+ if (node.right) walkPatternValue(node.right, visitor);
167
+ }
168
+ if (
169
+ (node.kind === 'aggregate' ||
170
+ node.kind === 'state' ||
171
+ node.kind === 'distinct') &&
172
+ node.path
173
+ ) {
174
+ visitor(node.ref, node.path, `pattern ${node.kind}`);
175
+ }
176
+ if (node.kind === 'semantic') {
177
+ for (const input of node.input ?? []) {
178
+ const [ref, ...parts] = String(input).split('.');
179
+ if (ref && parts.length) {
180
+ visitor(ref, parts.join('.'), 'pattern semantic input');
181
+ }
182
+ }
183
+ }
184
+
185
+ if (Array.isArray(node.children)) {
186
+ for (const child of node.children) walkPatternNode(child, visitor);
187
+ }
188
+ if (node.child) walkPatternNode(node.child, visitor);
189
+ if (node.forbidden) walkPatternNode(node.forbidden, visitor);
91
190
  }
92
191
 
93
192
  function expressionFor(match, refs) {
@@ -185,8 +284,69 @@ export class TriggerPlanner {
185
284
  });
186
285
 
187
286
  const refs = clauses.map((clause) => clause.id);
287
+
288
+ if (plan.patternV2) {
289
+ const patternRefs = collectPatternRefs(plan.patternV2.root);
290
+ for (const ref of patternRefs) {
291
+ if (!refs.includes(ref)) {
292
+ const error = new Error(
293
+ `Pattern AST v2 references unknown event id: ${ref}`,
294
+ );
295
+ error.code = 'TRIGGER_PLAN_EVENT_REF_UNKNOWN';
296
+ throw error;
297
+ }
298
+ }
299
+
300
+ const sourceForRef = (ref) => {
301
+ const index = clauses.findIndex((candidate) => candidate.id === ref);
302
+ if (index < 0) {
303
+ const error = new Error(
304
+ `Pattern AST v2 references unknown event id: ${ref}`,
305
+ );
306
+ error.code = 'TRIGGER_PLAN_EVENT_REF_UNKNOWN';
307
+ throw error;
308
+ }
309
+ return resolvedSources[index];
310
+ };
311
+
312
+ walkPatternNode(plan.patternV2.root, (ref, fieldPath, purpose) => {
313
+ validatePredicateAgainstSource(
314
+ sourceForRef(ref),
315
+ { path: fieldPath, op: 'exists', value: true },
316
+ warnings,
317
+ );
318
+ });
319
+
320
+ for (const dimension of plan.patternV2.partitionBy) {
321
+ for (const field of dimension.fields) {
322
+ const clauseIndex = clauses.findIndex(
323
+ (candidate) => candidate.id === field.ref,
324
+ );
325
+ if (clauseIndex < 0) {
326
+ const error = new Error(
327
+ `Pattern partition references unknown event id: ${field.ref}`,
328
+ );
329
+ error.code = 'TRIGGER_PLAN_EVENT_REF_UNKNOWN';
330
+ throw error;
331
+ }
332
+ validatePredicateAgainstSource(
333
+ resolvedSources[clauseIndex],
334
+ { path: field.path, op: 'exists', value: true },
335
+ warnings,
336
+ );
337
+ }
338
+ }
339
+ } else {
340
+ validateTemporalConditions(
341
+ plan.temporal,
342
+ clauses,
343
+ resolvedSources,
344
+ warnings,
345
+ );
346
+ }
347
+
188
348
  const correlation = {};
189
- if (plan.correlateBy) {
349
+ if (!plan.patternV2 && plan.correlateBy) {
190
350
  for (const field of plan.correlateBy) {
191
351
  if (!refs.includes(field.eventId)) {
192
352
  const error = new Error(
@@ -211,32 +371,46 @@ export class TriggerPlanner {
211
371
  })),
212
372
  };
213
373
  }
214
- if (plan.semanticCorrelation) {
374
+ if (!plan.patternV2 && plan.semanticCorrelation) {
215
375
  correlation.semantic = plan.semanticCorrelation;
216
376
  }
217
377
 
218
378
  const triggerId = plan.triggerId || `planned_${(
219
379
  await sha256Hex(canonicalJson({
220
- target: plan.target,
380
+ target: plan.target ?? null,
221
381
  events: plan.events,
222
382
  match: plan.match,
223
- continuation: plan.continuation.instruction,
383
+ temporal: plan.temporal,
384
+ patternV2: plan.patternV2 ?? null,
385
+ eventTime: plan.eventTime ?? null,
386
+ conditionOnly: plan.conditionOnly,
387
+ continuation: plan.continuation?.instruction ?? null,
224
388
  }))
225
389
  ).slice(0, 24)}`;
226
390
 
227
391
  const definition = parseCompositeTriggerDefinition({
228
392
  triggerId,
229
393
  version: plan.version,
230
- description:
231
- plan.description ||
232
- plan.continuation.instruction.slice(0, 500),
233
- continuation: plan.continuation,
394
+ ...(plan.description || plan.continuation?.instruction
395
+ ? {
396
+ description:
397
+ plan.description ||
398
+ plan.continuation.instruction.slice(0, 500),
399
+ }
400
+ : {}),
401
+ conditionOnly: plan.conditionOnly,
402
+ ...(plan.continuation ? { continuation: plan.continuation } : {}),
234
403
  clauses,
235
- expression: expressionFor(plan.match, refs),
404
+ expression: plan.patternV2
405
+ ? { kind: 'anyOf', refs }
406
+ : expressionFor(plan.match, refs),
407
+ temporal: plan.patternV2 ? [] : plan.temporal,
408
+ ...(plan.patternV2 ? { patternV2: plan.patternV2 } : {}),
236
409
  withinMs: plan.withinMs,
410
+ ...(plan.eventTime ? { eventTime: plan.eventTime } : {}),
237
411
  lifecycle: lifecycleFor(plan.lifecycle),
238
412
  ...(Object.keys(correlation).length ? { correlation } : {}),
239
- target: plan.target,
413
+ ...(plan.target ? { target: plan.target } : {}),
240
414
  });
241
415
 
242
416
  return {
@@ -255,12 +429,16 @@ export class TriggerPlanner {
255
429
  arguments: clause.arguments,
256
430
  where: clause.where,
257
431
  })),
432
+ temporal: definition.temporal,
433
+ patternV2: definition.patternV2 ?? null,
258
434
  withinMs: definition.withinMs,
435
+ eventTime: definition.eventTime ?? { allowedLatenessMs: 0 },
259
436
  },
260
437
  then: {
261
- target: definition.target,
438
+ conditionOnly: definition.conditionOnly,
439
+ target: definition.target ?? null,
262
440
  instruction: definition.continuation?.instruction ?? null,
263
- evidence: definition.continuation?.contextPolicy?.evidence ?? 'matched_events',
441
+ evidence: definition.continuation?.contextPolicy?.evidence ?? null,
264
442
  },
265
443
  },
266
444
  };
@@ -2,6 +2,8 @@ import { McpServer } from '@modelcontextprotocol/server';
2
2
  import { serveStdio } from '@modelcontextprotocol/server/stdio';
3
3
  import * as z from 'zod/v4';
4
4
  import {
5
+ TriggerPlanInputSchema,
6
+ describeTriggerLanguage,
5
7
  parseCompositeTriggerDefinition,
6
8
  } from '../dist/src/intelligenceProtocol/index.js';
7
9
  import {
@@ -57,59 +59,26 @@ function ownerFromEnv(env) {
57
59
  };
58
60
  }
59
61
 
60
- const predicateInputSchema = z.object({
61
- path: z.string().min(1),
62
- op: z.enum(['eq', 'neq', 'contains', 'in', 'exists', 'gt', 'gte', 'lt', 'lte']),
63
- value: z.unknown().optional(),
64
- });
62
+ const triggerPlanInputSchema = TriggerPlanInputSchema;
65
63
 
66
- const triggerPlanInputSchema = z.object({
67
- triggerId: z.string().min(1).optional(),
68
- version: z.string().min(1).optional(),
69
- description: z.string().max(500).optional(),
70
- events: z.array(z.object({
71
- id: z.string().min(1).optional(),
72
- event: z.string().min(1),
73
- serverId: z.string().min(1).optional(),
74
- arguments: z.record(z.string(), z.unknown()).optional(),
75
- where: z.array(predicateInputSchema).optional(),
76
- })).min(1),
77
- match: z.union([
78
- z.enum(['all', 'any', 'sequence']),
79
- z.object({
80
- kind: z.literal('count'),
81
- eventId: z.string().min(1),
82
- atLeast: z.number().int().min(1),
83
- }),
64
+ const triggerLanguageDescribeInputSchema = z.object({
65
+ category: z.enum([
66
+ 'predicates',
67
+ 'composition',
68
+ 'temporal',
69
+ 'pattern',
70
+ 'windows',
71
+ 'aggregates',
72
+ 'state',
73
+ 'selection',
74
+ 'semantic',
75
+ 'execution',
76
+ 'correlation',
77
+ 'timing',
78
+ 'lifecycle',
84
79
  ]).optional(),
85
- withinMs: z.number().int().positive().optional(),
86
- lifecycle: z.object({
87
- oneShot: z.boolean().optional(),
88
- maxFirings: z.number().int().min(1).optional(),
89
- cooldownMs: z.number().int().nonnegative().optional(),
90
- expiresAt: z.string().optional(),
91
- leaseUntil: z.string().optional(),
92
- completeOnGoal: z.boolean().optional(),
93
- }).optional(),
94
- target: z.object({
95
- runtime: z.string().min(1),
96
- kind: z.enum(['goal', 'session', 'conversation', 'task', 'spawn_template']),
97
- id: z.string().min(1),
98
- }),
99
- continuation: z.object({
100
- instruction: z.string().min(1).max(4000),
101
- contextPolicy: z.object({
102
- evidence: z.enum(['matched_events', 'refs_only']).optional(),
103
- maxEvents: z.number().int().min(1).max(50).optional(),
104
- includeData: z.boolean().optional(),
105
- }).optional(),
106
- }),
107
- correlateBy: z.array(z.object({
108
- eventId: z.string().min(1),
109
- path: z.string().min(1),
110
- })).min(2).optional(),
111
- semanticCorrelation: z.record(z.string(), z.unknown()).optional(),
112
- });
80
+ operator: z.string().min(1).optional(),
81
+ }).strict();
113
82
 
114
83
  function registerReadTools(server, runtime) {
115
84
  server.registerTool(
@@ -134,6 +103,22 @@ function registerReadTools(server, runtime) {
134
103
  },
135
104
  );
136
105
 
106
+ server.registerTool(
107
+ 'trigger_language_describe',
108
+ {
109
+ description:
110
+ 'Describe the public Event Intelligence trigger authoring language. Use this when you need to discover supported predicates, composition, temporal, correlation, timing, or lifecycle operators before planning a trigger.',
111
+ inputSchema: triggerLanguageDescribeInputSchema,
112
+ },
113
+ async (input) => {
114
+ try {
115
+ return jsonResult(describeTriggerLanguage(input));
116
+ } catch (error) {
117
+ return errorResult(error);
118
+ }
119
+ },
120
+ );
121
+
137
122
  server.registerTool(
138
123
  'trigger_plan',
139
124
  {
@@ -452,11 +437,11 @@ export async function buildEventIntelligenceMcpServer({
452
437
  const server = new McpServer(
453
438
  {
454
439
  name: 'mcp-event-intelligence',
455
- version: '0.3.1',
440
+ version: '0.5.0',
456
441
  },
457
442
  {
458
443
  instructions:
459
- 'Use Event Intelligence to discover event sources, inspect/simulate durable trigger programs, and govern continuations. The agent authors structured trigger definitions using the discovered source schemas. Persistent mutations are unavailable unless the operator enables MCP_WRITE_ENABLED=true, and each mutation requires confirmationId.',
444
+ 'Use Event Intelligence to discover event sources and the trigger authoring language, plan/inspect/simulate durable trigger programs, and govern continuations. Prefer event_sources_list plus trigger_language_describe when needed, then author through trigger_plan instead of raw canonical definitions. Persistent mutations are unavailable unless the operator enables MCP_WRITE_ENABLED=true, and each mutation requires confirmationId.',
460
445
  },
461
446
  );
462
447
 
package/server.json CHANGED
@@ -2,8 +2,8 @@
2
2
  "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
3
  "name": "io.github.sarooo17/event-intelligence",
4
4
  "title": "MCP Event Intelligence",
5
- "description": "Durable temporal event intelligence: composite triggers, derived events, and runtime wakeups.",
6
- "version": "0.4.0",
5
+ "description": "MCP-native CEP for durable agents: event-time patterns, semantic predicates, derived events.",
6
+ "version": "0.5.0",
7
7
  "repository": {
8
8
  "url": "https://github.com/sarooo17/event-intelligence",
9
9
  "source": "github"
@@ -13,7 +13,7 @@
13
13
  "registryType": "npm",
14
14
  "registryBaseUrl": "https://registry.npmjs.org",
15
15
  "identifier": "mcp-event-intelligence",
16
- "version": "0.4.0",
16
+ "version": "0.5.0",
17
17
  "transport": {
18
18
  "type": "stdio"
19
19
  },
@@ -49,7 +49,7 @@
49
49
  },
50
50
  {
51
51
  "name": "TYPESAFE_API_KEY",
52
- "description": "Optional TypeSafe Jev API key for semantic-correlation triggers.",
52
+ "description": "Optional TypeSafe Jev API key for semantic Pattern AST nodes and legacy semantic-correlation triggers.",
53
53
  "isRequired": false,
54
54
  "isSecret": true,
55
55
  "format": "string"