@eventcatalog/core 3.3.1 → 3.4.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.
Files changed (34) hide show
  1. package/dist/analytics/analytics.cjs +1 -1
  2. package/dist/analytics/analytics.js +2 -2
  3. package/dist/analytics/log-build.cjs +1 -1
  4. package/dist/analytics/log-build.js +3 -3
  5. package/dist/{chunk-IFELNUKH.js → chunk-56E6QDHD.js} +1 -1
  6. package/dist/{chunk-AJ7F2ASU.js → chunk-6AW5YJSJ.js} +1 -1
  7. package/dist/{chunk-B5CNGEHG.js → chunk-7CV7NFRY.js} +1 -1
  8. package/dist/{chunk-LZMHPUTE.js → chunk-IVLW66F7.js} +1 -1
  9. package/dist/{chunk-DXXGEMLA.js → chunk-SPL7HGIZ.js} +1 -1
  10. package/dist/constants.cjs +1 -1
  11. package/dist/constants.js +1 -1
  12. package/dist/eventcatalog.cjs +1 -1
  13. package/dist/eventcatalog.js +5 -5
  14. package/dist/generate.cjs +1 -1
  15. package/dist/generate.js +3 -3
  16. package/dist/utils/cli-logger.cjs +1 -1
  17. package/dist/utils/cli-logger.js +2 -2
  18. package/eventcatalog/integrations/eventcatalog-features.ts +9 -0
  19. package/eventcatalog/src/content.config.ts +1 -0
  20. package/eventcatalog/src/enterprise/ai/chat-api.ts +27 -83
  21. package/eventcatalog/src/enterprise/custom-documentation/pages/docs/custom/index.astro +21 -142
  22. package/eventcatalog/src/enterprise/mcp/mcp-server.ts +512 -0
  23. package/eventcatalog/src/enterprise/tools/catalog-tools.ts +690 -0
  24. package/eventcatalog/src/enterprise/tools/index.ts +5 -0
  25. package/eventcatalog/src/env.d.ts +11 -0
  26. package/eventcatalog/src/pages/diagrams/[id]/[version]/embed.astro +433 -10
  27. package/eventcatalog/src/pages/diagrams/[id]/[version]/index.astro +21 -100
  28. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/index.astro +26 -141
  29. package/eventcatalog/src/pages/docs/[type]/[id]/[version].mdx.ts +0 -4
  30. package/eventcatalog/src/types/mcp-modules.d.ts +66 -0
  31. package/eventcatalog/src/utils/feature.ts +2 -0
  32. package/eventcatalog/src/utils/mermaid-zoom.ts +751 -0
  33. package/eventcatalog/tsconfig.json +1 -1
  34. package/package.json +4 -1
