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,9 +1,11 @@
1
1
  import {
2
2
  canonicalJson,
3
+ collectPatternRefs,
3
4
  parseCompositeTriggerDefinition,
4
5
  parseTriggerPlanInput,
5
6
  sha256Hex,
6
7
  } from '../../dist/src/intelligenceProtocol/index.js';
8
+ import { assertJsonSchemaValue } from './json-schema.mjs';
7
9
 
8
10
  function schemaNodeAtPath(schema, path) {
9
11
  if (!schema || typeof schema !== 'object') return null;
@@ -25,9 +27,28 @@ function sourceSummary(source) {
25
27
  serverId: source.serverId,
26
28
  eventName: source.eventName,
27
29
  description: source.description ?? null,
30
+ delivery: source.delivery ?? [],
31
+ inputSchema: source.inputSchema ?? {},
32
+ payloadSchema: source.payloadSchema ?? {},
28
33
  };
29
34
  }
30
35
 
36
+ function validateArgumentsAgainstSource(source, args) {
37
+ const schema = source.inputSchema;
38
+ if (!schema || typeof schema !== 'object' || Object.keys(schema).length === 0) {
39
+ return;
40
+ }
41
+ try {
42
+ assertJsonSchemaValue(schema, args ?? {});
43
+ } catch (error) {
44
+ throw sourceError(
45
+ 'TRIGGER_PLAN_ARGUMENTS_INVALID',
46
+ `Subscription arguments do not match ${source.serverId}/${source.eventName}: ${error instanceof Error ? error.message : String(error)}`,
47
+ [source],
48
+ );
49
+ }
50
+ }
51
+
31
52
  function sourceError(code, message, sources = []) {
32
53
  const error = new Error(message);
33
54
  error.code = code;
@@ -68,6 +89,104 @@ function validatePredicateAgainstSource(source, predicate, warnings) {
68
89
  [source],
69
90
  );
70
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);
71
190
  }
72
191
 
73
192
  function expressionFor(match, refs) {
@@ -149,6 +268,7 @@ export class TriggerPlanner {
149
268
  }
150
269
 
151
270
  const source = matches[0];
271
+ validateArgumentsAgainstSource(source, event.arguments);
152
272
  for (const predicate of event.where) {
153
273
  validatePredicateAgainstSource(source, predicate, warnings);
154
274
  }
@@ -158,13 +278,75 @@ export class TriggerPlanner {
158
278
  id,
159
279
  event: event.event,
160
280
  serverId: source.serverId,
281
+ arguments: event.arguments,
161
282
  where: event.where,
162
283
  };
163
284
  });
164
285
 
165
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
+
166
348
  const correlation = {};
167
- if (plan.correlateBy) {
349
+ if (!plan.patternV2 && plan.correlateBy) {
168
350
  for (const field of plan.correlateBy) {
169
351
  if (!refs.includes(field.eventId)) {
170
352
  const error = new Error(
@@ -189,32 +371,46 @@ export class TriggerPlanner {
189
371
  })),
190
372
  };
191
373
  }
192
- if (plan.semanticCorrelation) {
374
+ if (!plan.patternV2 && plan.semanticCorrelation) {
193
375
  correlation.semantic = plan.semanticCorrelation;
194
376
  }
195
377
 
196
378
  const triggerId = plan.triggerId || `planned_${(
197
379
  await sha256Hex(canonicalJson({
198
- target: plan.target,
380
+ target: plan.target ?? null,
199
381
  events: plan.events,
200
382
  match: plan.match,
201
- 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,
202
388
  }))
203
389
  ).slice(0, 24)}`;
204
390
 
205
391
  const definition = parseCompositeTriggerDefinition({
206
392
  triggerId,
207
393
  version: plan.version,
208
- description:
209
- plan.description ||
210
- plan.continuation.instruction.slice(0, 500),
211
- 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 } : {}),
212
403
  clauses,
213
- 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 } : {}),
214
409
  withinMs: plan.withinMs,
