mcp-event-intelligence 0.1.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 (63) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/LICENSE +201 -0
  3. package/README.md +298 -0
  4. package/SECURITY.md +42 -0
  5. package/bin/mcp-event-intelligence.mjs +52 -0
  6. package/dist/src/composite/engine.d.ts +87 -0
  7. package/dist/src/composite/engine.js +667 -0
  8. package/dist/src/composite/temporal.d.ts +32 -0
  9. package/dist/src/composite/temporal.js +317 -0
  10. package/dist/src/intelligenceProtocol/audit.d.ts +19 -0
  11. package/dist/src/intelligenceProtocol/audit.js +56 -0
  12. package/dist/src/intelligenceProtocol/canonical.d.ts +2 -0
  13. package/dist/src/intelligenceProtocol/canonical.js +21 -0
  14. package/dist/src/intelligenceProtocol/index.d.ts +12 -0
  15. package/dist/src/intelligenceProtocol/index.js +8 -0
  16. package/dist/src/intelligenceProtocol/invariants.d.ts +3 -0
  17. package/dist/src/intelligenceProtocol/invariants.js +36 -0
  18. package/dist/src/intelligenceProtocol/lifecycle.d.ts +4 -0
  19. package/dist/src/intelligenceProtocol/lifecycle.js +24 -0
  20. package/dist/src/intelligenceProtocol/mapper.d.ts +14 -0
  21. package/dist/src/intelligenceProtocol/mapper.js +28 -0
  22. package/dist/src/intelligenceProtocol/schemas.d.ts +325 -0
  23. package/dist/src/intelligenceProtocol/schemas.js +182 -0
  24. package/dist/src/intelligenceProtocol/triggerSchemas.d.ts +488 -0
  25. package/dist/src/intelligenceProtocol/triggerSchemas.js +379 -0
  26. package/dist/src/intelligenceProtocol/version.d.ts +7 -0
  27. package/dist/src/intelligenceProtocol/version.js +7 -0
  28. package/dist/src/mcpEvents/consumer.d.ts +7 -0
  29. package/dist/src/mcpEvents/consumer.js +14 -0
  30. package/dist/src/mcpEvents/server.d.ts +41 -0
  31. package/dist/src/mcpEvents/server.js +122 -0
  32. package/dist/src/protocol/types.d.ts +37 -0
  33. package/dist/src/protocol/types.js +1 -0
  34. package/dist/src/semantic/conditionEngine.d.ts +7 -0
  35. package/dist/src/semantic/conditionEngine.js +65 -0
  36. package/dist/src/semantic/types.d.ts +31 -0
  37. package/dist/src/semantic/types.js +1 -0
  38. package/dist/src/semantic/typesafeJevEvaluator.d.ts +23 -0
  39. package/dist/src/semantic/typesafeJevEvaluator.js +95 -0
  40. package/package.json +85 -0
  41. package/scripts/host-integration.d.mts +86 -0
  42. package/scripts/host-integration.mjs +216 -0
  43. package/scripts/lib/composite-event-consumer.mjs +172 -0
  44. package/scripts/lib/composite-wake-coordinator.mjs +185 -0
  45. package/scripts/lib/derived-contract-registry.mjs +210 -0
  46. package/scripts/lib/derived-event-coordinator.mjs +242 -0
  47. package/scripts/lib/event-processor.mjs +409 -0
  48. package/scripts/lib/generic-runtime-wake.mjs +76 -0
  49. package/scripts/lib/github-mcp-events-adapter.mjs +66 -0
  50. package/scripts/lib/github-webhook.mjs +88 -0
  51. package/scripts/lib/http-wake-deliverer.mjs +31 -0
  52. package/scripts/lib/local-event-intelligence-runtime.mjs +193 -0
  53. package/scripts/lib/mcp-events-client.mjs +519 -0
  54. package/scripts/lib/persistent-event-store.mjs +769 -0
  55. package/scripts/lib/temporal-deadline-scheduler.mjs +111 -0
  56. package/scripts/lib/trigger-control-plane.mjs +939 -0
  57. package/scripts/lib/trigger-inspector.mjs +631 -0
  58. package/scripts/lib/wake-signature.mjs +10 -0
  59. package/scripts/mcp-stdio-server.mjs +380 -0
  60. package/scripts/mcp-stdio.mjs +3 -0
  61. package/scripts/run-tests.mjs +51 -0
  62. package/scripts/service.mjs +756 -0
  63. package/server.json +68 -0