@@ -0,0 +1,512 @@
1
+ import type { APIRoute } from 'astro';
2
+ import { Hono, type Context } from 'hono';
3
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
4
+ import { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js';
5
+ import { z } from 'zod';
6
+ import { join } from 'node:path';
7
+ import { isEventCatalogScaleEnabled } from '@utils/feature';
8
+ import {
9
+ getResources,
10
+ getResource,
11
+ getMessagesProducedOrConsumedByResource,
12
+ getSchemaForResource,
13
+ findResourcesByOwner,
14
+ getProducersOfMessage,
15
+ getConsumersOfMessage,
16
+ analyzeChangeImpact,
17
+ explainBusinessFlow,
18
+ getTeams,
19
+ getTeam,
20
+ getUsers,
21
+ getUser,
22
+ findMessageBySchemaId,
23
+ explainUbiquitousLanguageTerms,
24
+ collectionSchema,
25
+ resourceCollectionSchema,
26
+ messageCollectionSchema,
27
+ toolDescriptions,
28
+ } from '@enterprise/tools/catalog-tools';
29
+ import { getCollection } from 'astro:content';
30
+
31
+ const catalogDirectory = process.env.PROJECT_DIR || process.cwd();
32
+
33
+ // Helper to create consistent MCP tool handlers with error handling
34
+ function createToolHandler<T>(fn: (params: T) => Promise<any>, errorMessage: string) {
35
+ return async (params: T) => {
36
+ try {
37
+ const result = await fn(params);
38
+
39
+ if (result && 'error' in result) {
40
+ return {
41
+ content: [{ type: 'text' as const, text: JSON.stringify(result) }],
42
+ isError: true,
43
+ };
44
+ }
45
+
46
+ return {
47
+ content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],
48
+ };
49
+ } catch (error) {
50
+ return {
51
+ content: [{ type: 'text' as const, text: JSON.stringify({ error: `${errorMessage}: ${error}` }) }],
52
+ isError: true,
53
+ };
54
+ }
55
+ };
56
+ }
57
+
58
+ // Load extended tools from user configuration
59
+ let extendedTools: Record<string, any> = {};
60
+ let extendedToolNames: string[] = [];
61
+
62
+ try {
63
+ const providerConfiguration = await import(/* @vite-ignore */ join(catalogDirectory, 'eventcatalog.chat.js'));
64
+
65
+ if (isEventCatalogScaleEnabled() && providerConfiguration.tools) {
66
+ extendedTools = providerConfiguration.tools;
67
+ extendedToolNames = Object.keys(extendedTools);
68
+ }
69
+ } catch (error) {
70
+ // No chat configuration or tools defined - this is fine
71
+ }
72
+
73
+ // Create MCP Server with tools that access Astro collections
74
+ function createMcpServer() {
75
+ const server = new McpServer({
76
+ name: 'EventCatalog MCP Server',
77
+ version: '1.0.0',
78
+ });
79
+
80
+ // Register all built-in tools using the helper
81
+ server.registerTool(
82
+ 'getResources',
83
+ {
84
+ description: toolDescriptions.getResources,
85
+ inputSchema: z.object({
86
+ collection: collectionSchema.describe('The collection to get the resources from'),
87
+ cursor: z.string().optional().describe('Pagination cursor from previous response'),
88
+ search: z.string().optional().describe('Search term to filter resources by name, id, or summary (case-insensitive)'),
89
+ }),
90
+ },
91
+ createToolHandler(getResources, 'Failed to get resources')
92
+ );
93
+
94
+ server.registerTool(
95
+ 'getResource',
96
+ {
97
+ description: toolDescriptions.getResource,
98
+ inputSchema: z.object({
99
+ collection: collectionSchema.describe('The collection to get the resource from'),
100
+ id: z.string().describe('The id of the resource to get'),
101
+ version: z.string().describe('The version of the resource to get'),
102
+ }),
103
+ },
104
+ createToolHandler(getResource, 'Failed to get resource')
105
+ );
106
+
107
+ server.registerTool(
108
+ 'getMessagesProducedOrConsumedByResource',
109
+ {
110
+ description: toolDescriptions.getMessagesProducedOrConsumedByResource,
111
+ inputSchema: z.object({
112
+ resourceId: z.string().describe('The id of the resource to get the messages produced or consumed for'),
113
+ resourceVersion: z.string().describe('The version of the resource to get the messages produced or consumed for'),
114
+ resourceCollection: resourceCollectionSchema
115
+ .describe('The collection of the resource to get the messages produced or consumed for')
116
+ .default('services'),
117
+ }),
118
+ },
119
+ createToolHandler(getMessagesProducedOrConsumedByResource, 'Failed to get messages')
120
+ );
121
+
122
+ server.registerTool(
123
+ 'getSchemaForResource',
124
+ {
125
+ description: toolDescriptions.getSchemaForResource,
126
+ inputSchema: z.object({
127
+ resourceId: z.string().describe('The id of the resource to get the schema for'),
128
+ resourceVersion: z.string().describe('The version of the resource to get the schema for'),
129
+ resourceCollection: resourceCollectionSchema
130
+ .describe('The collection of the resource to get the schema for')
131
+ .default('services'),
132
+ }),
133
+ },
134
+ createToolHandler(getSchemaForResource, 'Failed to get schema')
135
+ );
136
+
137
+ server.registerTool(
138
+ 'findResourcesByOwner',
139
+ {
140
+ description: toolDescriptions.findResourcesByOwner,
141
+ inputSchema: z.object({
142
+ ownerId: z.string().describe('The id of the owner (team or user) to find resources for'),
143
+ }),
144
+ },
145
+ createToolHandler(findResourcesByOwner, 'Failed to find resources')
146
+ );
147
+
148
+ server.registerTool(
149
+ 'getProducersOfMessage',
150
+ {
151
+ description: toolDescriptions.getProducersOfMessage,
152
+ inputSchema: z.object({
153
+ messageId: z.string().describe('The id of the message to find producers for'),
154
+ messageVersion: z.string().describe('The version of the message'),
155
+ messageCollection: messageCollectionSchema
156
+ .describe('The collection type of the message (events, commands, or queries)')
157
+ .default('events'),
158
+ }),
159
+ },
160
+ createToolHandler(getProducersOfMessage, 'Failed to get producers')
161
+ );
162
+
163
+ server.registerTool(
164
+ 'getConsumersOfMessage',
165
+ {
166
+ description: toolDescriptions.getConsumersOfMessage,
167
+ inputSchema: z.object({
168
+ messageId: z.string().describe('The id of the message to find consumers for'),
169
+ messageVersion: z.string().describe('The version of the message'),
170
+ messageCollection: messageCollectionSchema
171
+ .describe('The collection type of the message (events, commands, or queries)')
172
+ .default('events'),
173
+ }),
174
+ },
175
+ createToolHandler(getConsumersOfMessage, 'Failed to get consumers')
176
+ );
177
+
178
+ server.registerTool(
179
+ 'analyzeChangeImpact',
180
+ {
181
+ description: toolDescriptions.analyzeChangeImpact,
182
+ inputSchema: z.object({
183
+ messageId: z.string().describe('The id of the message to analyze impact for'),
184
+ messageVersion: z.string().describe('The version of the message'),
185
+ messageCollection: messageCollectionSchema
186
+ .describe('The collection type of the message (events, commands, or queries)')
187
+ .default('events'),
188
+ }),
189
+ },
190
+ createToolHandler(analyzeChangeImpact, 'Failed to analyze impact')
191
+ );
192
+
193
+ server.registerTool(
194
+ 'explainBusinessFlow',
195
+ {
196
+ description: toolDescriptions.explainBusinessFlow,
197
+ inputSchema: z.object({
198
+ flowId: z.string().describe('The id of the flow to explain'),
199
+ flowVersion: z.string().describe('The version of the flow'),
200
+ }),
201
+ },
202
+ createToolHandler(explainBusinessFlow, 'Failed to explain flow')
203
+ );
204
+
205
+ server.registerTool(
206
+ 'getTeams',
207
+ {
208
+ description: toolDescriptions.getTeams,
209
+ inputSchema: z.object({
210
+ cursor: z.string().optional().describe('Pagination cursor from previous response'),
211
+ }),
212
+ },
213
+ createToolHandler(getTeams, 'Failed to get teams')
214
+ );
215
+
216
+ server.registerTool(
217
+ 'getTeam',
218
+ {
219
+ description: toolDescriptions.getTeam,
220
+ inputSchema: z.object({
221
+ id: z.string().describe('The id of the team to get'),
222
+ }),
223
+ },
224
+ createToolHandler(getTeam, 'Failed to get team')
225
+ );
226
+
227
+ server.registerTool(
228
+ 'getUsers',
229
+ {
230
+ description: toolDescriptions.getUsers,
231
+ inputSchema: z.object({
232
+ cursor: z.string().optional().describe('Pagination cursor from previous response'),
233
+ }),
234
+ },
235
+ createToolHandler(getUsers, 'Failed to get users')
236
+ );
237
+
238
+ server.registerTool(
239
+ 'getUser',
240
+ {
241
+ description: toolDescriptions.getUser,
242
+ inputSchema: z.object({
243
+ id: z.string().describe('The id of the user to get'),
244
+ }),
245
+ },
246
+ createToolHandler(getUser, 'Failed to get user')
247
+ );
248
+
249
+ server.registerTool(
250
+ 'findMessageBySchemaId',
251
+ {
252
+ description: toolDescriptions.findMessageBySchemaId,
253
+ inputSchema: z.object({
254
+ messageId: z.string().describe('The message id (from x-eventcatalog-id in the schema)'),
255
+ messageVersion: z
256
+ .string()
257
+ .optional()
258
+ .describe(
259
+ 'The message version (from x-eventcatalog-version in the schema). If not provided, returns the latest version.'
260
+ ),
261
+ collection: messageCollectionSchema
262
+ .optional()
263
+ .describe('Optional hint for which collection to search (events, commands, or queries)'),
264
+ }),
265
+ },
266
+ createToolHandler(findMessageBySchemaId, 'Failed to find message')
267
+ );
268
+
269
+ server.registerTool(
270
+ 'explainUbiquitousLanguageTerms',
271
+ {
272
+ description: toolDescriptions.explainUbiquitousLanguageTerms,
273
+ inputSchema: z.object({
274
+ domainId: z.string().describe('The id of the domain to get ubiquitous language terms for'),
275
+ domainVersion: z.string().optional().describe('The version of the domain. If not provided, uses the latest version.'),
276
+ }),
277
+ },
278
+ createToolHandler(explainUbiquitousLanguageTerms, 'Failed to get ubiquitous language terms')
279
+ );
280
+
281
+ // Register extended tools from user configuration
282
+ for (const [toolName, toolConfig] of Object.entries(extendedTools)) {
283
+ if (!toolConfig || typeof toolConfig !== 'object') continue;
284
+
285
+ // Extract tool properties (Vercel AI SDK format)
286
+ const { description, parameters, execute } = toolConfig;
287
+
288
+ if (!description || !execute) {
289
+ console.warn(`[MCP] Skipping invalid extended tool: ${toolName}`);
290
+ continue;
291
+ }
292
+
293
+ server.registerTool(
294
+ toolName,
295
+ {
296
+ description: description || `Custom tool: ${toolName}`,
297
+ inputSchema: parameters || z.object({}),
298
+ },
299
+ async (params: any) => {
300
+ try {
301
+ const result = await execute(params);
302
+ return {
303
+ content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],
304
+ };
305
+ } catch (error) {
306
+ return {
307
+ content: [{ type: 'text' as const, text: JSON.stringify({ error: `Failed to execute ${toolName}: ${error}` }) }],
308
+ isError: true,
309
+ };
310
+ }
311
+ }
312
+ );
313
+ }
314
+
315
+ // ============================================
316
+ // Register MCP Resources
317
+ // ============================================
318
+
319
+ const resourceDefinitions = [
320
+ {
321
+ name: 'All Resources in EventCatalog',
322
+ uri: 'eventcatalog://all',
323
+ description: 'All messages, domains and services in EventCatalog',
324
+ collections: ['events', 'commands', 'queries', 'services', 'domains', 'flows', 'channels', 'entities'] as const,
325
+ },
326
+ {
327
+ name: 'All Events in EventCatalog',
328
+ uri: 'eventcatalog://events',
329
+ description: 'All events in EventCatalog',
330
+ collections: ['events'] as const,
331
+ },
332
+ {
333
+ name: 'All Commands in EventCatalog',
334
+ uri: 'eventcatalog://commands',
335
+ description: 'All commands in EventCatalog',
336
+ collections: ['commands'] as const,
337
+ },
338
+ {
339
+ name: 'All Queries in EventCatalog',
340
+ uri: 'eventcatalog://queries',
341
+ description: 'All queries in EventCatalog',
342
+ collections: ['queries'] as const,
343
+ },
344
+ {
345
+ name: 'All Services in EventCatalog',
346
+ uri: 'eventcatalog://services',
347
+ description: 'All services in EventCatalog',
348
+ collections: ['services'] as const,
349
+ },
350
+ {
351
+ name: 'All Domains in EventCatalog',
352
+ uri: 'eventcatalog://domains',
353
+ description: 'All domains in EventCatalog',
354
+ collections: ['domains'] as const,
355
+ },
356
+ {
357
+ name: 'All Diagrams in EventCatalog',
358
+ uri: 'eventcatalog://diagrams',
359
+ description: 'All diagrams in EventCatalog',
360
+ collections: ['diagrams'] as const,
361
+ },
362
+ {
363
+ name: 'All Channels in EventCatalog',
364
+ uri: 'eventcatalog://channels',
365
+ description: 'All channels in EventCatalog',
366
+ collections: ['channels'] as const,
367
+ },
368
+ {
369
+ name: 'All Containers in EventCatalog',
370
+ uri: 'eventcatalog://containers',
371
+ description: 'All containers in EventCatalog',
372
+ collections: ['containers'] as const,
373
+ },
374
+ {
375
+ name: 'All Flows in EventCatalog',
376
+ uri: 'eventcatalog://flows',
377
+ description: 'All flows in EventCatalog',
378
+ collections: ['flows'] as const,
379
+ },
380
+ {
381
+ name: 'All Teams in EventCatalog',
382
+ uri: 'eventcatalog://teams',
383
+ description: 'All teams in EventCatalog',
384
+ collections: ['teams'] as const,
385
+ },
386
+ {
387
+ name: 'All Users in EventCatalog',
388
+ uri: 'eventcatalog://users',
389
+ description: 'All users in EventCatalog',
390
+ collections: ['users'] as const,
391
+ },
392
+ ];
393
+
394
+ for (const resource of resourceDefinitions) {
395
+ server.registerResource(
396
+ resource.name,
397
+ resource.uri,
398
+ { description: resource.description, mimeType: 'application/json' },
399
+ async (uri: URL) => {
400
+ const allResources: any[] = [];
401
+
402
+ for (const collectionName of resource.collections) {
403
+ try {
404
+ const items = await getCollection(collectionName as any);
405
+ for (const item of items) {
406
+ allResources.push({
407
+ type: collectionName,
408
+ id: (item as any).data.id,
409
+ version: (item as any).data.version,
410
+ name: (item as any).data.name || (item as any).data.id,
411
+ summary: (item as any).data.summary,
412
+ });
413
+ }
414
+ } catch {
415
+ // Collection might not exist, skip it
416
+ }
417
+ }
418
+
419
+ return {
420
+ contents: [
421
+ {
422
+ uri: uri.href,
423
+ text: JSON.stringify({ resources: allResources }, null, 2),
424
+ mimeType: 'application/json',
425
+ },
426
+ ],
427
+ };
428
+ }
429
+ );
430
+ }
431
+
432
+ return server;
433
+ }
434
+
435
+ // Create a single MCP server instance
436
+ const mcpServer = createMcpServer();
437
+
438
+ // Create transport for handling requests
439
+ const transport = new WebStandardStreamableHTTPServerTransport({
440
+ sessionIdGenerator: undefined, // Stateless mode
441
+ });
442
+
443
+ // Connect the server to the transport
444
+ let isConnected = false;
445
+
446
+ // Create Hono app for MCP routes
447
+ const app = new Hono().basePath('/docs/mcp');
448
+
449
+ // Built-in tool names derived from toolDescriptions
450
+ const builtInTools = Object.keys(toolDescriptions);
451
+
452
+ // MCP Resource URIs
453
+ const mcpResources = [
454
+ 'eventcatalog://all',
455
+ 'eventcatalog://events',
456
+ 'eventcatalog://commands',
457
+ 'eventcatalog://queries',
458
+ 'eventcatalog://services',
459
+ 'eventcatalog://domains',
460
+ 'eventcatalog://flows',
461
+ 'eventcatalog://teams',
462
+ 'eventcatalog://users',
463
+ ];
464
+
465
+ // Health check endpoint
466
+ app.get('/', async (c: Context) => {
467
+ return c.json({
468
+ name: 'EventCatalog MCP Server',
469
+ version: '1.0.0',
470
+ status: 'running',
471
+ tools: [...builtInTools, ...extendedToolNames],
472
+ extendedTools: extendedToolNames.length > 0 ? extendedToolNames : undefined,
473
+ resources: mcpResources,
474
+ });
475
+ });
476
+
477
+ // MCP protocol endpoint - handles POST requests for MCP protocol
478
+ app.post('/', async (c: Context) => {
479
+ try {
480
+ // Connect server to transport if not already connected
481
+ if (!isConnected) {
482
+ await mcpServer.connect(transport);
483
+ isConnected = true;
484
+ }
485
+
486
+ // Handle the MCP request using the web standard transport
487
+ return await transport.handleRequest(c.req.raw);
488
+ } catch (error) {
489
+ console.error('MCP request error:', error);
490
+ return c.json(
491
+ {
492
+ jsonrpc: '2.0',
493
+ error: {
494
+ code: -32603,
495
+ message: 'Internal server error',
496
+ },
497
+ id: null,
498
+ },
499
+ 500
500
+ );
501
+ }
502
+ });
503
+
504
+ // Astro API route handler - delegates all requests to Hono
505
+ // Note: SSR and Scale plan checks are handled at build time by the integration
506
+ // This route is only injected when isEventCatalogMCPEnabled() returns true
507
+ export const ALL: APIRoute = async ({ request }) => {
508
+ return app.fetch(request);
509
+ };
510
+
511
+ // Disable prerendering for SSR
512
+ export const prerender = false;