@eventcatalog/core 5.2.0 → 5.4.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 (82) 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-DJCOJ77J.js → chunk-7RJN4FNG.js} +1 -1
  8. package/dist/{chunk-RNYFUWJI.js → chunk-EPDVXHC7.js} +4 -4
  9. package/dist/{chunk-XI5HQWM2.js → chunk-O2K4VPDO.js} +1 -1
  10. package/dist/{chunk-N2LLD5TR.js → chunk-OT2U5OES.js} +1 -1
  11. package/dist/{chunk-QJ7KKN2D.js → chunk-OX6TSVHY.js} +1 -1
  12. package/dist/{chunk-66OL4B7U.js → chunk-PAS36QHK.js} +1 -1
  13. package/dist/{chunk-NBYKF4Z4.js → chunk-TERJXCLK.js} +1 -1
  14. package/dist/{chunk-ZAZHAVKB.js → chunk-YRZK7HKX.js} +10 -0
  15. package/dist/constants.cjs +1 -1
  16. package/dist/constants.js +1 -1
  17. package/dist/docs/development/ask-your-architecture/03-mcp-server/getting-started.md +1 -1
  18. package/dist/docs/development/developer-tools/api-catalog.md +0 -2
  19. package/dist/docs/development/export/01-introduction.md +1 -1
  20. package/dist/docs/development/export/04-reference.md +1 -1
  21. package/dist/docs/development/guides/fitness-functions/01-what-are-fitness-functions.md +116 -0
  22. package/dist/docs/development/guides/fitness-functions/02-create-fitness-function.md +222 -0
  23. package/dist/docs/development/guides/fitness-functions/03-global-and-resource-functions.md +198 -0
  24. package/dist/docs/development/guides/fitness-functions/04-run-in-ci.md +223 -0
  25. package/dist/docs/development/guides/fitness-functions/05-api-reference.md +271 -0
  26. package/dist/docs/development/guides/fitness-functions/06-examples.md +354 -0
  27. package/dist/docs/development/guides/fitness-functions/_category_.json +11 -0
  28. package/dist/docs/development/guides/owners/05-teams-reference.md +0 -12
  29. package/dist/docs/development/guides/owners/06-users-reference.md +0 -12
  30. package/dist/docs/development/upgrading/v5.md +1 -1
  31. package/dist/eventcatalog.cjs +26 -2
  32. package/dist/eventcatalog.js +28 -14
  33. package/dist/federation/federate.js +3 -3
  34. package/dist/federation/source-provider.js +2 -2
  35. package/dist/generate.cjs +1 -1
  36. package/dist/generate.js +3 -3
  37. package/dist/runtime-dependencies/@eventcatalog__fitness.mjs +3 -0
  38. package/dist/runtime-dependencies/@modelcontextprotocol__client.mjs +3 -0
  39. package/dist/runtime-dependencies/@modelcontextprotocol__server.mjs +3 -0
  40. package/dist/runtime-dependencies/manifest.json +3 -4
  41. package/dist/search-indexer.cjs +1 -1
  42. package/dist/search-indexer.js +1 -1
  43. package/dist/utils/cli-logger.cjs +1 -1
  44. package/dist/utils/cli-logger.js +2 -2
  45. package/eventcatalog/src/content.config.ts +0 -3
  46. package/eventcatalog/src/features/collections/resource-docs.ts +0 -1
  47. package/eventcatalog/src/features/mcp/apps/generated/canvas.html +73 -74
  48. package/eventcatalog/src/features/mcp/apps/generated/viewer.html +71 -73
  49. package/eventcatalog/src/features/mcp/apps/shared/app-view.tsx +1 -1
  50. package/eventcatalog/src/features/mcp/custom-tool-input.ts +38 -0
  51. package/eventcatalog/src/features/mcp/mcp-apps.ts +6 -4
  52. package/eventcatalog/src/features/mcp/mcp-scope.ts +2 -3
  53. package/eventcatalog/src/features/mcp/mcp-server.ts +24 -17
  54. package/eventcatalog/src/features/studio/server/canvas-mcp.ts +2 -4
  55. package/eventcatalog/src/features/tools/catalog-tools.ts +6 -8
  56. package/eventcatalog/src/pages/api-catalog/specifications/[collection]/[id]/[version]/[specification].ts +2 -4
  57. package/eventcatalog/src/utils/collections/adrs.ts +0 -1
  58. package/eventcatalog/src/utils/collections/agents.ts +0 -1
  59. package/eventcatalog/src/utils/collections/channels.ts +0 -1
  60. package/eventcatalog/src/utils/collections/commands.ts +0 -1
  61. package/eventcatalog/src/utils/collections/containers.ts +0 -1
  62. package/eventcatalog/src/utils/collections/domains.ts +0 -2
  63. package/eventcatalog/src/utils/collections/entities.ts +0 -1
  64. package/eventcatalog/src/utils/collections/events.ts +0 -1
  65. package/eventcatalog/src/utils/collections/flows.ts +0 -2
  66. package/eventcatalog/src/utils/collections/owners.ts +1 -4
  67. package/eventcatalog/src/utils/collections/queries.ts +0 -1
  68. package/eventcatalog/src/utils/collections/resource-docs.ts +1 -5
  69. package/eventcatalog/src/utils/collections/resource-owners.ts +1 -2
  70. package/eventcatalog/src/utils/collections/services.ts +0 -1
  71. package/eventcatalog/src/utils/collections/systems.ts +0 -1
  72. package/eventcatalog/src/utils/collections/teams.ts +3 -6
  73. package/eventcatalog/src/utils/collections/users.ts +4 -8
  74. package/eventcatalog/src/utils/homepage-dashboard.ts +2 -3
  75. package/eventcatalog/src/utils/node-graphs/flow-lanes.ts +2 -6
  76. package/package.json +9 -6
  77. package/dist/runtime-dependencies/@modelcontextprotocol__sdk.mjs +0 -3
  78. package/dist/runtime-dependencies/@modelcontextprotocol__sdk__server__mcp.js.mjs +0 -3
  79. package/dist/runtime-dependencies/@modelcontextprotocol__sdk__server__webStandardStreamableHttp.js.mjs +0 -3
  80. package/dist/runtime-dependencies/@modelcontextprotocol__sdk__types.js.mjs +0 -3
  81. package/eventcatalog/src/features/studio/server/tool-icons.ts +0 -30
  82. package/dist/{chunk-A2RZR3U4.js → chunk-6K7XZAYI.js} +3 -3
