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,65 @@
1
+ function getByPath(source, path) {
2
+ const parts = path.split('.');
3
+ let current = source;
4
+ for (const part of parts) {
5
+ if (current === null ||
6
+ typeof current !== 'object' ||
7
+ Array.isArray(current)) {
8
+ return undefined;
9
+ }
10
+ current = current[part];
11
+ }
12
+ return current;
13
+ }
14
+ export function projectSemanticInput(source, paths) {
15
+ const projected = {};
16
+ for (const path of paths) {
17
+ projected[path] = getByPath(source, path);
18
+ }
19
+ return projected;
20
+ }
21
+ export class SemanticConditionEngine {
22
+ evaluator;
23
+ constructor(evaluator) {
24
+ this.evaluator = evaluator;
25
+ }
26
+ async evaluate(source, condition) {
27
+ const result = await this.evaluator.evaluate({
28
+ instruction: condition.instruction,
29
+ input: projectSemanticInput(source, condition.input),
30
+ });
31
+ if (!Number.isFinite(result.probability) ||
32
+ result.probability < 0 ||
33
+ result.probability > 1) {
34
+ throw new Error('Semantic evaluator probability must be between 0 and 1');
35
+ }
36
+ const shared = {
37
+ probability: result.probability,
38
+ evaluator: result.evaluator,
39
+ condition,
40
+ ...(result.metadata ? { metadata: result.metadata } : {}),
41
+ };
42
+ if (result.probability >= condition.matchThreshold) {
43
+ return {
44
+ outcome: 'match',
45
+ matched: true,
46
+ shouldEscalate: false,
47
+ ...shared,
48
+ };
49
+ }
50
+ if (result.probability <= condition.rejectThreshold) {
51
+ return {
52
+ outcome: 'reject',
53
+ matched: false,
54
+ shouldEscalate: false,
55
+ ...shared,
56
+ };
57
+ }
58
+ return {
59
+ outcome: 'uncertain',
60
+ matched: condition.uncertain === 'match',
61
+ shouldEscalate: condition.uncertain === 'escalate',
62
+ ...shared,
63
+ };
64
+ }
65
+ }
@@ -0,0 +1,31 @@
1
+ export type UncertainPolicy = 'escalate' | 'reject' | 'match';
2
+ export interface SemanticBooleanCondition {
3
+ type: 'semantic_boolean';
4
+ instruction: string;
5
+ input: string[];
6
+ matchThreshold: number;
7
+ rejectThreshold: number;
8
+ uncertain: UncertainPolicy;
9
+ }
10
+ export interface SemanticEvaluationRequest {
11
+ instruction: string;
12
+ input: Record<string, unknown>;
13
+ }
14
+ export interface SemanticEvaluation {
15
+ evaluator: string;
16
+ probability: number;
17
+ metadata?: Record<string, unknown>;
18
+ }
19
+ export interface SemanticEvaluator {
20
+ evaluate(request: SemanticEvaluationRequest): Promise<SemanticEvaluation>;
21
+ }
22
+ export type SemanticOutcome = 'match' | 'reject' | 'uncertain';
23
+ export interface SemanticDecision {
24
+ outcome: SemanticOutcome;
25
+ matched: boolean;
26
+ shouldEscalate: boolean;
27
+ probability: number;
28
+ evaluator: string;
29
+ condition: SemanticBooleanCondition;
30
+ metadata?: Record<string, unknown>;
31
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,23 @@
1
+ import type { SemanticEvaluation, SemanticEvaluationRequest, SemanticEvaluator } from './types.js';
2
+ type FetchLike = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
3
+ export interface TypeSafeJevEvaluatorOptions {
4
+ apiKey: string;
5
+ model?: string;
6
+ baseUrl?: string;
7
+ fetchFn?: FetchLike;
8
+ maxRetries?: number;
9
+ retryBaseMs?: number;
10
+ }
11
+ export declare class TypeSafeJevEvaluator implements SemanticEvaluator {
12
+ private readonly options;
13
+ private readonly model;
14
+ private readonly baseUrl;
15
+ private readonly fetchFn;
16
+ private readonly maxRetries;
17
+ private readonly retryBaseMs;
18
+ constructor(options: TypeSafeJevEvaluatorOptions);
19
+ static fromEnvironment(env: Record<string, string | undefined>): TypeSafeJevEvaluator;
20
+ evaluate(request: SemanticEvaluationRequest): Promise<SemanticEvaluation>;
21
+ private requestWithRetry;
22
+ }
23
+ export {};
@@ -0,0 +1,95 @@
1
+ export class TypeSafeJevEvaluator {
2
+ options;
3
+ model;
4
+ baseUrl;
5
+ fetchFn;
6
+ maxRetries;
7
+ retryBaseMs;
8
+ constructor(options) {
9
+ this.options = options;
10
+ if (!options.apiKey) {
11
+ throw new Error('TypeSafe API key is required');
12
+ }
13
+ this.model = options.model ?? 'jev-latest';
14
+ this.baseUrl = options.baseUrl ?? 'https://api.typesafe.ai/v1/systemone';
15
+ this.fetchFn = options.fetchFn ?? fetch;
16
+ this.maxRetries = options.maxRetries ?? 2;
17
+ this.retryBaseMs = options.retryBaseMs ?? 100;
18
+ }
19
+ static fromEnvironment(env) {
20
+ const apiKey = env.TYPESAFE_API_KEY;
21
+ if (!apiKey) {
22
+ throw new Error('TYPESAFE_API_KEY is required');
23
+ }
24
+ return new TypeSafeJevEvaluator({
25
+ apiKey,
26
+ model: env.TYPESAFE_MODEL ?? 'jev-latest',
27
+ });
28
+ }
29
+ async evaluate(request) {
30
+ const payload = {
31
+ state: request.input,
32
+ model: this.model,
33
+ questions: {
34
+ match: {
35
+ type: 'noul',
36
+ instructions: request.instruction,
37
+ },
38
+ },
39
+ };
40
+ const response = await this.requestWithRetry(payload);
41
+ const answer = response.body.answers?.match;
42
+ if (!answer ||
43
+ answer.type !== 'noul' ||
44
+ !Number.isFinite(answer.noul) ||
45
+ answer.noul < 0 ||
46
+ answer.noul > 1) {
47
+ throw new Error('Invalid Jev noul response');
48
+ }
49
+ return {
50
+ evaluator: `typesafe/${response.body.model}`,
51
+ probability: answer.noul,
52
+ metadata: {
53
+ requestedModel: this.model,
54
+ resolvedModel: response.body.model,
55
+ inputTokens: response.body.usage?.input_tokens,
56
+ outputTokens: response.body.usage?.output_tokens,
57
+ cost: response.body.usage?.cost,
58
+ httpStatus: response.httpStatus,
59
+ requestId: response.requestId,
60
+ },
61
+ };
62
+ }
63
+ async requestWithRetry(payload) {
64
+ let attempt = 0;
65
+ while (true) {
66
+ const response = await this.fetchFn(this.baseUrl, {
67
+ method: 'POST',
68
+ headers: {
69
+ Authorization: `Bearer ${this.options.apiKey}`,
70
+ 'Content-Type': 'application/json',
71
+ },
72
+ body: JSON.stringify(payload),
73
+ });
74
+ if (response.ok) {
75
+ const body = (await response.json());
76
+ const requestId = response.headers.get('x-request-id') ??
77
+ response.headers.get('request-id') ??
78
+ response.headers.get('x-amzn-requestid') ??
79
+ undefined;
80
+ return {
81
+ body,
82
+ httpStatus: response.status,
83
+ requestId,
84
+ };
85
+ }
86
+ const retryable = response.status === 429 || response.status === 529;
87
+ if (!retryable || attempt >= this.maxRetries) {
88
+ throw new Error(`TypeSafe Jev request failed with HTTP ${response.status}`);
89
+ }
90
+ const waitMs = this.retryBaseMs * 2 ** attempt;
91
+ await new Promise((resolve) => setTimeout(resolve, waitMs));
92
+ attempt += 1;
93
+ }
94
+ }
95
+ }
package/package.json ADDED
@@ -0,0 +1,85 @@
1
+ {
2
+ "name": "mcp-event-intelligence",
3
+ "version": "0.1.0",
4
+ "description": "Durable temporal event intelligence for agent hosts: host-owned MCP event discovery, composite triggers, derived events, and targeted wakeups.",
5
+ "license": "Apache-2.0",
6
+ "type": "module",
7
+ "main": "./dist/src/intelligenceProtocol/index.js",
8
+ "types": "./dist/src/intelligenceProtocol/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/src/intelligenceProtocol/index.d.ts",
12
+ "import": "./dist/src/intelligenceProtocol/index.js"
13
+ },
14
+ "./host": {
15
+ "types": "./scripts/host-integration.d.mts",
16
+ "import": "./scripts/host-integration.mjs"
17
+ }
18
+ },
19
+ "bin": {
20
+ "mcp-event-intelligence": "./bin/mcp-event-intelligence.mjs"
21
+ },
22
+ "files": [
23
+ "bin/",
24
+ "dist/",
25
+ "scripts/service.mjs",
26
+ "scripts/lib/",
27
+ "README.md",
28
+ "LICENSE",
29
+ "SECURITY.md",
30
+ "CHANGELOG.md",
31
+ "server.json",
32
+ "scripts/mcp-stdio.mjs",
33
+ "scripts/mcp-stdio-server.mjs",
34
+ "scripts/host-integration.mjs",
35
+ "scripts/host-integration.d.mts",
36
+ "scripts/run-tests.mjs"
37
+ ],
38
+ "scripts": {
39
+ "build": "tsc -p tsconfig.json",
40
+ "test": "npm run build && node scripts/run-tests.mjs",
41
+ "check": "npm run test",
42
+ "pack:check": "npm pack --dry-run",
43
+ "prepack": "npm run build",
44
+ "start": "node scripts/service.mjs",
45
+ "protocol:check": "npm run check",
46
+ "mcp": "npm run build && node scripts/mcp-stdio.mjs",
47
+ "mcp:inspect": "npm run build && npx @modelcontextprotocol/inspector node scripts/mcp-stdio.mjs",
48
+ "registry:validate": "mcp-publisher validate server.json",
49
+ "pack:mcp-smoke": "node scripts/package-mcp-smoke.mjs"
50
+ },
51
+ "keywords": [
52
+ "mcp",
53
+ "model-context-protocol",
54
+ "ai-agents",
55
+ "event-driven",
56
+ "temporal",
57
+ "durable-execution",
58
+ "triggers",
59
+ "derived-events",
60
+ "agent-runtime"
61
+ ],
62
+ "repository": {
63
+ "type": "git",
64
+ "url": "git+https://github.com/sarooo17/event-intelligence.git"
65
+ },
66
+ "bugs": {
67
+ "url": "https://github.com/sarooo17/event-intelligence/issues"
68
+ },
69
+ "homepage": "https://github.com/sarooo17/event-intelligence#readme",
70
+ "publishConfig": {
71
+ "access": "public"
72
+ },
73
+ "dependencies": {
74
+ "zod": "4.6.5",
75
+ "@modelcontextprotocol/server": "2.0.0"
76
+ },
77
+ "engines": {
78
+ "node": ">=22"
79
+ },
80
+ "devDependencies": {
81
+ "typescript": "6.0.3",
82
+ "@modelcontextprotocol/client": "2.0.0"
83
+ },
84
+ "mcpName": "io.github.sarooo17/event-intelligence"
85
+ }
@@ -0,0 +1,86 @@
1
+ export interface HostMcpClientLike {
2
+ request(
3
+ request: { method: string; params?: unknown },
4
+ resultSchema?: unknown,
5
+ ): Promise<unknown>;
6
+ getServerCapabilities?(): unknown;
7
+ }
8
+
9
+ export interface HostMcpConnectionOptions {
10
+ connectionId?: string;
11
+ id?: string;
12
+ name?: string;
13
+ serverId?: string;
14
+ client?: HostMcpClientLike;
15
+ request?: (method: string, params?: unknown) => Promise<unknown>;
16
+ enabled?: boolean;
17
+ pollIntervalMs?: number;
18
+ maxEvents?: number;
19
+ }
20
+
21
+ export interface HostMcpRegistry {
22
+ listConnections?(): Iterable<HostMcpConnectionOptions> | Promise<Iterable<HostMcpConnectionOptions>>;
23
+ list?(): Iterable<HostMcpConnectionOptions> | Promise<Iterable<HostMcpConnectionOptions>>;
24
+ connections?(): Iterable<HostMcpConnectionOptions> | Promise<Iterable<HostMcpConnectionOptions>>;
25
+ subscribe?(listener: () => void): void | (() => void);
26
+ [Symbol.iterator]?(): Iterator<HostMcpConnectionOptions>;
27
+ }
28
+
29
+ export interface HostWakeReceipt {
30
+ runtimeReceiptId: string;
31
+ duplicate?: boolean;
32
+ status?: string;
33
+ }
34
+
35
+ export type HostWakeHandler = (
36
+ packet: Record<string, unknown>,
37
+ ) => Promise<HostWakeReceipt | string> | HostWakeReceipt | string;
38
+
39
+ export interface EventIntelligenceHostOptions {
40
+ dataDir?: string;
41
+ env?: Record<string, string | undefined>;
42
+ mcpRegistry?: HostMcpRegistry;
43
+ /** Low-level/manual fallback; prefer mcpRegistry for automatic discovery. */
44
+ mcpClients?: HostMcpConnectionOptions[];
45
+ /** Generic harness dispatcher. target.id identifies the task/session/agent to resume. */
46
+ wake?: HostWakeHandler;
47
+ /** Optional runtime-specific dispatchers. */
48
+ wakeHandlers?: Map<string, HostWakeHandler> | Record<string, HostWakeHandler>;
49
+ semanticEvaluator?: unknown;
50
+ }
51
+
52
+ export interface EventIntelligenceHost {
53
+ readonly runtime: any;
54
+ readonly store: any;
55
+ readonly triggerControl: any;
56
+ readonly triggerInspector: any;
57
+ readonly eventSources: any[];
58
+ refreshMcpRegistry(): Promise<any[]>;
59
+ attachMcpClient(connection: HostMcpConnectionOptions): Promise<{
60
+ connectionId: string;
61
+ events: any[];
62
+ }>;
63
+ detachMcpClient(connectionId: string): Promise<boolean>;
64
+ mcpStatus(): any[];
65
+ close(): Promise<void>;
66
+ }
67
+
68
+ export function createMcpRegistryAdapter(options: {
69
+ listConnections(): Iterable<HostMcpConnectionOptions> | Promise<Iterable<HostMcpConnectionOptions>>;
70
+ subscribe?: (listener: () => void) => void | (() => void);
71
+ }): HostMcpRegistry;
72
+
73
+ export function createHostMcpEventsConnection(
74
+ options: HostMcpConnectionOptions,
75
+ ): {
76
+ connectionId: string;
77
+ serverId: string;
78
+ request(method: string, params?: unknown): Promise<unknown>;
79
+ enabled: boolean;
80
+ pollIntervalMs: number;
81
+ maxEvents: number;
82
+ };
83
+
84
+ export function createEventIntelligenceHost(
85
+ options?: EventIntelligenceHostOptions,
86
+ ): Promise<EventIntelligenceHost>;
@@ -0,0 +1,216 @@
1
+ import {
2
+ createLocalEventIntelligenceRuntime,
3
+ } from './lib/local-event-intelligence-runtime.mjs';
4
+ import {
5
+ createHostMcpEventsConnection,
6
+ } from './lib/mcp-events-client.mjs';
7
+
8
+ function normalizeMcpConnection(input) {
9
+ const connectionId = String(
10
+ input?.connectionId ?? input?.id ?? input?.name ?? '',
11
+ ).trim();
12
+ if (!connectionId) {
13
+ throw new Error('Host MCP registry entry requires connectionId/id/name');
14
+ }
15
+
16
+ return createHostMcpEventsConnection({
17
+ ...input,
18
+ connectionId,
19
+ serverId: input?.serverId ?? connectionId,
20
+ client: input?.client ?? (
21
+ input && typeof input.request === 'function' ? input : undefined
22
+ ),
23
+ });
24
+ }
25
+
26
+ async function listRegistryConnections(registry) {
27
+ if (!registry) return [];
28
+
29
+ if (typeof registry.listConnections === 'function') {
30
+ return Array.from(await registry.listConnections());
31
+ }
32
+ if (typeof registry.list === 'function') {
33
+ return Array.from(await registry.list());
34
+ }
35
+ if (typeof registry.connections === 'function') {
36
+ return Array.from(await registry.connections());
37
+ }
38
+ if (Array.isArray(registry)) return registry;
39
+ if (typeof registry[Symbol.iterator] === 'function') {
40
+ return Array.from(registry);
41
+ }
42
+
43
+ throw new Error(
44
+ 'mcpRegistry must expose listConnections(), list(), connections(), or be iterable',
45
+ );
46
+ }
47
+
48
+ export function createMcpRegistryAdapter({
49
+ listConnections,
50
+ subscribe,
51
+ } = {}) {
52
+ if (typeof listConnections !== 'function') {
53
+ throw new Error('MCP registry adapter requires listConnections()');
54
+ }
55
+ if (subscribe !== undefined && typeof subscribe !== 'function') {
56
+ throw new Error('MCP registry adapter subscribe must be a function');
57
+ }
58
+ return {
59
+ listConnections,
60
+ ...(subscribe ? { subscribe } : {}),
61
+ };
62
+ }
63
+
64
+ /**
65
+ * Embed Event Intelligence once at the harness/host level.
66
+ *
67
+ * The host keeps ownership of MCP transports, auth, tools and credentials.
68
+ * Event Intelligence discovers Events-capable clients from the host registry,
69
+ * ignores tools-only MCPs, and stores only event/cursor state.
70
+ */
71
+ export async function createEventIntelligenceHost({
72
+ dataDir,
73
+ env = {},
74
+ mcpRegistry,
75
+ mcpClients = [],
76
+ wake,
77
+ wakeHandlers = {},
78
+ semanticEvaluator,
79
+ } = {}) {
80
+ const explicitConnections = mcpClients.map(normalizeMcpConnection);
81
+ const runtime = await createLocalEventIntelligenceRuntime({
82
+ env: {
83
+ ...process.env,
84
+ ...env,
85
+ ...(dataDir ? { DATA_DIR: dataDir } : {}),
86
+ },
87
+ mcpEventConnections: explicitConnections,
88
+ wake,
89
+ wakeHandlers,
90
+ semanticEvaluator,
91
+ });
92
+
93
+ const registryManaged = new Set();
94
+ let unsubscribe = null;
95
+
96
+ const refreshMcpRegistry = async () => {
97
+ const entries = await listRegistryConnections(mcpRegistry);
98
+ const desired = new Map();
99
+
100
+ for (const entry of entries) {
101
+ const normalized = normalizeMcpConnection(entry);
102
+ desired.set(normalized.connectionId, normalized);
103
+ }
104
+
105
+ const outcomes = [];
106
+
107
+ for (const connectionId of [...registryManaged]) {
108
+ if (!desired.has(connectionId)) {
109
+ await runtime.mcpEventsClient.detachConnection(connectionId);
110
+ registryManaged.delete(connectionId);
111
+ outcomes.push({ connectionId, status: 'detached' });
112
+ }
113
+ }
114
+
115
+ for (const [connectionId, connection] of desired) {
116
+ const existing = runtime.mcpEventsClient.connections.get(connectionId);
117
+ if (existing) {
118
+ if (
119
+ existing.identity === connection.identity &&
120
+ existing.serverId === connection.serverId
121
+ ) {
122
+ registryManaged.add(connectionId);
123
+ outcomes.push({ connectionId, status: 'already_attached' });
124
+ continue;
125
+ }
126
+
127
+ await runtime.mcpEventsClient.detachConnection(connectionId);
128
+ registryManaged.delete(connectionId);
129
+ }
130
+
131
+ try {
132
+ const result = await runtime.mcpEventsClient.attachConnection(connection);
133
+ registryManaged.add(connectionId);
134
+ outcomes.push({
135
+ connectionId,
136
+ status: existing ? 'reattached' : 'attached',
137
+ events: result.events.map((event) => event.name),
138
+ });
139
+ } catch (error) {
140
+ await runtime.mcpEventsClient.detachConnection(connectionId);
141
+ if (
142
+ error &&
143
+ typeof error === 'object' &&
144
+ error.code === 'MCP_EVENTS_CAPABILITY_UNAVAILABLE'
145
+ ) {
146
+ outcomes.push({
147
+ connectionId,
148
+ status: 'ignored_no_events',
149
+ });
150
+ continue;
151
+ }
152
+ outcomes.push({
153
+ connectionId,
154
+ status: 'error',
155
+ error: error instanceof Error ? error.message : String(error),
156
+ });
157
+ }
158
+ }
159
+
160
+ return outcomes;
161
+ };
162
+
163
+ if (mcpRegistry) {
164
+ await refreshMcpRegistry();
165
+
166
+ if (typeof mcpRegistry.subscribe === 'function') {
167
+ const maybeUnsubscribe = mcpRegistry.subscribe(() => {
168
+ refreshMcpRegistry().catch((error) => {
169
+ console.error(JSON.stringify({
170
+ message: 'event_intelligence_mcp_registry_refresh_failed',
171
+ error: error instanceof Error ? error.message : String(error),
172
+ }));
173
+ });
174
+ });
175
+ if (typeof maybeUnsubscribe === 'function') {
176
+ unsubscribe = maybeUnsubscribe;
177
+ }
178
+ }
179
+ }
180
+
181
+ return {
182
+ runtime,
183
+ get store() {
184
+ return runtime.store;
185
+ },
186
+ get triggerControl() {
187
+ return runtime.triggerControl;
188
+ },
189
+ get triggerInspector() {
190
+ return runtime.triggerInspector;
191
+ },
192
+ get eventSources() {
193
+ return runtime.triggerControl.listEventSources();
194
+ },
195
+ refreshMcpRegistry,
196
+ async attachMcpClient(connection) {
197
+ const normalized = normalizeMcpConnection(connection);
198
+ return runtime.mcpEventsClient.attachConnection(normalized);
199
+ },
200
+ async detachMcpClient(connectionId) {
201
+ registryManaged.delete(connectionId);
202
+ return runtime.mcpEventsClient.detachConnection(connectionId);
203
+ },
204
+ mcpStatus() {
205
+ return runtime.mcpEventsClient.status();
206
+ },
207
+ async close() {
208
+ unsubscribe?.();
209
+ await runtime.close();
210
+ },
211
+ };
212
+ }
213
+
214
+ export {
215
+ createHostMcpEventsConnection,
216
+ };