@eventcatalog/core 5.2.1 → 5.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 (47) hide show
  1. package/dist/analytics/analytics.cjs +1 -1
  2. package/dist/analytics/analytics.js +2 -2
  3. package/dist/analytics/count-resources.cjs +10 -0
  4. package/dist/analytics/count-resources.js +1 -1
  5. package/dist/analytics/log-build.cjs +11 -1
  6. package/dist/analytics/log-build.js +4 -4
  7. package/dist/{chunk-HVCPU4V3.js → chunk-GAKU752C.js} +1 -1
  8. package/dist/{chunk-VEQLCFU6.js → chunk-GQQFHJSY.js} +2 -2
  9. package/dist/{chunk-QSD6WMYE.js → chunk-NSVRGATQ.js} +1 -1
  10. package/dist/{chunk-OVEXZEJJ.js → chunk-VZNPE26F.js} +1 -1
  11. package/dist/{chunk-MESHMXLR.js → chunk-WG276AIE.js} +1 -1
  12. package/dist/{chunk-ZAZHAVKB.js → chunk-YRZK7HKX.js} +10 -0
  13. package/dist/constants.cjs +1 -1
  14. package/dist/constants.js +1 -1
  15. package/dist/docs/development/guides/fitness-functions/01-what-are-fitness-functions.md +116 -0
  16. package/dist/docs/development/guides/fitness-functions/02-create-fitness-function.md +222 -0
  17. package/dist/docs/development/guides/fitness-functions/03-global-and-resource-functions.md +198 -0
  18. package/dist/docs/development/guides/fitness-functions/04-run-in-ci.md +223 -0
  19. package/dist/docs/development/guides/fitness-functions/05-api-reference.md +271 -0
  20. package/dist/docs/development/guides/fitness-functions/06-examples.md +354 -0
  21. package/dist/docs/development/guides/fitness-functions/_category_.json +11 -0
  22. package/dist/eventcatalog.cjs +25 -1
  23. package/dist/eventcatalog.js +26 -12
  24. package/dist/federation/federate.js +3 -3
  25. package/dist/generate.cjs +1 -1
  26. package/dist/generate.js +3 -3
  27. package/dist/runtime-dependencies/@eventcatalog__fitness.mjs +3 -0
  28. package/dist/runtime-dependencies/@modelcontextprotocol__client.mjs +3 -0
  29. package/dist/runtime-dependencies/@modelcontextprotocol__server.mjs +3 -0
  30. package/dist/runtime-dependencies/manifest.json +3 -4
  31. package/dist/utils/cli-logger.cjs +1 -1
  32. package/dist/utils/cli-logger.js +2 -2
  33. package/eventcatalog/src/features/mcp/apps/generated/canvas.html +73 -74
  34. package/eventcatalog/src/features/mcp/apps/generated/viewer.html +71 -73
  35. package/eventcatalog/src/features/mcp/apps/shared/app-view.tsx +1 -1
  36. package/eventcatalog/src/features/mcp/custom-tool-input.ts +38 -0
  37. package/eventcatalog/src/features/mcp/mcp-apps.ts +6 -4
  38. package/eventcatalog/src/features/mcp/mcp-server.ts +24 -17
  39. package/eventcatalog/src/features/studio/hooks/use-canvas-webmcp.ts +4 -3
  40. package/eventcatalog/src/features/studio/server/canvas-mcp.ts +2 -4
  41. package/package.json +10 -7
  42. package/dist/runtime-dependencies/@modelcontextprotocol__sdk.mjs +0 -3
  43. package/dist/runtime-dependencies/@modelcontextprotocol__sdk__server__mcp.js.mjs +0 -3
  44. package/dist/runtime-dependencies/@modelcontextprotocol__sdk__server__webStandardStreamableHttp.js.mjs +0 -3
  45. package/dist/runtime-dependencies/@modelcontextprotocol__sdk__types.js.mjs +0 -3
  46. package/eventcatalog/src/features/studio/server/tool-icons.ts +0 -30
  47. package/dist/{chunk-NCEZDQSW.js → chunk-QJ7KKN2D.js} +7 -7