@@ -0,0 +1,379 @@
1
+ import { z } from 'zod';
2
+ import { RuntimeTargetSchema } from './schemas.js';
3
+ export const COMPOSITE_TRIGGER_PROTOCOL_VERSION = '0.1.0';
4
+ export const COMPOSITE_TRIGGER_SCHEMA_VERSION = 'trigger.v0.1';
5
+ const Id = z.string().min(1).max(200);
6
+ const Timestamp = z.string().datetime({ offset: true });
7
+ const Scalar = z.union([z.string(), z.number(), z.boolean()]);
8
+ const ClockTime = z.string().regex(/^(?:[01]\d|2[0-3]):[0-5]\d$/);
9
+ const CalendarDate = z.string().regex(/^\d{4}-\d{2}-\d{2}$/);
10
+ export const StructuredPredicateSchema = z.discriminatedUnion('op', [
11
+ z.object({ path: z.string().min(1), op: z.literal('eq'), value: Scalar }),
12
+ z.object({ path: z.string().min(1), op: z.literal('neq'), value: Scalar }),
13
+ z.object({ path: z.string().min(1), op: z.literal('contains'), value: Scalar }),
14
+ z.object({ path: z.string().min(1), op: z.literal('in'), value: z.array(Scalar).min(1) }),
15
+ z.object({ path: z.string().min(1), op: z.literal('exists'), value: z.boolean().default(true) }),
16
+ ]);
17
+ export const TriggerClauseSchema = z.object({
18
+ id: Id,
19
+ event: z.string().min(1),
20
+ serverId: Id.optional(),
21
+ contractVersion: z.string().min(1).max(50).optional(),
22
+ where: z.array(StructuredPredicateSchema).default([]),
23
+ });
24
+ const TriggerExpressionSchema = z.discriminatedUnion('kind', [
25
+ z.object({ kind: z.literal('allOf'), refs: z.array(Id).min(2) }),
26
+ z.object({ kind: z.literal('anyOf'), refs: z.array(Id).min(1) }),
27
+ z.object({ kind: z.literal('sequence'), refs: z.array(Id).min(2) }),
28
+ z.object({ kind: z.literal('count'), ref: Id, atLeast: z.number().int().min(1) }),
29
+ ]);
30
+ export const CalendarTemporalConditionSchema = z.object({
31
+ id: Id,
32
+ kind: z.literal('calendar'),
33
+ ref: Id,
34
+ timezone: z.string().min(1),
35
+ before: ClockTime.optional(),
36
+ after: ClockTime.optional(),
37
+ weekdays: z.array(z.number().int().min(1).max(7)).min(1).optional(),
38
+ dates: z.array(CalendarDate).min(1).optional(),
39
+ dateRange: z.object({
40
+ start: CalendarDate,
41
+ end: CalendarDate,
42
+ }).optional(),
43
+ dayOfMonth: z.array(z.number().int().min(1).max(31)).min(1).optional(),
44
+ }).strict();
45
+ export const AbsenceTemporalConditionSchema = z.object({
46
+ id: Id,
47
+ kind: z.literal('absence'),
48
+ ref: Id,
49
+ afterRef: Id,
50
+ forMs: z.number().int().positive().optional(),
51
+ untilLocalTime: ClockTime.optional(),
52
+ timezone: z.string().min(1).optional(),
53
+ }).superRefine((value, ctx) => {
54
+ if (!value.forMs && !value.untilLocalTime) {
55
+ ctx.addIssue({
56
+ code: 'custom',
57
+ message: 'absence requires forMs or untilLocalTime',
58
+ });
59
+ }
60
+ if (value.untilLocalTime && !value.timezone) {
61
+ ctx.addIssue({
62
+ code: 'custom',
63
+ message: 'absence untilLocalTime requires timezone',
64
+ });
65
+ }
66
+ });
67
+ export const TemporalConditionSchema = z.discriminatedUnion('kind', [
68
+ CalendarTemporalConditionSchema,
69
+ AbsenceTemporalConditionSchema,
70
+ z.object({ id: Id, kind: z.literal('not'), ref: Id }),
71
+ z.object({ id: Id, kind: z.literal('unless'), ref: Id }),
72
+ z.object({ id: Id, kind: z.literal('after'), ref: Id, afterRef: Id }),
73
+ z.object({ id: Id, kind: z.literal('until'), ref: Id, beforeRef: Id }),
74
+ z.object({
75
+ id: Id,
76
+ kind: z.literal('debounce'),
77
+ ref: Id,
78
+ forMs: z.number().int().positive(),
79
+ }),
80
+ z.object({
81
+ id: Id,
82
+ kind: z.literal('threshold'),
83
+ ref: Id,
84
+ atLeast: z.number().int().min(1),
85
+ }),
86
+ z.object({
87
+ id: Id,
88
+ kind: z.literal('rate'),
89
+ ref: Id,
90
+ atLeast: z.number().int().min(1),
91
+ perMs: z.number().int().positive(),
92
+ }),
93
+ z.object({
94
+ id: Id,
95
+ kind: z.literal('distinct'),
96
+ ref: Id,
97
+ path: z.string().min(1),
98
+ atLeast: z.number().int().min(1),
99
+ }),
100
+ ]);
101
+ export const DerivedEventProjectionSchema = z.object({
102
+ key: z.string().regex(/^[A-Za-z_][A-Za-z0-9_-]{0,63}$/),
103
+ ref: Id,
104
+ path: z.string().min(1),
105
+ }).strict();
106
+ export const DerivedEventDefinitionSchema = z.object({
107
+ name: z.string().min(1).max(200),
108
+ contractVersion: z.string().min(1).max(50),
109
+ projections: z.array(DerivedEventProjectionSchema).max(32).default([]),
110
+ constants: z.record(z.string(), Scalar).default({}),
111
+ }).strict();
112
+ export const DerivedEventEvidenceRefSchema = z.object({
113
+ serverId: Id.nullable(),
114
+ eventName: z.string().min(1),
115
+ sourceEventId: Id,
116
+ traceId: Id,
117
+ occurredAt: Timestamp,
118
+ payloadHash: z.string().regex(/^[a-f0-9]{64}$/).nullable(),
119
+ contractVersion: z.string().min(1).nullable().optional(),
120
+ schemaFingerprint: z.string().regex(/^[a-f0-9]{64}$/).nullable().optional(),
121
+ }).strict();
122
+ export const TriggerLifecyclePolicySchema = z.object({
123
+ oneShot: z.boolean().default(false),
124
+ maxFirings: z.number().int().min(1).optional(),
125
+ cooldownMs: z.number().int().nonnegative().default(0),
126
+ expiresAt: Timestamp.optional(),
127
+ leaseUntil: Timestamp.optional(),
128
+ completeOnGoal: z.boolean().default(false),
129
+ }).superRefine((value, ctx) => {
130
+ if (value.oneShot && value.maxFirings && value.maxFirings !== 1) {
131
+ ctx.addIssue({
132
+ code: 'custom',
133
+ message: 'oneShot is incompatible with maxFirings other than 1',
134
+ });
135
+ }
136
+ if (value.expiresAt &&
137
+ value.leaseUntil &&
138
+ Date.parse(value.leaseUntil) > Date.parse(value.expiresAt)) {
139
+ ctx.addIssue({
140
+ code: 'custom',
141
+ message: 'leaseUntil cannot be after expiresAt',
142
+ });
143
+ }
144
+ });
145
+ export const DeterministicCorrelationSchema = z.object({
146
+ kind: z.literal('same_value'),
147
+ fields: z.array(z.object({
148
+ ref: Id,
149
+ path: z.string().min(1),
150
+ })).min(2),
151
+ });
152
+ export const SemanticCorrelationSchema = z.object({
153
+ instruction: z.string().min(1),
154
+ input: z.array(z.string().min(1)).min(1),
155
+ matchThreshold: z.number().min(0).max(1),
156
+ rejectThreshold: z.number().min(0).max(1),
157
+ uncertain: z.enum(['escalate', 'reject', 'match']).default('escalate'),
158
+ });
159
+ export const CompositeTriggerDefinitionSchema = z.object({
160
+ protocolVersion: z.literal(COMPOSITE_TRIGGER_PROTOCOL_VERSION)
161
+ .default(COMPOSITE_TRIGGER_PROTOCOL_VERSION),
162
+ schemaVersion: z.literal(COMPOSITE_TRIGGER_SCHEMA_VERSION)
163
+ .default(COMPOSITE_TRIGGER_SCHEMA_VERSION),
164
+ triggerId: Id,
165
+ version: z.string().min(1),
166
+ description: z.string().max(500).optional(),
167
+ clauses: z.array(TriggerClauseSchema).min(1),
168
+ expression: TriggerExpressionSchema,
169
+ temporal: z.array(TemporalConditionSchema).default([]),
170
+ lifecycle: TriggerLifecyclePolicySchema.default({
171
+ oneShot: false,
172
+ cooldownMs: 0,
173
+ completeOnGoal: false,
174
+ }),
175
+ withinMs: z.number().int().positive().max(1000 * 60 * 60 * 24 * 30),
176
+ correlation: z.object({
177
+ deterministic: DeterministicCorrelationSchema.optional(),
178
+ semantic: SemanticCorrelationSchema.optional(),
179
+ }).optional(),
180
+ target: RuntimeTargetSchema.optional(),
181
+ derivedEvent: DerivedEventDefinitionSchema.optional(),
182
+ }).superRefine((value, ctx) => {
183
+ if (!value.target && !value.derivedEvent) {
184
+ ctx.addIssue({
185
+ code: 'custom',
186
+ message: 'Trigger requires target, derivedEvent, or both',
187
+ });
188
+ }
189
+ const ids = new Set(value.clauses.map((clause) => clause.id));
190
+ if (ids.size !== value.clauses.length) {
191
+ ctx.addIssue({ code: 'custom', message: 'Trigger clause ids must be unique' });
192
+ }
193
+ const refs = value.expression.kind === 'count'
194
+ ? [value.expression.ref]
195
+ : value.expression.refs;
196
+ for (const ref of refs) {
197
+ if (!ids.has(ref)) {
198
+ ctx.addIssue({
199
+ code: 'custom',
200
+ message: `Expression references unknown clause: ${ref}`,
201
+ });
202
+ }
203
+ }
204
+ for (const field of value.correlation?.deterministic?.fields ?? []) {
205
+ if (!ids.has(field.ref)) {
206
+ ctx.addIssue({
207
+ code: 'custom',
208
+ message: `Correlation references unknown clause: ${field.ref}`,
209
+ });
210
+ }
211
+ }
212
+ if (value.derivedEvent) {
213
+ const projectionKeys = new Set();
214
+ for (const projection of value.derivedEvent.projections) {
215
+ if (!ids.has(projection.ref)) {
216
+ ctx.addIssue({
217
+ code: 'custom',
218
+ message: `Derived event projection references unknown clause: ${projection.ref}`,
219
+ });
220
+ }
221
+ if (projectionKeys.has(projection.key)) {
222
+ ctx.addIssue({
223
+ code: 'custom',
224
+ message: `Derived event projection key must be unique: ${projection.key}`,
225
+ });
226
+ }
227
+ if (projection.key === '_derived') {
228
+ ctx.addIssue({
229
+ code: 'custom',
230
+ message: 'Derived event projection key _derived is reserved',
231
+ });
232
+ }
233
+ if (Object.prototype.hasOwnProperty.call(value.derivedEvent.constants, projection.key)) {
234
+ ctx.addIssue({
235
+ code: 'custom',
236
+ message: `Derived event key cannot be both constant and projection: ${projection.key}`,
237
+ });
238
+ }
239
+ projectionKeys.add(projection.key);
240
+ }
241
+ if (Object.prototype.hasOwnProperty.call(value.derivedEvent.constants, '_derived')) {
242
+ ctx.addIssue({
243
+ code: 'custom',
244
+ message: 'Derived event constant key _derived is reserved',
245
+ });
246
+ }
247
+ }
248
+ const conditionIds = new Set();
249
+ for (const condition of value.temporal) {
250
+ if (conditionIds.has(condition.id)) {
251
+ ctx.addIssue({
252
+ code: 'custom',
253
+ message: `Temporal condition id must be unique: ${condition.id}`,
254
+ });
255
+ }
256
+ conditionIds.add(condition.id);
257
+ const conditionRefs = [];
258
+ if ('ref' in condition)
259
+ conditionRefs.push(condition.ref);
260
+ if ('afterRef' in condition)
261
+ conditionRefs.push(condition.afterRef);
262
+ if ('beforeRef' in condition)
263
+ conditionRefs.push(condition.beforeRef);
264
+ for (const ref of conditionRefs) {
265
+ if (!ids.has(ref)) {
266
+ ctx.addIssue({
267
+ code: 'custom',
268
+ message: `Temporal condition ${condition.id} references unknown clause: ${ref}`,
269
+ });
270
+ }
271
+ }
272
+ if (condition.kind === 'calendar') {
273
+ try {
274
+ new Intl.DateTimeFormat('en-US', { timeZone: condition.timezone });
275
+ }
276
+ catch {
277
+ ctx.addIssue({
278
+ code: 'custom',
279
+ message: `Invalid IANA timezone: ${condition.timezone}`,
280
+ });
281
+ }
282
+ if (condition.before &&
283
+ condition.after &&
284
+ condition.before === condition.after) {
285
+ ctx.addIssue({
286
+ code: 'custom',
287
+ message: 'calendar before and after cannot be identical',
288
+ });
289
+ }
290
+ }
291
+ if (condition.kind === 'absence' &&
292
+ condition.timezone) {
293
+ try {
294
+ new Intl.DateTimeFormat('en-US', { timeZone: condition.timezone });
295
+ }
296
+ catch {
297
+ ctx.addIssue({
298
+ code: 'custom',
299
+ message: `Invalid IANA timezone: ${condition.timezone}`,
300
+ });
301
+ }
302
+ }
303
+ }
304
+ });
305
+ export const CorrelatableEventSchema = z.object({
306
+ traceId: Id,
307
+ sourceEventId: Id,
308
+ name: z.string().min(1),
309
+ occurredAt: Timestamp,
310
+ provider: z.string().min(1).optional(),
311
+ serverId: Id.optional(),
312
+ payloadHash: z.string().regex(/^[a-f0-9]{64}$/).optional(),
313
+ data: z.record(z.string(), z.unknown()),
314
+ });
315
+ export const DerivedEventRecordSchema = z.object({
316
+ event: CorrelatableEventSchema,
317
+ triggerId: Id,
318
+ triggerVersion: z.string().min(1),
319
+ matchId: Id,
320
+ directParents: z.array(DerivedEventEvidenceRefSchema).min(1),
321
+ rootEvidence: z.array(DerivedEventEvidenceRefSchema).min(1),
322
+ createdAt: Timestamp,
323
+ }).strict();
324
+ export const TriggerSourceEventSchema = z.object({
325
+ clauseId: Id,
326
+ traceId: Id,
327
+ sourceEventId: Id,
328
+ eventName: z.string().min(1),
329
+ occurredAt: Timestamp,
330
+ provider: z.string().min(1).optional(),
331
+ serverId: Id.optional(),
332
+ payloadHash: z.string().regex(/^[a-f0-9]{64}$/).optional(),
333
+ data: z.record(z.string(), z.unknown()),
334
+ });
335
+ export const CompositeCorrelationDecisionSchema = z.object({
336
+ evaluator: z.string().min(1),
337
+ outcome: z.enum(['match', 'reject', 'uncertain']),
338
+ probability: z.number().min(0).max(1),
339
+ matched: z.boolean(),
340
+ shouldEscalate: z.boolean(),
341
+ inputFields: z.array(z.string().min(1)),
342
+ providerEvidence: z.record(z.string(), z.unknown()).optional(),
343
+ evaluatedAt: Timestamp,
344
+ });
345
+ export const TriggerMatchStatusSchema = z.enum([
346
+ 'partial',
347
+ 'matched',
348
+ 'emitted',
349
+ 'fired',
350
+ 'expired',
351
+ ]);
352
+ export const TriggerMatchRecordSchema = z.object({
353
+ protocolVersion: z.literal(COMPOSITE_TRIGGER_PROTOCOL_VERSION),
354
+ schemaVersion: z.literal(COMPOSITE_TRIGGER_SCHEMA_VERSION),
355
+ matchId: Id,
356
+ triggerId: Id,
357
+ triggerVersion: z.string().min(1),
358
+ status: TriggerMatchStatusSchema,
359
+ correlationKey: z.string().nullable(),
360
+ openedAt: Timestamp,
361
+ expiresAt: Timestamp,
362
+ updatedAt: Timestamp,
363
+ sourceEvents: z.array(TriggerSourceEventSchema),
364
+ correlationDecision: CompositeCorrelationDecisionSchema.nullable(),
365
+ firedWakeId: Id.nullable(),
366
+ derivedEventIds: z.array(Id).default([]),
367
+ });
368
+ export function parseCompositeTriggerDefinition(value) {
369
+ return CompositeTriggerDefinitionSchema.parse(value);
370
+ }
371
+ export function parseCorrelatableEvent(value) {
372
+ return CorrelatableEventSchema.parse(value);
373
+ }
374
+ export function parseTriggerMatchRecord(value) {
375
+ return TriggerMatchRecordSchema.parse(value);
376
+ }
377
+ export function parseDerivedEventRecord(value) {
378
+ return DerivedEventRecordSchema.parse(value);
379
+ }
@@ -0,0 +1,7 @@
1
+ export declare const EVENT_INTELLIGENCE_PROTOCOL: "mcp-event-intelligence";
2
+ export declare const EVENT_INTELLIGENCE_PROTOCOL_VERSION: "0.1.0";
3
+ export declare const EVENT_INTELLIGENCE_SCHEMA_VERSION: "ei.v0.1";
4
+ export declare const EVENT_INTELLIGENCE_COMPATIBILITY: {
5
+ readonly mcpEvents: "experimental-ext-triggers-events/design-sketch";
6
+ readonly mcpCore: "2026-07-28";
7
+ };
@@ -0,0 +1,7 @@
1
+ export const EVENT_INTELLIGENCE_PROTOCOL = 'mcp-event-intelligence';
2
+ export const EVENT_INTELLIGENCE_PROTOCOL_VERSION = '0.1.0';
3
+ export const EVENT_INTELLIGENCE_SCHEMA_VERSION = 'ei.v0.1';
4
+ export const EVENT_INTELLIGENCE_COMPATIBILITY = {
5
+ mcpEvents: 'experimental-ext-triggers-events/design-sketch',
6
+ mcpCore: '2026-07-28',
7
+ };
@@ -0,0 +1,7 @@
1
+ import type { EventOccurrence } from '../protocol/types.js';
2
+ import { type CorrelatableEvent } from '../intelligenceProtocol/triggerSchemas.js';
3
+ export declare function mcpOccurrenceToCorrelatableEvent(event: EventOccurrence, input: {
4
+ traceId: string;
5
+ provider?: string;
6
+ serverId?: string;
7
+ }): Promise<CorrelatableEvent>;
@@ -0,0 +1,14 @@
1
+ import { sha256Hex } from '../intelligenceProtocol/canonical.js';
2
+ import { CorrelatableEventSchema, } from '../intelligenceProtocol/triggerSchemas.js';
3
+ export async function mcpOccurrenceToCorrelatableEvent(event, input) {
4
+ return CorrelatableEventSchema.parse({
5
+ traceId: input.traceId,
6
+ sourceEventId: event.eventId,
7
+ name: event.name,
8
+ occurredAt: event.timestamp,
9
+ ...(input.provider ? { provider: input.provider } : {}),
10
+ ...(input.serverId ? { serverId: input.serverId } : {}),
11
+ payloadHash: await sha256Hex(event.data),
12
+ data: event.data,
13
+ });
14
+ }
@@ -0,0 +1,41 @@
1
+ import type { EventDescriptor, EventOccurrence, JsonRpcRequest, JsonRpcResponse } from '../protocol/types.js';
2
+ export interface PersistedMcpOccurrence {
3
+ sequence: number;
4
+ serverId: string;
5
+ event: EventOccurrence;
6
+ }
7
+ export interface McpEventOccurrenceStore {
8
+ latestMcpEventSequence(): number;
9
+ listMcpOccurrencesAfter(sequence: number): PersistedMcpOccurrence[];
10
+ }
11
+ export interface RegisteredMcpEvent {
12
+ descriptor: EventDescriptor;
13
+ matcher: (args: Record<string, unknown>, event: EventOccurrence) => boolean;
14
+ }
15
+ export interface PollEventsParams {
16
+ name: string;
17
+ arguments?: Record<string, unknown>;
18
+ cursor?: string | null;
19
+ maxAgeMs?: number;
20
+ maxEvents?: number;
21
+ }
22
+ export interface PollEventsResult {
23
+ events: EventOccurrence[];
24
+ cursor: string;
25
+ truncated: boolean;
26
+ hasMore: boolean;
27
+ nextPollMs: number;
28
+ }
29
+ export declare function encodeMcpEventCursor(sequence: number): string;
30
+ export declare function decodeMcpEventCursor(cursor: string): number;
31
+ export declare class ExperimentalMcpEventsServer {
32
+ private readonly store;
33
+ private readonly now;
34
+ private readonly events;
35
+ constructor(store: McpEventOccurrenceStore, now?: () => Date);
36
+ registerEvent(event: RegisteredMcpEvent): void;
37
+ handleRequest(request: JsonRpcRequest): Promise<JsonRpcResponse>;
38
+ poll(params: PollEventsParams): PollEventsResult;
39
+ private ok;
40
+ private error;
41
+ }
@@ -0,0 +1,122 @@
1
+ const CURSOR_PREFIX = 'mcp-events-v1:';
2
+ export function encodeMcpEventCursor(sequence) {
3
+ return `${CURSOR_PREFIX}${sequence}`;
4
+ }
5
+ export function decodeMcpEventCursor(cursor) {
6
+ if (!cursor.startsWith(CURSOR_PREFIX)) {
7
+ throw new Error('Invalid MCP Events cursor');
8
+ }
9
+ const value = Number(cursor.slice(CURSOR_PREFIX.length));
10
+ if (!Number.isInteger(value) || value < 0) {
11
+ throw new Error('Invalid MCP Events cursor');
12
+ }
13
+ return value;
14
+ }
15
+ export class ExperimentalMcpEventsServer {
16
+ store;
17
+ now;
18
+ events = new Map();
19
+ constructor(store, now = () => new Date()) {
20
+ this.store = store;
21
+ this.now = now;
22
+ }
23
+ registerEvent(event) {
24
+ this.events.set(event.descriptor.name, event);
25
+ }
26
+ async handleRequest(request) {
27
+ try {
28
+ switch (request.method) {
29
+ case 'server/discover':
30
+ return this.ok(request.id, {
31
+ supportedVersions: ['2026-07-28'],
32
+ capabilities: {
33
+ experimental: {
34
+ 'io.modelcontextprotocol.experimental/events': {
35
+ status: 'draft',
36
+ designDate: '2026-02-19',
37
+ methods: ['events/list', 'events/poll'],
38
+ listChanged: false,
39
+ },
40
+ },
41
+ },
42
+ instructions: 'Experimental MCP Events provider. Events follow the Triggers & Events WG design sketch; this is not finalized MCP conformance.',
43
+ });
44
+ case 'events/list':
45
+ return this.ok(request.id, {
46
+ events: [...this.events.values()].map((entry) => entry.descriptor),
47
+ });
48
+ case 'events/poll':
49
+ return this.ok(request.id, this.poll((request.params ?? {})));
50
+ default:
51
+ return this.error(request.id, -32601, `Method not found: ${request.method}`);
52
+ }
53
+ }
54
+ catch (error) {
55
+ return this.error(request.id, -32602, error instanceof Error ? error.message : 'Invalid params');
56
+ }
57
+ }
58
+ poll(params) {
59
+ const registered = this.events.get(params.name);
60
+ if (!registered) {
61
+ throw new Error(`Unknown event: ${params.name}`);
62
+ }
63
+ const latest = this.store.latestMcpEventSequence();
64
+ if (params.cursor === null || params.cursor === undefined) {
65
+ return {
66
+ events: [],
67
+ cursor: encodeMcpEventCursor(latest),
68
+ truncated: false,
69
+ hasMore: false,
70
+ nextPollMs: 5000,
71
+ };
72
+ }
73
+ const start = decodeMcpEventCursor(params.cursor);
74
+ const limit = Math.max(1, Math.min(params.maxEvents ?? 50, 500));
75
+ const minTimestamp = params.maxAgeMs === undefined
76
+ ? Number.NEGATIVE_INFINITY
77
+ : this.now().getTime() - params.maxAgeMs;
78
+ const candidates = this.store.listMcpOccurrencesAfter(start);
79
+ const args = params.arguments ?? {};
80
+ const events = [];
81
+ let cursorSequence = start;
82
+ let truncated = false;
83
+ let stoppedIndex = candidates.length;
84
+ for (let index = 0; index < candidates.length; index += 1) {
85
+ const record = candidates[index];
86
+ cursorSequence = record.sequence;
87
+ if (record.event.name !== params.name)
88
+ continue;
89
+ if (Date.parse(record.event.timestamp) < minTimestamp) {
90
+ truncated = true;
91
+ continue;
92
+ }
93
+ if (!registered.matcher(args, record.event))
94
+ continue;
95
+ events.push(record.event);
96
+ if (events.length >= limit) {
97
+ stoppedIndex = index + 1;
98
+ break;
99
+ }
100
+ }
101
+ const remaining = candidates.slice(stoppedIndex);
102
+ const hasMore = remaining.some((record) => record.event.name === params.name &&
103
+ Date.parse(record.event.timestamp) >= minTimestamp &&
104
+ registered.matcher(args, record.event));
105
+ if (!hasMore && candidates.length > 0) {
106
+ cursorSequence = candidates[candidates.length - 1].sequence;
107
+ }
108
+ return {
109
+ events,
110
+ cursor: encodeMcpEventCursor(cursorSequence),
111
+ truncated,
112
+ hasMore,
113
+ nextPollMs: hasMore ? 0 : 5000,
114
+ };
115
+ }
116
+ ok(id, result) {
117
+ return { jsonrpc: '2.0', id, result };
118
+ }
119
+ error(id, code, message) {
120
+ return { jsonrpc: '2.0', id, error: { code, message } };
121
+ }
122
+ }
@@ -0,0 +1,37 @@
1
+ export type JsonRpcId = string | number;
2
+ export interface JsonRpcRequest<T = unknown> {
3
+ jsonrpc: '2.0';
4
+ id: JsonRpcId;
5
+ method: string;
6
+ params?: T;
7
+ }
8
+ export interface JsonRpcSuccess<T = unknown> {
9
+ jsonrpc: '2.0';
10
+ id: JsonRpcId;
11
+ result: T;
12
+ }
13
+ export interface JsonRpcError {
14
+ jsonrpc: '2.0';
15
+ id: JsonRpcId;
16
+ error: {
17
+ code: number;
18
+ message: string;
19
+ data?: unknown;
20
+ };
21
+ }
22
+ export type JsonRpcResponse<T = unknown> = JsonRpcSuccess<T> | JsonRpcError;
23
+ export interface EventDescriptor {
24
+ name: string;
25
+ description: string;
26
+ delivery: Array<'poll' | 'push' | 'webhook'>;
27
+ inputSchema: Record<string, unknown>;
28
+ payloadSchema: Record<string, unknown>;
29
+ }
30
+ export interface EventOccurrence<TData extends Record<string, unknown> = Record<string, unknown>> {
31
+ eventId: string;
32
+ name: string;
33
+ timestamp: string;
34
+ data: TData;
35
+ cursor?: string | null;
36
+ _meta?: Record<string, unknown>;
37
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,7 @@
1
+ import type { SemanticBooleanCondition, SemanticDecision, SemanticEvaluator } from './types.js';
2
+ export declare function projectSemanticInput(source: Record<string, unknown>, paths: string[]): Record<string, unknown>;
3
+ export declare class SemanticConditionEngine {
4
+ private readonly evaluator;
5
+ constructor(evaluator: SemanticEvaluator);
6
+ evaluate(source: Record<string, unknown>, condition: SemanticBooleanCondition): Promise<SemanticDecision>;
7
+ }