mcp-event-intelligence 0.1.0 → 0.1.1
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 +16 -0
- package/README.md +44 -2
- package/bin/mcp-event-intelligence.mjs +5 -1
- package/dist/src/mcpEvents/provider.d.ts +48 -0
- package/dist/src/mcpEvents/provider.js +296 -0
- package/package.json +5 -1
- package/scripts/mcp-stdio-server.mjs +1 -1
- package/server.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
All notable public changes are documented here.
|
|
4
4
|
|
|
5
|
+
## [0.1.1] - 2026-09-20
|
|
6
|
+
|
|
7
|
+
Provider integration patch.
|
|
8
|
+
|
|
9
|
+
### Provider-facing MCP Events adapter
|
|
10
|
+
|
|
11
|
+
- public `mcp-event-intelligence/provider` package export;
|
|
12
|
+
- callback-backed `createMcpEventsProvider()` for stateless provider runtimes;
|
|
13
|
+
- generic experimental Events capability advertisement plus `server/discover`, `events/list`, and `events/poll` dispatch;
|
|
14
|
+
- provider-owned opaque cursors, authentication context, data queries and domain event semantics;
|
|
15
|
+
- fail-closed EventOccurrence and payload-schema validation;
|
|
16
|
+
- lower-level store-backed `ExperimentalMcpEventsServer` remains available through the provider export;
|
|
17
|
+
- packed-artifact smoke coverage for the new subpath.
|
|
18
|
+
|
|
19
|
+
The Event Intelligence and composite-trigger protocol/schema versions remain `0.1.0` / `v0.1`; this patch adds a package integration surface without changing persisted protocol contracts.
|
|
20
|
+
|
|
5
21
|
## [0.1.0] - 2026-09-20
|
|
6
22
|
|
|
7
23
|
Initial public reference release.
|
package/README.md
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
> Durable temporal event intelligence for sleeping agents.
|
|
4
4
|
|
|
5
5
|
[](https://github.com/sarooo17/event-intelligence/actions/workflows/ci.yml)
|
|
6
|
+
[](https://www.npmjs.com/package/mcp-event-intelligence)
|
|
7
|
+
[](https://registry.modelcontextprotocol.io/?q=io.github.sarooo17%2Fevent-intelligence)
|
|
6
8
|
[](LICENSE)
|
|
7
9
|
|
|
8
10
|
MCP Event Intelligence is an experimental event runtime for agents that need to react to **future conditions over multiple event sources** without keeping an LLM or agent loop alive.
|
|
@@ -21,12 +23,14 @@ Event Intelligence persists that continuation independently of the model, waits
|
|
|
21
23
|
|
|
22
24
|
## Embed it in an existing agent host
|
|
23
25
|
|
|
24
|
-
|
|
26
|
+
Install from npm:
|
|
25
27
|
|
|
26
28
|
```bash
|
|
27
29
|
npm install mcp-event-intelligence
|
|
28
30
|
```
|
|
29
31
|
|
|
32
|
+
Published package: [npm](https://www.npmjs.com/package/mcp-event-intelligence) · [Official MCP Registry](https://registry.modelcontextprotocol.io/?q=io.github.sarooo17%2Fevent-intelligence)
|
|
33
|
+
|
|
30
34
|
Pass the harness-level MCP registry once — not every MCP one by one:
|
|
31
35
|
|
|
32
36
|
```js
|
|
@@ -50,6 +54,44 @@ const ei = await createEventIntelligenceHost({
|
|
|
50
54
|
|
|
51
55
|
Event Intelligence enumerates the host registry automatically. GitHub, Gmail, private/company MCPs and future connections do not need to be configured again inside EI. Tools-only MCPs remain available to the agent and are ignored by the Events layer; Events-capable MCPs are attached automatically.
|
|
52
56
|
|
|
57
|
+
|
|
58
|
+
## Add Events to an MCP provider
|
|
59
|
+
|
|
60
|
+
Providers can expose the experimental Events boundary without reimplementing the generic JSON-RPC glue:
|
|
61
|
+
|
|
62
|
+
```js
|
|
63
|
+
import { createMcpEventsProvider } from 'mcp-event-intelligence/provider';
|
|
64
|
+
|
|
65
|
+
const events = createMcpEventsProvider({
|
|
66
|
+
events: [
|
|
67
|
+
{
|
|
68
|
+
descriptor: {
|
|
69
|
+
name: 'erpnext.sales_invoice.submitted',
|
|
70
|
+
description: 'A submitted Sales Invoice was observed.',
|
|
71
|
+
delivery: ['poll'],
|
|
72
|
+
inputSchema: { type: 'object' },
|
|
73
|
+
payloadSchema: {
|
|
74
|
+
type: 'object',
|
|
75
|
+
required: ['name', 'company', 'grand_total'],
|
|
76
|
+
properties: {
|
|
77
|
+
name: { type: 'string' },
|
|
78
|
+
company: { type: 'string' },
|
|
79
|
+
grand_total: { type: 'number' },
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
poll: async ({ cursor, maxEvents, context }) => {
|
|
84
|
+
return providerRuntime.pollInvoices({ cursor, maxEvents, context });
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
});
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The package owns capability advertisement, `server/discover`, `events/list`, `events/poll`, common validation and response shapes. The provider owns domain event definitions, authentication, data queries, occurrence IDs and opaque cursor semantics.
|
|
92
|
+
|
|
93
|
+
This adapter remains experimental compatibility work around MCP Events; it is not a claim of finalized MCP Events conformance.
|
|
94
|
+
|
|
53
95
|
## What v0.1 implements
|
|
54
96
|
|
|
55
97
|
### Host-owned event sources
|
|
@@ -166,7 +208,7 @@ A separate live regression also verified real GitHub webhook ingress into the MC
|
|
|
166
208
|
|
|
167
209
|
```bash
|
|
168
210
|
git clone https://github.com/sarooo17/event-intelligence.git
|
|
169
|
-
cd
|
|
211
|
+
cd event-intelligence
|
|
170
212
|
npm ci
|
|
171
213
|
npm run check
|
|
172
214
|
|
|
@@ -4,7 +4,7 @@ const args = process.argv.slice(2);
|
|
|
4
4
|
const flags = new Set(args);
|
|
5
5
|
|
|
6
6
|
if (flags.has('--version') || flags.has('-v')) {
|
|
7
|
-
console.log('0.1.
|
|
7
|
+
console.log('0.1.1');
|
|
8
8
|
process.exit(0);
|
|
9
9
|
}
|
|
10
10
|
|
|
@@ -39,6 +39,10 @@ Embedded hosts:
|
|
|
39
39
|
import { createEventIntelligenceHost } from 'mcp-event-intelligence/host'
|
|
40
40
|
Pass the harness MCP registry once; EI discovers Events-capable clients automatically.
|
|
41
41
|
|
|
42
|
+
Provider runtimes:
|
|
43
|
+
import { createMcpEventsProvider } from 'mcp-event-intelligence/provider'
|
|
44
|
+
Keep domain events, auth, data access and opaque cursors provider-owned.
|
|
45
|
+
|
|
42
46
|
Important:
|
|
43
47
|
stdout is reserved for MCP JSON-RPC in stdio mode.
|
|
44
48
|
`);
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { EventDescriptor, EventOccurrence, JsonRpcRequest, JsonRpcResponse } from '../protocol/types.js';
|
|
2
|
+
export declare const MCP_EVENTS_CAPABILITY_KEY: "io.modelcontextprotocol.experimental/events";
|
|
3
|
+
export declare const MCP_EVENTS_CAPABILITY: Readonly<{
|
|
4
|
+
status: "draft";
|
|
5
|
+
designDate: "2026-02-19";
|
|
6
|
+
methods: readonly ["events/list", "events/poll"];
|
|
7
|
+
listChanged: false;
|
|
8
|
+
}>;
|
|
9
|
+
export interface ProviderPollRequest<TContext = unknown> {
|
|
10
|
+
name: string;
|
|
11
|
+
arguments: Record<string, unknown>;
|
|
12
|
+
cursor: string | null;
|
|
13
|
+
maxAgeMs?: number;
|
|
14
|
+
maxEvents: number;
|
|
15
|
+
context?: TContext;
|
|
16
|
+
}
|
|
17
|
+
export interface ProviderPollResult {
|
|
18
|
+
events: EventOccurrence[];
|
|
19
|
+
cursor: string;
|
|
20
|
+
truncated?: boolean;
|
|
21
|
+
hasMore?: boolean;
|
|
22
|
+
nextPollMs?: number;
|
|
23
|
+
}
|
|
24
|
+
export interface McpProviderEvent<TContext = unknown> {
|
|
25
|
+
descriptor: EventDescriptor;
|
|
26
|
+
poll: (request: ProviderPollRequest<TContext>) => ProviderPollResult | Promise<ProviderPollResult>;
|
|
27
|
+
}
|
|
28
|
+
export interface CreateMcpEventsProviderOptions<TContext = unknown> {
|
|
29
|
+
events: McpProviderEvent<TContext>[];
|
|
30
|
+
supportedVersions?: string[];
|
|
31
|
+
instructions?: string;
|
|
32
|
+
}
|
|
33
|
+
export declare function assertJsonSchemaValue(schemaInput: Record<string, unknown>, value: unknown, path?: string): void;
|
|
34
|
+
export declare class McpEventsProvider<TContext = unknown> {
|
|
35
|
+
private readonly events;
|
|
36
|
+
readonly supportedVersions: string[];
|
|
37
|
+
readonly instructions: string;
|
|
38
|
+
constructor(options: CreateMcpEventsProviderOptions<TContext>);
|
|
39
|
+
canHandle(method: string): boolean;
|
|
40
|
+
listEvents(): EventDescriptor[];
|
|
41
|
+
handleRequest(request: JsonRpcRequest, context?: TContext): Promise<JsonRpcResponse>;
|
|
42
|
+
private poll;
|
|
43
|
+
private ok;
|
|
44
|
+
private error;
|
|
45
|
+
}
|
|
46
|
+
export declare function createMcpEventsProvider<TContext = unknown>(options: CreateMcpEventsProviderOptions<TContext>): McpEventsProvider<TContext>;
|
|
47
|
+
export { ExperimentalMcpEventsServer, decodeMcpEventCursor, encodeMcpEventCursor, } from './server.js';
|
|
48
|
+
export type { McpEventOccurrenceStore, PersistedMcpOccurrence, PollEventsParams, PollEventsResult, RegisteredMcpEvent, } from './server.js';
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import { McpEventOccurrenceSchema, } from '../intelligenceProtocol/schemas.js';
|
|
2
|
+
export const MCP_EVENTS_CAPABILITY_KEY = 'io.modelcontextprotocol.experimental/events';
|
|
3
|
+
export const MCP_EVENTS_CAPABILITY = Object.freeze({
|
|
4
|
+
status: 'draft',
|
|
5
|
+
designDate: '2026-02-19',
|
|
6
|
+
methods: ['events/list', 'events/poll'],
|
|
7
|
+
listChanged: false,
|
|
8
|
+
});
|
|
9
|
+
const ALLOWED_DELIVERY = new Set(['poll', 'push', 'webhook']);
|
|
10
|
+
function assertRecord(value, message) {
|
|
11
|
+
if (!value || Array.isArray(value) || typeof value !== 'object') {
|
|
12
|
+
throw new Error(message);
|
|
13
|
+
}
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
function optionalFiniteNumber(value, name) {
|
|
17
|
+
if (value === undefined)
|
|
18
|
+
return undefined;
|
|
19
|
+
if (typeof value !== 'number' || !Number.isFinite(value) || value < 0) {
|
|
20
|
+
throw new Error(`${name} must be a non-negative finite number`);
|
|
21
|
+
}
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
function normalizeDescriptor(input) {
|
|
25
|
+
const raw = assertRecord(input, 'MCP event descriptor must be an object');
|
|
26
|
+
const name = String(raw.name ?? '').trim();
|
|
27
|
+
if (!name)
|
|
28
|
+
throw new Error('MCP event descriptor requires name');
|
|
29
|
+
const deliveryRaw = Array.isArray(raw.delivery) ? raw.delivery : ['poll'];
|
|
30
|
+
const delivery = deliveryRaw.map((entry) => String(entry));
|
|
31
|
+
if (delivery.length === 0 ||
|
|
32
|
+
delivery.some((entry) => !ALLOWED_DELIVERY.has(entry))) {
|
|
33
|
+
throw new Error(`MCP event descriptor ${name} has invalid delivery modes`);
|
|
34
|
+
}
|
|
35
|
+
if (!delivery.includes('poll')) {
|
|
36
|
+
throw new Error(`MCP provider event ${name} must advertise poll delivery in v0.1`);
|
|
37
|
+
}
|
|
38
|
+
const inputSchema = raw.inputSchema === undefined
|
|
39
|
+
? {}
|
|
40
|
+
: assertRecord(raw.inputSchema, `MCP event descriptor ${name} inputSchema must be an object`);
|
|
41
|
+
const payloadSchema = raw.payloadSchema === undefined
|
|
42
|
+
? {}
|
|
43
|
+
: assertRecord(raw.payloadSchema, `MCP event descriptor ${name} payloadSchema must be an object`);
|
|
44
|
+
return {
|
|
45
|
+
name,
|
|
46
|
+
description: String(raw.description ?? ''),
|
|
47
|
+
delivery: delivery,
|
|
48
|
+
inputSchema,
|
|
49
|
+
payloadSchema,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function schemaTypes(schema) {
|
|
53
|
+
if (Array.isArray(schema.type)) {
|
|
54
|
+
return schema.type.map((entry) => String(entry));
|
|
55
|
+
}
|
|
56
|
+
return schema.type === undefined ? [] : [String(schema.type)];
|
|
57
|
+
}
|
|
58
|
+
export function assertJsonSchemaValue(schemaInput, value, path = '$') {
|
|
59
|
+
const schema = assertRecord(schemaInput, `Invalid JSON Schema at ${path}`);
|
|
60
|
+
if (Object.prototype.hasOwnProperty.call(schema, 'const') &&
|
|
61
|
+
!Object.is(schema.const, value)) {
|
|
62
|
+
throw new Error(`MCP event payload violates schema at ${path}: const mismatch`);
|
|
63
|
+
}
|
|
64
|
+
if (Array.isArray(schema.enum) &&
|
|
65
|
+
!schema.enum.some((entry) => Object.is(entry, value))) {
|
|
66
|
+
throw new Error(`MCP event payload violates schema at ${path}: value not in enum`);
|
|
67
|
+
}
|
|
68
|
+
const types = schemaTypes(schema);
|
|
69
|
+
if (types.length > 0) {
|
|
70
|
+
const matches = types.some((type) => {
|
|
71
|
+
if (type === 'null')
|
|
72
|
+
return value === null;
|
|
73
|
+
if (type === 'array')
|
|
74
|
+
return Array.isArray(value);
|
|
75
|
+
if (type === 'object') {
|
|
76
|
+
return value !== null && !Array.isArray(value) && typeof value === 'object';
|
|
77
|
+
}
|
|
78
|
+
if (type === 'integer')
|
|
79
|
+
return Number.isInteger(value);
|
|
80
|
+
if (type === 'number') {
|
|
81
|
+
return typeof value === 'number' && Number.isFinite(value);
|
|
82
|
+
}
|
|
83
|
+
if (type === 'string')
|
|
84
|
+
return typeof value === 'string';
|
|
85
|
+
if (type === 'boolean')
|
|
86
|
+
return typeof value === 'boolean';
|
|
87
|
+
return false;
|
|
88
|
+
});
|
|
89
|
+
if (!matches) {
|
|
90
|
+
throw new Error(`MCP event payload violates schema at ${path}: expected ${types.join('|')}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (typeof value === 'string') {
|
|
94
|
+
if (typeof schema.minLength === 'number' && value.length < schema.minLength) {
|
|
95
|
+
throw new Error(`MCP event payload violates schema at ${path}: string too short`);
|
|
96
|
+
}
|
|
97
|
+
if (typeof schema.maxLength === 'number' && value.length > schema.maxLength) {
|
|
98
|
+
throw new Error(`MCP event payload violates schema at ${path}: string too long`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
102
|
+
if (typeof schema.minimum === 'number' && value < schema.minimum) {
|
|
103
|
+
throw new Error(`MCP event payload violates schema at ${path}: below minimum`);
|
|
104
|
+
}
|
|
105
|
+
if (typeof schema.maximum === 'number' && value > schema.maximum) {
|
|
106
|
+
throw new Error(`MCP event payload violates schema at ${path}: above maximum`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (value !== null && !Array.isArray(value) && typeof value === 'object') {
|
|
110
|
+
const record = value;
|
|
111
|
+
const required = Array.isArray(schema.required)
|
|
112
|
+
? schema.required.map((entry) => String(entry))
|
|
113
|
+
: [];
|
|
114
|
+
for (const key of required) {
|
|
115
|
+
if (!Object.prototype.hasOwnProperty.call(record, key)) {
|
|
116
|
+
throw new Error(`MCP event payload violates schema at ${path}: missing required property ${key}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const properties = schema.properties &&
|
|
120
|
+
!Array.isArray(schema.properties) &&
|
|
121
|
+
typeof schema.properties === 'object'
|
|
122
|
+
? schema.properties
|
|
123
|
+
: {};
|
|
124
|
+
for (const [key, childSchema] of Object.entries(properties)) {
|
|
125
|
+
if (Object.prototype.hasOwnProperty.call(record, key) &&
|
|
126
|
+
childSchema &&
|
|
127
|
+
!Array.isArray(childSchema) &&
|
|
128
|
+
typeof childSchema === 'object') {
|
|
129
|
+
assertJsonSchemaValue(childSchema, record[key], `${path}.${key}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (schema.additionalProperties === false) {
|
|
133
|
+
for (const key of Object.keys(record)) {
|
|
134
|
+
if (!Object.prototype.hasOwnProperty.call(properties, key)) {
|
|
135
|
+
throw new Error(`MCP event payload violates schema at ${path}: unexpected property ${key}`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (Array.isArray(value) &&
|
|
141
|
+
schema.items &&
|
|
142
|
+
!Array.isArray(schema.items) &&
|
|
143
|
+
typeof schema.items === 'object') {
|
|
144
|
+
value.forEach((entry, index) => assertJsonSchemaValue(schema.items, entry, `${path}[${index}]`));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
function normalizePollParams(paramsInput) {
|
|
148
|
+
const params = assertRecord(paramsInput ?? {}, 'events/poll params must be an object');
|
|
149
|
+
const name = String(params.name ?? '').trim();
|
|
150
|
+
if (!name)
|
|
151
|
+
throw new Error('events/poll requires name');
|
|
152
|
+
const cursor = params.cursor === undefined || params.cursor === null
|
|
153
|
+
? null
|
|
154
|
+
: String(params.cursor);
|
|
155
|
+
const argumentsValue = params.arguments === undefined
|
|
156
|
+
? {}
|
|
157
|
+
: assertRecord(params.arguments, 'events/poll arguments must be an object');
|
|
158
|
+
const maxAgeMs = optionalFiniteNumber(params.maxAgeMs, 'maxAgeMs');
|
|
159
|
+
const maxEventsRaw = params.maxEvents ?? 50;
|
|
160
|
+
if (typeof maxEventsRaw !== 'number' ||
|
|
161
|
+
!Number.isInteger(maxEventsRaw) ||
|
|
162
|
+
maxEventsRaw < 1 ||
|
|
163
|
+
maxEventsRaw > 500) {
|
|
164
|
+
throw new Error('maxEvents must be an integer between 1 and 500');
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
name,
|
|
168
|
+
arguments: argumentsValue,
|
|
169
|
+
cursor,
|
|
170
|
+
...(maxAgeMs === undefined ? {} : { maxAgeMs }),
|
|
171
|
+
maxEvents: maxEventsRaw,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
export class McpEventsProvider {
|
|
175
|
+
events = new Map();
|
|
176
|
+
supportedVersions;
|
|
177
|
+
instructions;
|
|
178
|
+
constructor(options) {
|
|
179
|
+
if (!options || !Array.isArray(options.events)) {
|
|
180
|
+
throw new Error('MCP Events provider requires events[]');
|
|
181
|
+
}
|
|
182
|
+
for (const input of options.events) {
|
|
183
|
+
if (!input || typeof input.poll !== 'function') {
|
|
184
|
+
throw new Error('MCP provider event requires poll()');
|
|
185
|
+
}
|
|
186
|
+
const descriptor = normalizeDescriptor(input.descriptor);
|
|
187
|
+
if (this.events.has(descriptor.name)) {
|
|
188
|
+
throw new Error(`Duplicate MCP provider event: ${descriptor.name}`);
|
|
189
|
+
}
|
|
190
|
+
this.events.set(descriptor.name, {
|
|
191
|
+
descriptor,
|
|
192
|
+
poll: input.poll,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
this.supportedVersions =
|
|
196
|
+
options.supportedVersions?.map(String).filter(Boolean) ?? ['2026-07-28'];
|
|
197
|
+
this.instructions =
|
|
198
|
+
options.instructions ??
|
|
199
|
+
'Experimental MCP Events provider compatibility adapter. Events follow the Triggers & Events design sketch; this is not finalized MCP conformance.';
|
|
200
|
+
}
|
|
201
|
+
canHandle(method) {
|
|
202
|
+
return (method === 'server/discover' ||
|
|
203
|
+
method === 'events/list' ||
|
|
204
|
+
method === 'events/poll');
|
|
205
|
+
}
|
|
206
|
+
listEvents() {
|
|
207
|
+
return [...this.events.values()].map(({ descriptor }) => descriptor);
|
|
208
|
+
}
|
|
209
|
+
async handleRequest(request, context) {
|
|
210
|
+
try {
|
|
211
|
+
switch (request.method) {
|
|
212
|
+
case 'server/discover':
|
|
213
|
+
return this.ok(request.id, {
|
|
214
|
+
supportedVersions: this.supportedVersions,
|
|
215
|
+
capabilities: {
|
|
216
|
+
experimental: {
|
|
217
|
+
[MCP_EVENTS_CAPABILITY_KEY]: MCP_EVENTS_CAPABILITY,
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
instructions: this.instructions,
|
|
221
|
+
});
|
|
222
|
+
case 'events/list':
|
|
223
|
+
return this.ok(request.id, {
|
|
224
|
+
events: this.listEvents(),
|
|
225
|
+
});
|
|
226
|
+
case 'events/poll':
|
|
227
|
+
return this.ok(request.id, await this.poll(request.params, context));
|
|
228
|
+
default:
|
|
229
|
+
return this.error(request.id, -32601, `Method not found: ${request.method}`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
catch (error) {
|
|
233
|
+
return this.error(request.id, -32602, error instanceof Error ? error.message : 'Invalid params');
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
async poll(paramsInput, context) {
|
|
237
|
+
const params = normalizePollParams(paramsInput);
|
|
238
|
+
const registered = this.events.get(params.name);
|
|
239
|
+
if (!registered) {
|
|
240
|
+
throw new Error(`Unknown event: ${params.name}`);
|
|
241
|
+
}
|
|
242
|
+
const result = assertRecord(await registered.poll({
|
|
243
|
+
...params,
|
|
244
|
+
context,
|
|
245
|
+
}), `MCP provider ${params.name} returned invalid poll result`);
|
|
246
|
+
if (typeof result.cursor !== 'string') {
|
|
247
|
+
throw new Error(`MCP provider ${params.name} returned poll result without string cursor`);
|
|
248
|
+
}
|
|
249
|
+
if (!Array.isArray(result.events)) {
|
|
250
|
+
throw new Error(`MCP provider ${params.name} returned poll result without events[]`);
|
|
251
|
+
}
|
|
252
|
+
if (result.events.length > params.maxEvents) {
|
|
253
|
+
throw new Error(`MCP provider ${params.name} returned more than maxEvents=${params.maxEvents}`);
|
|
254
|
+
}
|
|
255
|
+
const events = result.events.map((raw) => {
|
|
256
|
+
const parsed = McpEventOccurrenceSchema.safeParse(raw);
|
|
257
|
+
if (!parsed.success) {
|
|
258
|
+
throw new Error(`MCP provider ${params.name} returned invalid EventOccurrence: ${parsed.error.message}`);
|
|
259
|
+
}
|
|
260
|
+
if (parsed.data.name !== params.name) {
|
|
261
|
+
throw new Error(`MCP provider event name mismatch: expected ${params.name}, got ${parsed.data.name}`);
|
|
262
|
+
}
|
|
263
|
+
assertJsonSchemaValue(registered.descriptor.payloadSchema, parsed.data.data);
|
|
264
|
+
return parsed.data;
|
|
265
|
+
});
|
|
266
|
+
const truncated = result.truncated ?? false;
|
|
267
|
+
const hasMore = result.hasMore ?? false;
|
|
268
|
+
if (typeof truncated !== 'boolean' || typeof hasMore !== 'boolean') {
|
|
269
|
+
throw new Error(`MCP provider ${params.name} returned invalid pagination flags`);
|
|
270
|
+
}
|
|
271
|
+
const nextPollMs = result.nextPollMs ?? 5000;
|
|
272
|
+
if (typeof nextPollMs !== 'number' ||
|
|
273
|
+
!Number.isInteger(nextPollMs) ||
|
|
274
|
+
nextPollMs < 0 ||
|
|
275
|
+
nextPollMs > 300000) {
|
|
276
|
+
throw new Error(`MCP provider ${params.name} returned invalid nextPollMs`);
|
|
277
|
+
}
|
|
278
|
+
return {
|
|
279
|
+
events,
|
|
280
|
+
cursor: result.cursor,
|
|
281
|
+
truncated,
|
|
282
|
+
hasMore,
|
|
283
|
+
nextPollMs,
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
ok(id, result) {
|
|
287
|
+
return { jsonrpc: '2.0', id, result };
|
|
288
|
+
}
|
|
289
|
+
error(id, code, message) {
|
|
290
|
+
return { jsonrpc: '2.0', id, error: { code, message } };
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
export function createMcpEventsProvider(options) {
|
|
294
|
+
return new McpEventsProvider(options);
|
|
295
|
+
}
|
|
296
|
+
export { ExperimentalMcpEventsServer, decodeMcpEventCursor, encodeMcpEventCursor, } from './server.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-event-intelligence",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Durable temporal event intelligence for agent hosts: host-owned MCP event discovery, composite triggers, derived events, and targeted wakeups.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -14,6 +14,10 @@
|
|
|
14
14
|
"./host": {
|
|
15
15
|
"types": "./scripts/host-integration.d.mts",
|
|
16
16
|
"import": "./scripts/host-integration.mjs"
|
|
17
|
+
},
|
|
18
|
+
"./provider": {
|
|
19
|
+
"types": "./dist/src/mcpEvents/provider.d.ts",
|
|
20
|
+
"import": "./dist/src/mcpEvents/provider.js"
|
|
17
21
|
}
|
|
18
22
|
},
|
|
19
23
|
"bin": {
|
package/server.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "io.github.sarooo17/event-intelligence",
|
|
4
4
|
"title": "MCP Event Intelligence",
|
|
5
5
|
"description": "Durable temporal event intelligence: composite triggers, derived events, and runtime wakeups.",
|
|
6
|
-
"version": "0.1.
|
|
6
|
+
"version": "0.1.1",
|
|
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.1.
|
|
16
|
+
"version": "0.1.1",
|
|
17
17
|
"transport": {
|
|
18
18
|
"type": "stdio"
|
|
19
19
|
},
|