@@ -7,7 +7,7 @@ import { config as configureZod } from 'zod';
7
7
  import { createRoot } from 'react-dom/client';
8
8
  import type { App, McpUiHostContext } from '@modelcontextprotocol/ext-apps';
9
9
  import { useApp } from '@modelcontextprotocol/ext-apps/react';
10
- import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
10
+ import type { CallToolResult } from '@modelcontextprotocol/client';
11
11
 
12
12
  // MCP hosts block eval in views, so stop zod (used by the MCP Apps SDK) from probing for it
13
13
  configureZod({ jitless: true });
@@ -0,0 +1,38 @@
1
+ import { fromJsonSchema, type JsonSchemaType, type StandardSchemaWithJSON } from '@modelcontextprotocol/server';
2
+ import { asSchema, type FlexibleSchema } from 'ai';
3
+ import { z } from 'zod';
4
+
5
+ /** Zod 4.2+ (and other Standard JSON Schema) schemas, which the MCP SDK lists and validates itself */
6
+ const hasJsonSchema = (schema: unknown): schema is StandardSchemaWithJSON =>
7
+ typeof schema === 'object' &&
8
+ schema !== null &&
9
+ typeof (schema as { '~standard'?: { jsonSchema?: { input?: unknown } } })['~standard']?.jsonSchema?.input === 'function';
10
+
11
+ /**
12
+ * A custom tool's input schema (eventcatalog.chat.js, in the AI SDK's format) for the MCP SDK. Zod 4.2+ schemas are
13
+ * used as they are. The others the AI SDK takes (zod 3, jsonSchema()) are listed from their JSON Schema and validated
14
+ * by the schema itself, so coercion, defaults and transforms work as before: checking the JSON Schema first would turn
15
+ * away input the schema accepts, such as "5" for a coerced number. MCP SDK 2 can't list zod 3 schemas, and one failed
16
+ * tools/list for every tool.
17
+ */
18
+ export const toCustomToolInput = async (schema: unknown): Promise<StandardSchemaWithJSON> => {
19
+ if (!schema) return z.object({});
20
+ if (hasJsonSchema(schema)) return schema;
21
+
22
+ const { jsonSchema, validate } = asSchema(schema as FlexibleSchema<unknown>);
23
+ const json = (await jsonSchema) as JsonSchemaType;
24
+ // jsonSchema() can leave out a validator, and then the arguments are checked against the JSON Schema
25
+ if (!validate) return fromJsonSchema(json);
26
+
27
+ return {
28
+ '~standard': {
29
+ version: 1,
30
+ vendor: 'eventcatalog',
31
+ jsonSchema: { input: () => json, output: () => json },
32
+ validate: async (value) => {
33
+ const result = await validate(value);
34
+ return result.success ? { value: result.value } : { issues: [{ message: result.error.message }] };
35
+ },
36
+ },
37
+ };
38
+ };
@@ -1,5 +1,4 @@
1
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import type { ToolAnnotations } from '@modelcontextprotocol/sdk/types.js';
1
+ import type { Icon, McpServer, ToolAnnotations } from '@modelcontextprotocol/server';
3
2
  import type { McpUiDisplayMode } from '@modelcontextprotocol/ext-apps';
4
3
  import { registerAppResource, RESOURCE_MIME_TYPE, RESOURCE_URI_META_KEY } from '@modelcontextprotocol/ext-apps/server';
5
4
  import { z } from 'zod';
@@ -40,6 +39,8 @@ type ToolDefinition<Shape extends z.ZodRawShape> = {
40
39
  input: Shape;
41
40
  /** More `_meta` for the tool, e.g. host extensions such as ChatGPT's `openai/ui` entrypoints */
42
41
  meta?: Record<string, unknown>;
42
+ /** Icons listed with the tool (MCP 2025-11-25 tool icons, which ChatGPT shows on entrypoints) */
43
+ icons?: Icon[];
43
44
  };
