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.
- package/CHANGELOG.md +59 -0
- package/README.md +102 -14
- package/SECURITY.md +4 -4
- package/bin/mcp-event-intelligence.mjs +3 -3
- package/dist/src/composite/engine.d.ts +6 -0
- package/dist/src/composite/engine.js +599 -10
- package/dist/src/composite/matching.js +45 -0
- package/dist/src/intelligenceProtocol/activationSchemas.d.ts +2 -0
- package/dist/src/intelligenceProtocol/activationSchemas.js +1 -0
- package/dist/src/intelligenceProtocol/index.d.ts +9 -2
- package/dist/src/intelligenceProtocol/index.js +5 -1
- package/dist/src/intelligenceProtocol/operatorRegistry.d.ts +567 -0
- package/dist/src/intelligenceProtocol/operatorRegistry.js +477 -0
- package/dist/src/intelligenceProtocol/patternV2Schemas.d.ts +188 -0
- package/dist/src/intelligenceProtocol/patternV2Schemas.js +801 -0
- package/dist/src/intelligenceProtocol/schemas.d.ts +4 -0
- package/dist/src/intelligenceProtocol/schemas.js +2 -0
- package/dist/src/intelligenceProtocol/triggerPlanSchemas.d.ts +177 -4
- package/dist/src/intelligenceProtocol/triggerPlanSchemas.js +37 -4
- package/dist/src/intelligenceProtocol/triggerSchemas.d.ts +256 -0
- package/dist/src/intelligenceProtocol/triggerSchemas.js +161 -2
- package/dist/src/mcpEvents/consumer.d.ts +1 -0
- package/dist/src/mcpEvents/consumer.js +1 -0
- package/dist/src/patternV2/evaluator.d.ts +43 -0
- package/dist/src/patternV2/evaluator.js +927 -0
- package/dist/src/patternV2/legacyCompiler.d.ts +3 -0
- package/dist/src/patternV2/legacyCompiler.js +184 -0
- package/package.json +5 -2
- package/scripts/host-integration.d.mts +7 -0
- package/scripts/host-integration.mjs +9 -0
- package/scripts/lib/activation-hydrator.mjs +1 -0
- package/scripts/lib/composite-event-consumer.mjs +2 -0
- package/scripts/lib/derived-event-coordinator.mjs +75 -1
- package/scripts/lib/mcp-events-client.mjs +62 -24
- package/scripts/lib/mcp-events-compatibility.mjs +54 -0
- package/scripts/lib/trigger-control-plane.mjs +154 -0
- package/scripts/lib/trigger-inspector.mjs +15 -0
- package/scripts/lib/trigger-planner.mjs +190 -12
- package/scripts/mcp-stdio-server.mjs +38 -53
- 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
|
-
|
|
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
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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:
|
|
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
|
-
|
|
438
|
+
conditionOnly: definition.conditionOnly,
|
|
439
|
+
target: definition.target ?? null,
|
|
262
440
|
instruction: definition.continuation?.instruction ?? null,
|
|
263
|
-
evidence: definition.continuation?.contextPolicy?.evidence ??
|
|
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
|
|
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
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
|
|
86
|
-
|
|
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.
|
|
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.
|
|
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": "
|
|
6
|
-
"version": "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.
|
|
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"
|