@@ -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,
@@ -35,7 +35,6 @@ type CatalogEntry = {
35
35
  version: string;
36
36
  name?: string;
37
37
  summary?: string;
38
- hidden?: boolean;
39
38
  [key: string]: any;
40
39
  };
41
40
  [key: string]: any;
@@ -78,7 +77,7 @@ export class McpScope {
78
77
  }
79
78
 
80
79
  add(entry: CatalogEntry | undefined) {
81
- if (!entry || entry.data.hidden === true) return false;
80
+ if (!entry) return false;
82
81
 
83
82
  const collectionEntries = this.entries.get(entry.collection) ?? new Map<string, CatalogEntry>();
84
83
  const key = entryKey(entry.data.id, entry.data.version);
@@ -147,7 +146,7 @@ export async function resolveMcpScope(
147
146
 
148
147
  const rootCollection = graphTypeToCollection[graph.root.type];
149
148
  const root = resolve(rootCollection, graph.root.id, graph.root.version);
150
- if (!root || root.data.hidden === true) throw scopeNotFound(ref);
149
+ if (!root) throw scopeNotFound(ref);
151
150
 
152
151
  const scope = new McpScope(ref, root);
153
152
  for (const resource of graph.resources) {
@@ -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) => {
@@ -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
  }
@@ -220,14 +220,12 @@ const describeVersion = (version?: string) => version ?? 'latest';
220
220
  */
221
221
  export async function getResources(params: { collection: string; cursor?: string; search?: string }) {
222
222
  const resources = await getCollection(params.collection as any);
223
- let allResults = resources
224
- .filter((resource: any) => resource.data.hidden !== true)
225
- .map((resource: any) => ({
226
- id: resource.data.id,
227
- version: resource.data.version,
228
- name: resource.data.name,
229
- summary: resource.data.summary,
230
- }));
223
+ let allResults = resources.map((resource: any) => ({
224
+ id: resource.data.id,
225
+ version: resource.data.version,
226
+ name: resource.data.name,
227
+ summary: resource.data.summary,
228
+ }));
231
229
 
232
230
  // Apply search filter if provided
233
231
  if (params.search) {
@@ -39,9 +39,7 @@ const getSpecificationsForResource = (
39
39
 
40
40
  export async function getStaticPaths() {
41
41
  const [services, domains] = await Promise.all([getCollection('services'), getCollection('domains')]);
42
- const resources = [...services, ...domains].filter((resource) => resource.data.hidden !== true);
43
-
44
- return resources.flatMap((resource) =>
42
+ return [...services, ...domains].flatMap((resource) =>
45
43
  getSpecificationsForResource(resource).map((specification) => ({
46
44
  params: {
47
45
  collection: resource.collection,
@@ -74,7 +72,7 @@ export const GET: APIRoute = async ({ params, props }) => {
74
72
  }
75
73
 
76
74
  const resources = await getCollection(collection);
77
- const resource = resources.find((item) => item.data.id === id && item.data.version === version && item.data.hidden !== true);
75
+ const resource = resources.find((item) => item.data.id === id && item.data.version === version);
78
76
 
79
77
  if (!resource) {
80
78
  return new Response(JSON.stringify({ error: 'Resource not found' }), {
@@ -39,7 +39,6 @@ export const getAdrs = async ({ getAllVersions = true, returnBody = false }: Pro
39
39
  const adrMap = createVersionedMap(allAdrs);
40
40
 
41
41
  const targetAdrs = allAdrs.filter((adr) => {
42
- if (adr.data.hidden === true) return false;
43
42
  if (!getAllVersions && adr.filePath?.includes('versioned')) return false;
44
43
  return true;
45
44
  });
@@ -38,7 +38,6 @@ export const getAgents = async ({ getAllVersions = true, returnBody = false }: P
38
38
  const flowMap = createVersionedMap(allFlows);
39
39
 
40
40
  const targetAgents = allAgents.filter((agent) => {
41
- if (agent.data.hidden === true) return false;
42
41
  if (!getAllVersions && agent.filePath?.includes('versioned')) return false;
43
42
  return true;
44
43
  });
@@ -66,7 +66,6 @@ export const getChannels = async ({ getAllVersions = true }: Props = {}): Promis
66
66
 
67
67
  // 4. Filter channels
68
68
  const targetChannels = allChannels.filter((channel) => {
69
- if (channel.data.hidden === true) return false;
70
69
  if (!getAllVersions && channel.filePath?.includes('versioned')) return false;
71
70
  return true;
72
71
  });
@@ -51,7 +51,6 @@ export const getCommands = async ({ getAllVersions = true, hydrateServices = tru
51
51
 
52
52
  // 3. Filter commands
53
53
  const targetCommands = allCommands.filter((command) => {
54
- if (command.data.hidden === true) return false;
55
54
  if (!getAllVersions && command.filePath?.includes('versioned')) return false;
56
55
  return true;
57
56
  });
@@ -33,7 +33,6 @@ export const getContainers = async ({ getAllVersions = true }: Props = {}): Prom
33
33
 
34
34
  // 3. Filter containers
35
35
  const targetContainers = allContainers.filter((container) => {
36
- if (container.data.hidden === true) return false;
37
36
  if (!getAllVersions && container.filePath?.includes('versioned')) return false;
38
37
  return true;
39
38
  });
@@ -79,8 +79,6 @@ export const getDomains = async ({
79
79
 
80
80
  // 3. Filter the domains we actually want to process/return
81
81
  const targetDomains = allDomains.filter((domain: Domain) => {
82
- // Filter out hidden
83
- if (domain.data.hidden === true) return false;
84
82
  // Handle version filtering
85
83
  if (!getAllVersions && domain.filePath?.includes('versioned')) return false;
86
84
  return true;
@@ -34,7 +34,6 @@ export const getEntities = async ({ getAllVersions = true }: Props = {}): Promis
34
34
 
35
35
  // 3. Filter entities
36
36
  const targetEntities = allEntities.filter((entity) => {
37
- if (entity.data.hidden === true) return false;
38
37
  if (!getAllVersions && entity.filePath?.includes('versioned')) return false;
39
38
  return true;
40
39
  });
@@ -51,7 +51,6 @@ export const getEvents = async ({ getAllVersions = true, hydrateServices = true
51
51
 
52
52
  // 3. Filter events
53
53
  const targetEvents = allEvents.filter((event) => {
54
- if (event.data.hidden === true) return false;
55
54
  if (!getAllVersions && event.filePath?.includes('versioned')) return false;
56
55
  return true;
57
56
  });
@@ -46,7 +46,6 @@ export const getFlows = async ({ getAllVersions = true }: Props = {}): Promise<F
46
46
 
47
47
  // 3. Filter flows
48
48
  const targetFlows = allFlows.filter((flow) => {
49
- if (flow.data.hidden === true) return false;
50
49
  if (!getAllVersions && flow.filePath?.includes('versioned')) return false;
51
50
  return true;
52
51
  });
@@ -230,7 +229,6 @@ export const getFlowsForMessages = async (
230
229
  const flowsByMessage = new Map<string, FlowReference[]>();
231
230
 
232
231
  for (const flow of flows) {
233
- if (flow.data.hidden === true) continue;
234
232
  const reference: FlowReference = {
235
233
  id: flow.data.id,
236
234
  version: flow.data.version,
@@ -11,10 +11,7 @@ const getOwners = (function () {
11
11
  async function init() {
12
12
  const ownersMap = new Map<string, CollectionEntry<'users' | 'teams'>>();
13
13
 
14
- const owners = await Promise.all([
15
- getCollection('users', (entry) => entry.data.hidden !== true),
16
- getCollection('teams', (entry) => entry.data.hidden !== true),
17
- ]);
14
+ const owners = await Promise.all([getCollection('users'), getCollection('teams')]);
18
15
 
19
16
  for (const owner of owners.flat()) {
20
17
  ownersMap.set(owner.data.id, owner);
@@ -50,7 +50,6 @@ export const getQueries = async ({ getAllVersions = true, hydrateServices = true
50
50
 
51
51
  // 3. Filter queries
52
52
  const targetQueries = allQueries.filter((query) => {
53
- if (query.data.hidden === true) return false;
54
53
  if (!getAllVersions && query.filePath?.includes('versioned')) return false;
55
54
  return true;
56
55
  });
@@ -250,7 +250,7 @@ const getResourceFolderFromFilePath = (filePath: string): string | undefined =>
250
250
 
251
251
  const buildLookup = (
252
252
  resources: Array<{
253
- data: { id: string; version: string; hidden?: boolean };
253
+ data: { id: string; version: string };
254
254
  filePath?: string;
255
255
  }>
256
256
  ): ResourceLookup => {
@@ -258,9 +258,6 @@ const buildLookup = (
258
258
  const folderToId = new Map<string, string>();
259
259
 
260
260
  for (const resource of resources) {
261
- if (resource.data.hidden === true) {
262
- continue;
263
- }
264
261
  const list = groupedById.get(resource.data.id) || [];
265
262
  list.push(resource.data.version);
266
263
  groupedById.set(resource.data.id, list);
@@ -390,7 +387,6 @@ export const getResourceDocs = async (): Promise<ResourceDocEntry[]> => {
390
387
  const [docs, lookups] = await Promise.all([getCollection('resourceDocs'), getResourceLookups()]);
391
388
 
392
389
  const docsWithResources = docs
393
- .filter((doc) => doc.data.hidden !== true)
394
390
  .map((doc) => {
395
391
  if (!doc.filePath) {
396
392
  return null;
@@ -53,8 +53,7 @@ const once = <T>(load: () => Promise<T>) => {
53
53
  return () => (loaded ??= load());
54
54
  };
55
55
 
56
- const isCurrentVersion = (entry: { data: { hidden?: boolean }; filePath?: string }) =>
57
- entry.data.hidden !== true && !entry.filePath?.includes('versioned');
56
+ const isCurrentVersion = (entry: { filePath?: string }) => !entry.filePath?.includes('versioned');
58
57
 
59
58
  // Owners come from their own collection's loader, so an owner of a type is that collection's entry.
60
59
  const asEntry = <C extends CollectionKey>(owner: ResourceOwner) => owner as unknown as CollectionEntry<C>;
@@ -54,7 +54,6 @@ export const getServices = async ({ getAllVersions = true, returnBody = false }:
54
54
 
55
55
  // 3. Filter services
56
56
  const targetServices = allServices.filter((service) => {
57
- if (service.data.hidden === true) return false;
58
57
  if (!getAllVersions && service.filePath?.includes('versioned')) return false;
59
58
  return true;
60
59
  });
@@ -42,7 +42,6 @@ export const getSystems = async ({ getAllVersions = true, enrichServices = false
42
42
 
43
43
  // Filter systems
44
44
  const targetSystems = allSystems.filter((system) => {
45
- if (system.data.hidden === true) return false;
46
45
  if (!getAllVersions && system.filePath?.includes('versioned')) return false;
47
46
  return true;
48
47
  });
@@ -29,10 +29,7 @@ export const getTeams = async (): Promise<Team[]> => {
29
29
  getAdrs({ getAllVersions: false }),
30
30
  ]);
31
31
 
32
- // 2. Filter teams
33
- const targetTeams = allTeams.filter((team) => team.data.hidden !== true);
34
-
35
- // 3. Build Owner Index: Map<OwnerID, Item[]>
32
+ // 2. Build Owner Index: Map<OwnerID, Item[]>
36
33
  // This index groups all items (domains, services, etc.) by their owner IDs.
37
34
  // This allows O(1) lookup to find all items owned by a specific team.
38
35
  const ownershipMap = new Map<string, CollectionEntry<CollectionTypes>[]>();
@@ -59,8 +56,8 @@ export const getTeams = async (): Promise<Team[]> => {
59
56
  addToIndex(allQueries);
60
57
  addToIndex(allAdrs);
61
58
 
62
- // 4. Enrich teams using the ownership index
63
- const processedTeams = targetTeams.map((team) => {
59
+ // 3. Enrich teams using the ownership index
60
+ const processedTeams = allTeams.map((team) => {
64
61
  const teamId = team.data.id;
65
62
  const ownedItems = ownershipMap.get(teamId) || [];
66
63
 
@@ -31,11 +31,7 @@ export const getUsers = async (): Promise<User[]> => {
31
31
  getAdrs({ getAllVersions: false }),
32
32
  ]);
33
33
 
34
- // 2. Filter users
35
- const targetUsers = allUsers.filter((user) => user.data.hidden !== true);
36
- const visibleTeams = allTeams.filter((team) => team.data.hidden !== true);
37
-
38
- // 3. Process users (Optimization: Iterate once over relationships if possible,
34
+ // 2. Process users (Optimization: Iterate once over relationships if possible,
39
35
  // but since we need to check ownership for EACH user against ALL items,
40
36
  // we can't easily invert the map without building an "owner" index first.
41
37
  // Given users/teams count is usually lower than events/services, iterating users and filtering items is acceptable,
@@ -67,8 +63,8 @@ export const getUsers = async (): Promise<User[]> => {
67
63
  addToIndex(allAdrs);
68
64
 
69
65
  // Team Membership Index: Map<UserID, Team[]>
70
- const teamMembershipMap = new Map<string, typeof visibleTeams>();
71
- for (const team of visibleTeams) {
66
+ const teamMembershipMap = new Map<string, typeof allTeams>();
67
+ for (const team of allTeams) {
72
68
  if (team.data.members) {
73
69
  for (const member of team.data.members) {
74
70
  if (!teamMembershipMap.has(member.id)) {
@@ -79,7 +75,7 @@ export const getUsers = async (): Promise<User[]> => {
79
75
  }
80
76
  }
81
77
 
82
- const mappedUsers = targetUsers.map((user) => {
78
+ const mappedUsers = allUsers.map((user) => {
83
79
  const userId = user.data.id;
84
80
  const associatedTeams = teamMembershipMap.get(userId) || [];
85
81
  const associatedTeamIds = associatedTeams.map((t) => t.data.id);
@@ -17,7 +17,6 @@ export type DashboardEntry = {
17
17
  name?: string;
18
18
  version: string;
19
19
  summary?: string;
20
- hidden?: boolean;
21
20
  owners?: Owner[];
22
21
  schemaPath?: string;
23
22
  };
@@ -41,8 +40,8 @@ export type DashboardData = {
41
40
 
42
41
  const MESSAGE_COLLECTIONS = ['events', 'commands', 'queries'] as const;
43
42
 
44
- // The latest version of a resource that isn't hidden (older versions live in a `versioned` folder)
45
- const isCurrent = (entry: DashboardEntry) => entry.data.hidden !== true && !entry.filePath?.includes('versioned');
43
+ // The latest version of a resource (older versions live in a `versioned` folder)
44
+ const isCurrent = (entry: DashboardEntry) => !entry.filePath?.includes('versioned');
46
45
 
47
46
  /**
48
47
  * Everything the homepage dashboard shows, from the catalog's collections: counts, and what changed recently.
@@ -39,8 +39,7 @@ const pointerIds = (pointers?: { id: string }[]) => (pointers || []).map((pointe
39
39
  * A resource listed by a domain directly belongs to that domain, otherwise to
40
40
  * the domain of the system it's in. Built once per flow from the collections,
41
41
  * rather than asking `getDomainsForService`, `getOwner` and so on for each step,
42
- * so it follows their rules: latest versions only, and hidden teams and users
43
- * are left out.
42
+ * so it follows their rules: latest versions only.
44
43
  */
45
44
  export const createLaneIndex = ({ domains, systems, teams, users }: LaneCollections) => {
46
45
  const systemOf = new Map<string, Lane>();
@@ -75,14 +74,12 @@ export const createLaneIndex = ({ domains, systems, teams, users }: LaneCollecti
75
74
  }
76
75
 
77
76
  const owners = new Map<string, { lane: Lane; isTeam: boolean }>();
78
- const hiddenOwners = new Set<string>();
79
77
  for (const [entries, isTeam] of [
80
78
  [users, false],
81
79
  [teams, true],
82
80
  ] as const) {
83
81
  for (const owner of entries) {
84
- if (owner.data.hidden) hiddenOwners.add(owner.data.id);
85
- else owners.set(owner.data.id, { lane: toLane(owner), isTeam });
82
+ owners.set(owner.data.id, { lane: toLane(owner), isTeam });
86
83
  }
87
84
  }
88
85
 
@@ -90,7 +87,6 @@ export const createLaneIndex = ({ domains, systems, teams, users }: LaneCollecti
90
87
  const teamOf = (resource: Resource): Lane | undefined => {
91
88
  const resolved = (resource.data.owners || [])
92
89
  .map((owner) => (typeof owner === 'string' ? owner : owner.id))
93
- .filter((id) => !hiddenOwners.has(id))
94
90
  .map((id) => owners.get(id) ?? { lane: { id, name: id }, isTeam: false })
95
91
  .sort((a, b) => Number(b.isTeam) - Number(a.isTeam));
96
92
  return resolved[0]?.lane;
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  },
8
8
  "license": "BUSL-1.1",
9
9
  "type": "module",
10
- "version": "5.2.0",
10
+ "version": "5.4.0",
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,9 +141,10 @@
140
141
  "y-protocols": "^1.0.7",
141
142
  "yjs": "^13.6.33",
142
143
  "zod": "^4.6.6",
143
- "@eventcatalog/exporters": "0.1.0",
144
- "@eventcatalog/linter": "1.2.2",
145
- "@eventcatalog/sdk": "2.31.0",
144
+ "@eventcatalog/fitness": "0.1.0",
145
+ "@eventcatalog/sdk": "2.31.2",
146
+ "@eventcatalog/linter": "1.2.4",
147
+ "@eventcatalog/exporters": "0.1.2",
146
148
  "@eventcatalog/visualiser": "^5.0.0"
147
149
  },
148
150
  "devDependencies": {
@@ -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;