44
45
 
45
46
  export type McpAppDefinition<Shape extends z.ZodRawShape, ViewShape extends z.ZodRawShape, Payload> = {
@@ -106,13 +107,14 @@ export function registerMcpApp<Shape extends z.ZodRawShape, ViewShape extends z.
106
107
  };
107
108
 
108
109
  const toolInput = z.object(app.tool.input);
109
- server.registerTool<z.ZodRawShape, typeof toolInput>(
110
+ server.registerTool(
110
111
  app.tool.name,
111
112
  {
112
113
  title: app.tool.title,
113
114
  description: app.tool.description,
114
115
  inputSchema: toolInput,
115
116
  annotations: app.annotations,
117
+ ...(app.tool.icons && { icons: app.tool.icons }),
116
118
  _meta: loadView ? { ...appViewMeta(app.resource.uri), ...app.tool.meta } : undefined,
117
119
  },
118
120
  showInView
@@ -130,7 +132,7 @@ export function registerMcpApp<Shape extends z.ZodRawShape, ViewShape extends z.
130
132
  };
131
133
 
132
134
  const viewToolInput = z.object(app.viewTool.input);
133
- server.registerTool<z.ZodRawShape, typeof viewToolInput>(
135
+ server.registerTool(
134
136
  app.viewTool.name,
135
137
  {
136
138
  title: app.viewTool.title,
@@ -1,7 +1,6 @@
1
1
  import type { APIRoute } from 'astro';
2
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';
3
+ import { McpServer, WebStandardStreamableHTTPServerTransport, type StandardSchemaWithJSON } from '@modelcontextprotocol/server';
5
4
  import { z } from 'zod';
6
5
  import { join } from 'node:path';
7
6
  import * as catalogTools from '@features/tools/catalog-tools';
@@ -21,6 +20,7 @@ import type { SchemaViewerPayload } from './apps/schema-viewer/shared';
21
20
  import { RESOURCE_VIEWS, VIEWER_META_KEY, VIEWER_RESOURCE_URI, VIEWER_VIEW_TOOL, type ViewerPayload } from './apps/viewer/shared';
22
21
  import { toSchemaViewerSchemas } from './schema-viewer';
23
22
  import { getMcpAppViewLoader, mcpAppNote, registerMcpApp } from './mcp-apps';
23
+ import { toCustomToolInput } from './custom-tool-input';
24
24
  import { buildUrl } from '@utils/url-builder';
25
25
  import { isCanvasEnabled } from '@utils/feature';
26
26
  import { CANVAS_TOOL_NAMES, registerCanvasTools } from '@features/studio/server/canvas-mcp';
@@ -74,19 +74,34 @@ function createToolHandler<T>(fn: (params: T) => Promise<any>, errorMessage: str
74
74
 
75
75
  // Load extended tools from user configuration
76
76
  let extendedTools: Record<string, any> = {};
77
- let extendedToolNames: string[] = [];
78
77
 
79
78
  try {
80
79
  const providerConfiguration = await import(/* @vite-ignore */ join(catalogDirectory, 'eventcatalog.chat.js'));
81
80
 
82
81
  if (providerConfiguration.tools) {
83
82
  extendedTools = providerConfiguration.tools;
84
- extendedToolNames = Object.keys(extendedTools);
85
83
  }
86
84
  } catch (error) {
87
85
  // No chat configuration or tools defined - this is fine
88
86
  }
89
87
 
88
+ // The extended tools the MCP server offers, each with its input for the MCP SDK. Worked out once (converting a
89
+ // schema can be async), so the tools registered and the tools listed by GET /docs/mcp are the same ones.
90
+ const extendedToolInputs = new Map<string, StandardSchemaWithJSON>();
91
+ for (const [toolName, toolConfig] of Object.entries(extendedTools)) {
92
+ if (!toolConfig || typeof toolConfig !== 'object') continue;
93
+ if (!toolConfig.description || !toolConfig.execute) {
94
+ console.warn(`[MCP] Skipping invalid extended tool: ${toolName}`);
95
+ continue;
96
+ }
97
+ try {
98
+ extendedToolInputs.set(toolName, await toCustomToolInput(toolConfig.inputSchema || toolConfig.parameters));
99
+ } catch (error) {
100
+ console.warn(`[MCP] Skipping extended tool ${toolName}: its input schema could not be read (${error})`);
101
+ }
102
+ }
103
+ const extendedToolNames = [...extendedToolInputs.keys()];
104
+
90
105
  const MCP_SERVER_VERSION = '1.3.0';
91
106
 
92
107
  // Every built-in tool only reads the catalog, so clients can safely auto-approve them
@@ -633,24 +648,16 @@ function createMcpServer(
633
648
  }
634
649
 
635
650
  // Register extended tools from user configuration
636
- for (const [toolName, toolConfig] of Object.entries(scope ? {} : extendedTools)) {
637
- if (!toolConfig || typeof toolConfig !== 'object') continue;
638
-
639
- // Extract tool properties (Vercel AI SDK format)
640
- // The AI SDK tool() helper uses "inputSchema" for Zod schemas
641
- const { title, description, parameters, inputSchema, annotations, execute } = toolConfig;
642
-
643
- if (!description || !execute) {
644
- console.warn(`[MCP] Skipping invalid extended tool: ${toolName}`);
645
- continue;
646
- }
651
+ for (const [toolName, input] of scope ? [] : extendedToolInputs) {
652
+ // Tool properties in the Vercel AI SDK format
653
+ const { title, description, annotations, execute } = extendedTools[toolName];
647
654
 
648
655
  server.registerTool(
649
656
  toolName,
650
657
  {
651
658
  ...(title && { title }),
652
- description: description || `Custom tool: ${toolName}`,
653
- inputSchema: inputSchema || parameters || z.object({}),
659
+ description,
660
+ inputSchema: input,
654
661
  ...(annotations && { annotations }),
655
662
  },
656
663
  async (params: any) => {
@@ -46,7 +46,7 @@ type ModelContextTool = {
46
46
  title?: string;
47
47
  description: string;
48
48
  inputSchema?: object;
49
- annotations?: { readOnlyHint?: boolean; consequentialHint?: boolean };
49
+ annotations?: { readOnlyHint?: boolean; consequentialHint?: boolean; untrustedContentHint?: boolean };
50
50
  execute: (input: unknown, options?: { signal?: AbortSignal }) => Promise<ToolResult>;
51
51
  };
52
52
  type ModelContext = { registerTool: (tool: ModelContextTool, options?: { signal?: AbortSignal }) => Promise<void> | void };
@@ -204,7 +204,8 @@ const createCanvasTools = (get: () => CanvasWebMcpOptions, getStage: () => Agent
204
204
  description:
205
205
  'Reads the collaborative canvas open on this page: its nodes (ids, names, positions, the catalog resource they stand for), connections, comment threads, and who is on it. Call it first, and again to see what people changed.',
206
206
  input: z.object({}),
207
- annotations: { readOnlyHint: true },
207
+ // Names, notes and comments come from people
208
+ annotations: { readOnlyHint: true, untrustedContentHint: true },
208
209
  execute: () =>
209
210
  withDoc((doc) => ({
210
211
  ...describeCanvas(doc),
@@ -342,7 +343,7 @@ const createCanvasTools = (get: () => CanvasWebMcpOptions, getStage: () => Agent
342
343
  description:
343
344
  'Selects nodes and moves the view to them, to point the user at something. Leave out nodeIds to show the whole canvas.',
344
345
  input: z.object({ nodeIds: z.array(z.string()).optional() }),
345
- annotations: { readOnlyHint: true },
346
+ // Not read only: it changes the user's selection and view
346
347
  execute: ({ nodeIds }) => {
347
348
  if (nodeIds?.length) get().select(nodeIds);
348
349
  get().fitView(nodeIds);
@@ -1,4 +1,4 @@
1
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
1
+ import type { McpServer } from '@modelcontextprotocol/server';
2
2
  import type * as Y from 'yjs';
3
3
  import { z } from 'zod';
4
4
  import { buildUrl } from '@utils/url-builder';
@@ -37,7 +37,6 @@ import {
37
37
  } from '../agent-choreography';
38
38
  import { STUDIO_SOCKET_PATH, findStudioRuntime } from './runtime';
39
39
  import { getAgent } from '../agents';
40
- import { addToolIcons } from './tool-icons';
41
40
 
42
41
  /**
43
42
  * MCP tools for the collaborative canvas: agents create canvases, join the ones people are on, and
@@ -139,6 +138,7 @@ export function registerCanvasTools(server: McpServer, { catalogUrl, userAgent }
139
138
  // ChatGPT entrypoints (OpenAI MCP extensions): people can open canvases from the sidebar or as a tab in a
140
139
  // thread, which calls the tool with {} (the list of canvases)
141
140
  meta: { 'openai/ui': { entrypoints: [{ type: 'global' }, { type: 'thread' }] } },
141
+ icons: [CANVAS_ICON],
142
142
  input: {
143
143
  canvasId: canvasId.optional(),
144
144
  title: z.string().optional().describe('For a new canvas: what it is for'),
@@ -490,6 +490,4 @@ export function registerCanvasTools(server: McpServer, { catalogUrl, userAgent }
490
490
  return found ? text({ threadId, resolved }) : text({ error: `No comment thread "${threadId}"` }, true);
491
491
  }
492
492
  );
493
-
494
- addToolIcons(server, { openCanvas: [CANVAS_ICON] });
495
493
  }
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  },
8
8
  "license": "BUSL-1.1",
9
9
  "type": "module",
10
- "version": "5.2.1",
10
+ "version": "5.4.1",
11
11
  "publishConfig": {
12
12
  "access": "public"
13
13
  },
@@ -65,8 +65,9 @@
65
65
  "@iconify-json/logos": "^1.2.4",
66
66
  "@mcp-b/webmcp-polyfill": "5.1.0",
67
67
  "@mermaid-js/layout-elk": "^0.2.0",
68
- "@modelcontextprotocol/ext-apps": "^1.7.5",
69
- "@modelcontextprotocol/sdk": "^1.32.0",
68
+ "@modelcontextprotocol/client": "2.3.1",
69
+ "@modelcontextprotocol/ext-apps": "2.0.3",
70
+ "@modelcontextprotocol/server": "2.3.1",
70
71
  "@nanostores/react": "^1.0.0",
71
72
  "@parcel/watcher": "^2.4.1",
72
73
  "@radix-ui/react-context-menu": "^2.2.6",
@@ -140,10 +141,11 @@
140
141
  "y-protocols": "^1.0.7",
141
142
  "yjs": "^13.6.33",
142
143
  "zod": "^4.6.6",
143
- "@eventcatalog/exporters": "0.1.1",
144
- "@eventcatalog/linter": "1.2.3",
145
- "@eventcatalog/sdk": "2.31.1",
146
- "@eventcatalog/visualiser": "^5.0.0"
144
+ "@eventcatalog/fitness": "0.1.0",
145
+ "@eventcatalog/visualiser": "^5.0.0",
146
+ "@eventcatalog/linter": "1.2.4",
147
+ "@eventcatalog/sdk": "2.31.2",
148
+ "@eventcatalog/exporters": "0.1.2"
147
149
  },
148
150
  "devDependencies": {
149
151
  "@astrojs/check": "^0.9.10",
@@ -188,6 +190,7 @@
188
190
  "start:catalog:server": "node scripts/start-server-locally.js",
189
191
  "preview:catalog": "node scripts/preview-catalog-locally.js",
190
192
  "generate:catalog": "node scripts/generate-catalog-locally.js",
193
+ "fitness:catalog": "node scripts/fitness-catalog-locally.js",
191
194
  "verify-build:catalog": "rimraf dist && pnpm run build:cd",
192
195
  "generate": "pnpm run build:bin && npx . generate",
193
196
  "format": "prettier --config .prettierrc --write \"**/*.{js,jsx,ts,tsx,json,astro}\"",
@@ -1,3 +0,0 @@
1
- export * from "@modelcontextprotocol/sdk";
2
- import * as dependency from "@modelcontextprotocol/sdk";
3
- export default dependency.default;
@@ -1,3 +0,0 @@
1
- export * from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import * as dependency from "@modelcontextprotocol/sdk/server/mcp.js";
3
- export default dependency.default;
@@ -1,3 +0,0 @@
1
- export * from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
2
- import * as dependency from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
3
- export default dependency.default;
@@ -1,3 +0,0 @@
1
- export * from "@modelcontextprotocol/sdk/types.js";
2
- import * as dependency from "@modelcontextprotocol/sdk/types.js";
3
- export default dependency.default;
@@ -1,30 +0,0 @@
1
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
-
3
- /** A tool icon (MCP 2025-11-25) */
4
- export type ToolIcon = { src: string; mimeType?: string; sizes?: string[] };
5
- type ListTools = (request: unknown, extra: unknown) => Promise<{ tools: { name: string }[] }>;
6
-
7
- /** The SDK's request handlers (private in SDK 1.x, so checked rather than assumed) */
8
- const getRequestHandlers = (server: McpServer): Map<string, ListTools> | undefined => {
9
- const handlers: unknown = Reflect.get(server.server, '_requestHandlers');
10
- return handlers instanceof Map ? handlers : undefined;
11
- };
12
-
13
- /**
14
- * Adds icons to tools in tools/list (MCP 2025-11-25 tool icons, which ChatGPT shows on entrypoints). The MCP
15
- * TypeScript SDK 1.x doesn't list tool icons, so this wraps its tools/list handler. If a new SDK changes how
16
- * it keeps handlers, tools are listed without icons and this says so.
17
- */
18
- export const addToolIcons = (server: McpServer, icons: Record<string, ToolIcon[]>) => {
19
- const handlers = getRequestHandlers(server);
20
- const listTools = handlers?.get('tools/list');
21
- if (!handlers || !listTools) {
22
- console.warn('[EventCatalog MCP] Could not add tool icons: the MCP SDK no longer exposes its tools/list handler');
23
- return false;
24
- }
25
- handlers.set('tools/list', async (request, extra) => {
26
- const result = await listTools(request, extra);
27
- return { ...result, tools: result.tools.map((tool) => (icons[tool.name] ? { ...tool, icons: icons[tool.name] } : tool)) };
28
- });
29
- return true;
30
- };
@@ -1,10 +1,3 @@
1
- import {
2
- createFederationContentCache
3
- } from "./chunk-R7Z5ALYI.js";
4
- import {
5
- getFederationDiagnostics,
6
- resolveFederationRules
7
- } from "./chunk-JJPB6EOZ.js";
8
1
  import {
9
2
  withFederationOutputTransaction
10
3
  } from "./chunk-WRNH5C3U.js";
@@ -14,6 +7,13 @@ import {
14
7
  import {
15
8
  createFederationSourceProvider
16
9
  } from "./chunk-A2RZR3U4.js";
10
+ import {
11
+ createFederationContentCache
12
+ } from "./chunk-R7Z5ALYI.js";
13
+ import {
14
+ getFederationDiagnostics,
15
+ resolveFederationRules
16
+ } from "./chunk-JJPB6EOZ.js";
17
17
  import {
18
18
  getEventCatalogConfigFile
19
19
  } from "./chunk-4G7FSRPN.js";