410
+ ...(plan.eventTime ? { eventTime: plan.eventTime } : {}),
215
411
  lifecycle: lifecycleFor(plan.lifecycle),
216
412
  ...(Object.keys(correlation).length ? { correlation } : {}),
217
- target: plan.target,
413
+ ...(plan.target ? { target: plan.target } : {}),
218
414
  });
219
415
 
220
416
  return {
@@ -230,14 +426,19 @@ export class TriggerPlanner {
230
426
  id: clause.id,
231
427
  event: clause.event,
232
428
  serverId: clause.serverId,
429
+ arguments: clause.arguments,
233
430
  where: clause.where,
234
431
  })),
432
+ temporal: definition.temporal,
433
+ patternV2: definition.patternV2 ?? null,
235
434
  withinMs: definition.withinMs,
435
+ eventTime: definition.eventTime ?? { allowedLatenessMs: 0 },
236
436
  },
237
437
  then: {
238
- target: definition.target,
438
+ conditionOnly: definition.conditionOnly,
439
+ target: definition.target ?? null,
239
440
  instruction: definition.continuation?.instruction ?? null,
240
- evidence: definition.continuation?.contextPolicy?.evidence ?? 'matched_events',
441
+ evidence: definition.continuation?.contextPolicy?.evidence ?? null,
241
442
  },
242
443
  },
243
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,58 +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
- where: z.array(predicateInputSchema).optional(),
75
- })).min(1),
76
- match: z.union([
77
- z.enum(['all', 'any', 'sequence']),
78
- z.object({
79
- kind: z.literal('count'),
80
- eventId: z.string().min(1),
81
- atLeast: z.number().int().min(1),
82
- }),
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',
83
79
  ]).optional(),
84
- withinMs: z.number().int().positive().optional(),
85
- lifecycle: z.object({
86
- oneShot: z.boolean().optional(),
87
- maxFirings: z.number().int().min(1).optional(),
88
- cooldownMs: z.number().int().nonnegative().optional(),
89
- expiresAt: z.string().optional(),
90
- leaseUntil: z.string().optional(),
91
- completeOnGoal: z.boolean().optional(),
92
- }).optional(),
93
- target: z.object({
94
- runtime: z.string().min(1),
95
- kind: z.enum(['goal', 'session', 'conversation', 'task', 'spawn_template']),
96
- id: z.string().min(1),
97
- }),
98
- continuation: z.object({
99
- instruction: z.string().min(1).max(4000),
100
- contextPolicy: z.object({
101
- evidence: z.enum(['matched_events', 'refs_only']).optional(),
102
- maxEvents: z.number().int().min(1).max(50).optional(),
103
- includeData: z.boolean().optional(),
104
- }).optional(),
105
- }),
106
- correlateBy: z.array(z.object({
107
- eventId: z.string().min(1),
108
- path: z.string().min(1),
109
- })).min(2).optional(),
110
- semanticCorrelation: z.record(z.string(), z.unknown()).optional(),
111
- });
80
+ operator: z.string().min(1).optional(),
81
+ }).strict();
112
82
 
113
83
  function registerReadTools(server, runtime) {
114
84
  server.registerTool(
@@ -133,6 +103,22 @@ function registerReadTools(server, runtime) {
133
103
  },
134
104
  );
135
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
+
136
122
  server.registerTool(
137
123
  'trigger_plan',
138
124
  {
@@ -451,11 +437,11 @@ export async function buildEventIntelligenceMcpServer({
451
437
  const server = new McpServer(
452
438
  {
453
439
  name: 'mcp-event-intelligence',
454
- version: '0.3.1',
440
+ version: '0.5.0',
455
441
  },
456
442
  {
457
443
  instructions:
458
- '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.',
459
445
  },
460
446
  );
461
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.3.1",
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.3.1",
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"