memorix 1.8.0 → 1.8.2

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 (45) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/cli/index.js +407 -130
  3. package/dist/dashboard/static/app.js +37 -23
  4. package/dist/dashboard/static/index.html +1 -1
  5. package/dist/index.d.ts +2 -2
  6. package/dist/index.js +633 -391
  7. package/dist/maintenance-runner.js +31 -25
  8. package/dist/media-audio-runner.js +31 -25
  9. package/dist/media-video-runner.js +31 -25
  10. package/dist/memcode-runtime/CHANGELOG.md +29 -0
  11. package/dist/sdk.js +625 -390
  12. package/dist/vector-backfill-runner.js +31 -25
  13. package/docs/1.2.0-PROVIDER-QUALITY.md +15 -6
  14. package/docs/1.3.3-MULTIMODAL-MEDIA-AND-MCP-COMPAT.md +7 -6
  15. package/docs/1.8.0-RELEASE-CANDIDATE-SPEC.md +10 -8
  16. package/docs/API_REFERENCE.md +12 -8
  17. package/docs/ARCHITECTURE.md +3 -1
  18. package/docs/CONFIGURATION.md +2 -2
  19. package/docs/DASHBOARD.md +6 -0
  20. package/docs/DEVELOPMENT.md +2 -2
  21. package/docs/README.md +14 -8
  22. package/package.json +1 -1
  23. package/plugins/antigravity/memorix/plugin.json +2 -1
  24. package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
  25. package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
  26. package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
  27. package/plugins/copilot/memorix/plugin.json +1 -1
  28. package/plugins/gemini/memorix/gemini-extension.json +1 -1
  29. package/plugins/hermes/memorix/plugin.yaml +1 -1
  30. package/plugins/omp/memorix/package.json +1 -1
  31. package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
  32. package/plugins/pi/memorix/package.json +1 -1
  33. package/server.json +3 -3
  34. package/src/cli/commands/dashboard.ts +1 -1
  35. package/src/cli/commands/serve-http.ts +38 -3
  36. package/src/codegraph/external-provider.ts +299 -19
  37. package/src/config/dotenv-loader.ts +9 -4
  38. package/src/config.ts +16 -16
  39. package/src/dashboard/maintenance.ts +4 -1
  40. package/src/dashboard/server.ts +85 -86
  41. package/src/dashboard/static/index.html +1 -1
  42. package/src/llm/provider.ts +17 -4
  43. package/src/server/mcp-compat.ts +82 -0
  44. package/src/server/mcp-diagnostics.ts +13 -4
  45. package/src/server.ts +9 -6
@@ -958,18 +958,10 @@ interface DashboardState {
958
958
  dataDir: string;
959
959
  projectRoot: string | null;
960
960
  projectResolved: boolean;
961
- mode: 'standalone' | 'control-plane';
961
+ mode: 'standalone';
962
962
  port: number;
963
963
  }
964
964
 
965
- /** Optional coordination instances passed from MCP server */
966
- export interface TeamInstances {
967
- registry: { listAgents: (filter?: any) => any[]; getActiveCount: () => number; getAgent: (id: string) => any };
968
- fileLocks: { listLocks: (agentId?: string) => any[]; cleanExpired: () => void };
969
- taskManager: { list: (filter?: any) => any[]; getAvailable: () => any[] };
970
- messageBus: { getUnreadCount: (agentId: string) => number };
971
- }
972
-
973
965
  function parseJsonField(value: unknown, fallback: unknown): unknown {
974
966
  if (typeof value !== 'string') return value ?? fallback;
975
967
  try {
@@ -1027,7 +1019,72 @@ function normalizeDashboardTask(task: any) {
1027
1019
  };
1028
1020
  }
1029
1021
 
1030
- async function buildTeamSnapshot(dataDir: string, projectId: string, scope: string, mode: DashboardState['mode']) {
1022
+ type TeamSnapshotStatus = 'ok' | 'degraded' | 'error';
1023
+
1024
+ interface TeamSnapshot {
1025
+ status: TeamSnapshotStatus;
1026
+ mode: DashboardState['mode'];
1027
+ readOnly: true;
1028
+ scope: string;
1029
+ agents: any[];
1030
+ activeCount: number;
1031
+ recentCount: number;
1032
+ historicalCount: number;
1033
+ totalAgents: number;
1034
+ recentWindowDays: number;
1035
+ locks: any[];
1036
+ tasks: any[];
1037
+ availableTasks: number;
1038
+ sessions: number;
1039
+ roles: any[];
1040
+ roleOccupancy: any[];
1041
+ handoffs: any[];
1042
+ openTasks: number;
1043
+ openHandoffs: number;
1044
+ totalUnread: number;
1045
+ activeSessions: number;
1046
+ error?: string;
1047
+ errorCode?: string;
1048
+ }
1049
+
1050
+ function emptyTeamSnapshot(
1051
+ mode: DashboardState['mode'],
1052
+ scope: string,
1053
+ status: Exclude<TeamSnapshotStatus, 'ok'>,
1054
+ ): TeamSnapshot {
1055
+ return {
1056
+ status,
1057
+ mode,
1058
+ readOnly: true,
1059
+ scope,
1060
+ agents: [],
1061
+ activeCount: 0,
1062
+ recentCount: 0,
1063
+ historicalCount: 0,
1064
+ totalAgents: 0,
1065
+ recentWindowDays: 7,
1066
+ locks: [],
1067
+ tasks: [],
1068
+ availableTasks: 0,
1069
+ sessions: 0,
1070
+ roles: [],
1071
+ roleOccupancy: [],
1072
+ handoffs: [],
1073
+ openTasks: 0,
1074
+ openHandoffs: 0,
1075
+ totalUnread: 0,
1076
+ activeSessions: 0,
1077
+ error: status === 'error' ? 'Coordination state unavailable' : 'Coordination state is incomplete',
1078
+ errorCode: status === 'error' ? 'COORDINATION_STATE_UNAVAILABLE' : 'COORDINATION_STATE_DEGRADED',
1079
+ };
1080
+ }
1081
+
1082
+ function logTeamSnapshotFailure(scope: string, status: Exclude<TeamSnapshotStatus, 'ok'>): void {
1083
+ const safeScope = scope === 'global' ? 'global' : 'project';
1084
+ console.error('[dashboard] coordination snapshot unavailable', { status, scope: safeScope });
1085
+ }
1086
+
1087
+ async function buildTeamSnapshot(dataDir: string, projectId: string, scope: string, mode: DashboardState['mode']): Promise<TeamSnapshot> {
1031
1088
  try {
1032
1089
  const { initTeamStore } = await import('../team/team-store.js');
1033
1090
  const teamStore = await initTeamStore(dataDir);
@@ -1053,8 +1110,9 @@ async function buildTeamSnapshot(dataDir: string, projectId: string, scope: stri
1053
1110
  const roleOccupancy = effectiveProjectId ? teamStore.getRoleOccupancy(effectiveProjectId) : [];
1054
1111
  const handoffs = effectiveProjectId ? teamStore.listHandoffs(effectiveProjectId) : [];
1055
1112
  return {
1113
+ status: 'ok',
1056
1114
  mode,
1057
- readOnly: mode === 'standalone',
1115
+ readOnly: true,
1058
1116
  scope,
1059
1117
  agents: withTier,
1060
1118
  activeCount,
@@ -1075,28 +1133,8 @@ async function buildTeamSnapshot(dataDir: string, projectId: string, scope: stri
1075
1133
  activeSessions: activeCount,
1076
1134
  };
1077
1135
  } catch {
1078
- return {
1079
- mode,
1080
- readOnly: mode === 'standalone',
1081
- scope,
1082
- agents: [],
1083
- activeCount: 0,
1084
- recentCount: 0,
1085
- historicalCount: 0,
1086
- totalAgents: 0,
1087
- recentWindowDays: 7,
1088
- locks: [],
1089
- tasks: [],
1090
- availableTasks: 0,
1091
- sessions: 0,
1092
- roles: [],
1093
- roleOccupancy: [],
1094
- handoffs: [],
1095
- openTasks: 0,
1096
- openHandoffs: 0,
1097
- totalUnread: 0,
1098
- activeSessions: 0,
1099
- };
1136
+ logTeamSnapshotFailure(scope, 'error');
1137
+ return emptyTeamSnapshot(mode, scope, 'error');
1100
1138
  }
1101
1139
  }
1102
1140
 
@@ -1125,7 +1163,6 @@ export async function startDashboard(
1125
1163
  projectId: string,
1126
1164
  projectName: string,
1127
1165
  autoOpen = true,
1128
- teamInstances?: TeamInstances,
1129
1166
  projectRoot: string | null = null,
1130
1167
  projectResolved = true,
1131
1168
  ): Promise<void> {
@@ -1136,8 +1173,7 @@ export async function startDashboard(
1136
1173
  const baseDir = getBaseDataDir();
1137
1174
 
1138
1175
  // Mutable state — can be updated via /api/set-current-project
1139
- const isControlPlane = !!teamInstances;
1140
- const state: DashboardState = { projectId, projectName, dataDir, projectRoot, projectResolved, mode: isControlPlane ? 'control-plane' : 'standalone', port };
1176
+ const state: DashboardState = { projectId, projectName, dataDir, projectRoot, projectResolved, mode: 'standalone', port };
1141
1177
 
1142
1178
  const server = createServer(async (req, res) => {
1143
1179
  const url = req.url || '/';
@@ -1164,56 +1200,20 @@ export async function startDashboard(
1164
1200
  return;
1165
1201
  }
1166
1202
 
1167
- if (url.startsWith('/api/team')) {
1168
- if (!teamInstances) {
1169
- const parsedUrl = new URL(url, `http://127.0.0.1:${port}`);
1170
- const scope = parsedUrl.searchParams.get('scope') || 'project';
1171
- sendJson(res, await buildTeamSnapshot(state.dataDir, state.projectId, scope, state.mode));
1172
- return;
1173
- }
1174
- try {
1175
- teamInstances.fileLocks.cleanExpired();
1176
- const agents = teamInstances.registry.listAgents();
1177
- const locks = teamInstances.fileLocks.listLocks();
1178
- const tasks = teamInstances.taskManager.list();
1179
- const available = teamInstances.taskManager.getAvailable();
1180
-
1181
- // Role occupancy and handoffs from TeamStore (if available)
1182
- let roles: any[] = [];
1183
- let roleOccupancy: any[] = [];
1184
- let handoffs: any[] = [];
1185
- try {
1186
- const { getTeamStore, isTeamStoreInitialized } = await import('../team/team-store.js');
1187
- if (isTeamStoreInitialized()) {
1188
- const teamStore = getTeamStore();
1189
- const projectId = state.projectId;
1190
- roles = teamStore.listRoles(projectId);
1191
- roleOccupancy = teamStore.getRoleOccupancy(projectId);
1192
- handoffs = teamStore.listHandoffs(projectId);
1193
- }
1194
- } catch { /* team store not available in standalone mode */ }
1195
-
1203
+ const parsedUrl = new URL(url, `http://127.0.0.1:${port}`);
1204
+ if (parsedUrl.pathname === '/api/team') {
1205
+ if (req.method !== 'GET') {
1196
1206
  sendJson(res, {
1197
- agents: agents.map((a: any) => ({
1198
- ...a,
1199
- unread: teamInstances!.messageBus.getUnreadCount(a.id),
1200
- })),
1201
- activeCount: teamInstances.registry.getActiveCount(),
1202
- locks,
1203
- tasks,
1204
- availableTasks: available.length,
1205
- roles,
1206
- roleOccupancy,
1207
- handoffs,
1208
- // Resume data for "Continue this project" area
1209
- openTasks: tasks.filter((t: any) => t.status === 'pending' || t.status === 'in_progress').length,
1210
- openHandoffs: handoffs.filter((h: any) => h.handoff_status === 'open' || h.handoffStatus === 'open').length,
1211
- totalUnread: agents.reduce((sum: number, a: any) => sum + teamInstances!.messageBus.getUnreadCount(a.id), 0),
1212
- activeSessions: agents.filter((a: any) => a.status === 'active').length,
1213
- });
1214
- } catch {
1215
- sendJson(res, { agents: [], activeCount: 0, locks: [], tasks: [], availableTasks: 0, roles: [], roleOccupancy: [], handoffs: [] });
1207
+ status: 'error',
1208
+ readOnly: true,
1209
+ error: 'Coordination status is read-only',
1210
+ errorCode: 'COORDINATION_STATUS_READ_ONLY',
1211
+ }, 405);
1212
+ return;
1216
1213
  }
1214
+ const scope = parsedUrl.searchParams.get('scope') || 'project';
1215
+ const snapshot = await buildTeamSnapshot(state.dataDir, state.projectId, scope, state.mode);
1216
+ sendJson(res, snapshot, snapshot.status === 'error' ? 503 : 200);
1217
1217
  return;
1218
1218
  }
1219
1219
 
@@ -1237,13 +1237,12 @@ export async function startDashboard(
1237
1237
  server.listen(port, '127.0.0.1', () => {
1238
1238
  const url = `http://127.0.0.1:${port}`;
1239
1239
  const resolvedLabel = projectResolved ? 'resolved' : 'unresolved';
1240
- const modeLabel = isControlPlane ? 'Control Plane' : 'Standalone';
1240
+ const modeLabel = 'Standalone';
1241
1241
  console.error(` Memorix Dashboard [${modeLabel}]`);
1242
1242
  console.error(` ───────────────────────`);
1243
1243
  console.error(` Mode: ${modeLabel}`);
1244
1244
  console.error(` Project: ${projectName} (${projectId}) [${resolvedLabel}]`);
1245
1245
  console.error(` Local: ${url}`);
1246
- if (isControlPlane) console.error(` MCP: ${url}/mcp`);
1247
1246
  console.error(` Data dir: ${dataDir}`);
1248
1247
  console.error(`\n Press Ctrl+C to stop\n`);
1249
1248
  if (autoOpen) openBrowser(url);
@@ -135,7 +135,7 @@
135
135
  </svg>
136
136
  <span class="nav-label">Sessions</span>
137
137
  </button>
138
- <button class="nav-btn" data-page="team" title="Coordination" aria-label="Coordination">
138
+ <button class="nav-btn" data-page="team" title="Coordination Status" aria-label="Coordination Status">
139
139
  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
140
140
  <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/>
141
141
  <circle cx="9" cy="7" r="4"/>
@@ -210,6 +210,10 @@ export type LLMConfigScope = 'memory' | 'agent';
210
210
 
211
211
  export interface InitLLMOptions {
212
212
  scope?: LLMConfigScope;
213
+ /** Resolve project TOML/.env instead of relying on process.cwd(). */
214
+ projectRoot?: string | null;
215
+ /** Override the home directory used for global TOML/.env resolution. */
216
+ homeDir?: string;
213
217
  }
214
218
 
215
219
  /**
@@ -218,20 +222,29 @@ export interface InitLLMOptions {
218
222
  */
219
223
  export function initLLM(options: InitLLMOptions = {}): LLMConfig | null {
220
224
  const scope = options.scope ?? 'memory';
221
- const apiKey = scope === 'agent' ? getAgentLLMApiKey() : getLLMApiKey();
225
+ const configOptions = { projectRoot: options.projectRoot, homeDir: options.homeDir };
226
+ const apiKey = scope === 'agent'
227
+ ? getAgentLLMApiKey(configOptions)
228
+ : getLLMApiKey(configOptions);
222
229
  if (!apiKey) {
223
230
  currentConfig = null;
224
231
  return null;
225
232
  }
226
233
 
227
- const provider = (scope === 'agent' ? getAgentLLMProvider() : getLLMProvider()) as LLMConfig['provider'];
234
+ const provider = (scope === 'agent'
235
+ ? getAgentLLMProvider(configOptions)
236
+ : getLLMProvider(configOptions)) as LLMConfig['provider'];
228
237
  const defaults = PROVIDER_DEFAULTS[provider] ?? PROVIDER_DEFAULTS.openai;
229
238
 
230
239
  currentConfig = {
231
240
  provider,
232
241
  apiKey,
233
- model: scope === 'agent' ? getAgentLLMModel(defaults.model) : getLLMModel(defaults.model),
234
- baseUrl: scope === 'agent' ? getAgentLLMBaseUrl(defaults.baseUrl) : getLLMBaseUrl(defaults.baseUrl),
242
+ model: scope === 'agent'
243
+ ? getAgentLLMModel(defaults.model, configOptions)
244
+ : getLLMModel(defaults.model, configOptions),
245
+ baseUrl: scope === 'agent'
246
+ ? getAgentLLMBaseUrl(defaults.baseUrl, configOptions)
247
+ : getLLMBaseUrl(defaults.baseUrl, configOptions),
235
248
  };
236
249
 
237
250
  return currentConfig;
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Small MCP 2026-07-28 discovery adapter for the pinned v1 SDK.
3
+ *
4
+ * The v1 SDK can route an extension request through its low-level Server, but
5
+ * it does not implement the modern discovery/cache negotiation itself. Keep
6
+ * this adapter deliberately narrow: advertise discovery and the existing
7
+ * legacy tool handlers, while leaving unsupported extensions unadvertised.
8
+ */
9
+
10
+ import { RequestSchema, type ServerCapabilities } from '@modelcontextprotocol/sdk/types.js';
11
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
12
+ import { z } from 'zod/v4';
13
+
14
+ export const MCP_MODERN_PROTOCOL_VERSION = '2026-07-28';
15
+ export const MCP_LEGACY_PROTOCOL_VERSION = '2025-11-25';
16
+ export const MCP_OLDEST_LEGACY_PROTOCOL_VERSION = '2024-11-05';
17
+ export const MCP_SERVER_DISCOVER_METHOD = 'server/discover';
18
+ export const MCP_SERVER_INFO_META_KEY = 'io.modelcontextprotocol/serverInfo';
19
+
20
+ const ServerDiscoverRequestSchema = RequestSchema.extend({
21
+ method: z.literal(MCP_SERVER_DISCOVER_METHOD),
22
+ params: z.looseObject({}).optional(),
23
+ });
24
+
25
+ export interface McpServerInfo {
26
+ name: string;
27
+ version: string;
28
+ }
29
+
30
+ export interface McpDiscoverResult {
31
+ resultType: 'complete';
32
+ supportedVersions: string[];
33
+ capabilities: ServerCapabilities;
34
+ _meta: {
35
+ [MCP_SERVER_INFO_META_KEY]: McpServerInfo;
36
+ };
37
+ ttlMs: number;
38
+ cacheScope: 'public' | 'private';
39
+ }
40
+
41
+ const registeredServers = new WeakSet<McpServer>();
42
+
43
+ /** Build the wire result without exposing unsupported cache/task features. */
44
+ export function buildMcpDiscoverResult(server: McpServer, serverInfo: McpServerInfo): McpDiscoverResult {
45
+ // SDK 1.30 exposes this method at runtime but marks it private in its
46
+ // declaration file. The discovery response must reflect the capabilities
47
+ // already registered on this exact low-level Server instance.
48
+ const getCapabilities = (server.server as unknown as {
49
+ getCapabilities: () => ServerCapabilities;
50
+ }).getCapabilities.bind(server.server);
51
+ return {
52
+ resultType: 'complete',
53
+ supportedVersions: [
54
+ MCP_MODERN_PROTOCOL_VERSION,
55
+ MCP_LEGACY_PROTOCOL_VERSION,
56
+ MCP_OLDEST_LEGACY_PROTOCOL_VERSION,
57
+ ],
58
+ capabilities: getCapabilities(),
59
+ _meta: {
60
+ [MCP_SERVER_INFO_META_KEY]: { ...serverInfo },
61
+ },
62
+ // v1 has no response-cache negotiation. A zero private TTL is the
63
+ // conservative result and prevents clients from inventing a shared cache.
64
+ ttlMs: 0,
65
+ cacheScope: 'private',
66
+ };
67
+ }
68
+
69
+ /**
70
+ * Register `server/discover` on the v1 low-level Server.
71
+ *
72
+ * This is idempotent for a single McpServer instance because HTTP/session
73
+ * setup can revisit the registration boundary while reusing an object.
74
+ */
75
+ export function registerMcpDiscovery(server: McpServer, serverInfo: McpServerInfo): void {
76
+ if (registeredServers.has(server)) return;
77
+
78
+ server.server.setRequestHandler(ServerDiscoverRequestSchema, async () => (
79
+ buildMcpDiscoverResult(server, serverInfo)
80
+ ));
81
+ registeredServers.add(server);
82
+ }
@@ -1,7 +1,8 @@
1
1
  /** MCP compatibility diagnostics shared by HTTP and CLI-facing checks. */
2
2
 
3
3
  export const CURRENT_MCP_PROTOCOL_VERSION = '2026-07-28';
4
- export const LEGACY_MCP_PROTOCOL_VERSION = '2024-11-05';
4
+ export const LEGACY_MCP_PROTOCOL_VERSION = '2025-11-25';
5
+ export const OLDEST_LEGACY_MCP_PROTOCOL_VERSION = '2024-11-05';
5
6
  export const MCP_SERVER_NAME = 'io.github.AVIDS2/memorix';
6
7
 
7
8
  export interface McpDiagnostics {
@@ -9,13 +10,17 @@ export interface McpDiagnostics {
9
10
  protocol: {
10
11
  current: string;
11
12
  legacy: string;
13
+ discovery: 'supported';
14
+ versionlessRequests: 'legacy-compatible';
15
+ modernResultMetadata: 'discovery-only';
12
16
  statelessRequests: 'supported' | 'compatibility-only' | 'unsupported';
13
17
  statefulStreamableHttp: 'supported';
14
18
  };
15
19
  capabilities: {
16
20
  tasks: 'supported' | 'unsupported';
17
21
  polling: 'supported' | 'unsupported';
18
- listCache: 'supported';
22
+ subscriptions: 'supported' | 'unsupported';
23
+ listCache: 'supported' | 'unsupported';
19
24
  explicitProjectHandle: 'supported';
20
25
  };
21
26
  }
@@ -27,13 +32,17 @@ export function getMcpDiagnostics(): McpDiagnostics {
27
32
  protocol: {
28
33
  current: CURRENT_MCP_PROTOCOL_VERSION,
29
34
  legacy: LEGACY_MCP_PROTOCOL_VERSION,
30
- statelessRequests: 'supported',
35
+ discovery: 'supported',
36
+ versionlessRequests: 'legacy-compatible',
37
+ modernResultMetadata: 'discovery-only',
38
+ statelessRequests: 'compatibility-only',
31
39
  statefulStreamableHttp: 'supported',
32
40
  },
33
41
  capabilities: {
34
42
  tasks: 'unsupported',
35
43
  polling: 'unsupported',
36
- listCache: 'supported',
44
+ subscriptions: 'unsupported',
45
+ listCache: 'unsupported',
37
46
  explicitProjectHandle: 'supported',
38
47
  },
39
48
  };
package/src/server.ts CHANGED
@@ -62,6 +62,7 @@ import {
62
62
  type ProjectBindingController,
63
63
  type ProjectBindingSource,
64
64
  } from './server/request-context.js';
65
+ import { registerMcpDiscovery } from './server/mcp-compat.js';
65
66
 
66
67
  // ── Timeout budgets for LLM-heavy paths ──────────────────────────
67
68
  const FORMATION_TIMEOUT_MS = parseFormationTimeoutMs(process.env.MEMORIX_FORMATION_TIMEOUT_MS); // Formation pipeline (extract+resolve+evaluate)
@@ -198,7 +199,7 @@ function createDeterministicInstanceId(projectId: string, agentType: string, age
198
199
  * Create and configure the Memorix MCP Server.
199
200
  */
200
201
  /** Optional shared TeamStore — passed by serve-http so all sessions share state */
201
- export interface SharedTeamInstances {
202
+ export interface SharedTeamStore {
202
203
  teamStore: import('./team/team-store.js').TeamStore;
203
204
  }
204
205
 
@@ -245,7 +246,7 @@ export function shouldAwaitProjectRuntime(toolName: string): boolean {
245
246
  export async function createMemorixServer(
246
247
  cwd?: string,
247
248
  existingServer?: McpServer,
248
- sharedTeam?: SharedTeamInstances,
249
+ sharedTeam?: SharedTeamStore,
249
250
  options: CreateMemorixServerOptions = {},
250
251
  ): Promise<{
251
252
  server: McpServer;
@@ -593,10 +594,12 @@ export async function createMemorixServer(
593
594
  };
594
595
 
595
596
  // Create MCP server (or use existing one from roots-aware flow)
596
- const server = existingServer ?? new McpServer({
597
+ const serverInfo = {
597
598
  name: 'memorix',
598
599
  version: typeof __MEMORIX_VERSION__ !== 'undefined' ? __MEMORIX_VERSION__ : '1.0.1',
599
- });
600
+ };
601
+ const server = existingServer ?? new McpServer(serverInfo);
602
+ registerMcpDiscovery(server, serverInfo);
600
603
 
601
604
  const originalRegisterTool = server.registerTool.bind(server);
602
605
  server.registerTool = ((name: string, ...args: unknown[]) => {
@@ -4420,7 +4423,7 @@ export async function createMemorixServer(
4420
4423
  console.error(`[memorix] Dashboard staticDir: ${staticDir}`);
4421
4424
 
4422
4425
  // Start in background (non-blocking), disable auto-open (we'll open it ourselves)
4423
- startDashboard(projectDir, portNum, staticDir, project.id, project.name, false, undefined, project.rootPath, projectResolved)
4426
+ startDashboard(projectDir, portNum, staticDir, project.id, project.name, false, project.rootPath, projectResolved)
4424
4427
  .then(() => { dashboardRunning = true; })
4425
4428
  .catch((err) => { console.error('[memorix] Dashboard error:', err); dashboardRunning = false; });
4426
4429
 
@@ -4786,7 +4789,7 @@ export async function createMemorixServer(
4786
4789
  server.registerTool(
4787
4790
  'team_task',
4788
4791
  {
4789
- title: 'Task Board',
4792
+ title: 'Task Status',
4790
4793
  description:
4791
4794
  'Create, claim, complete, or list tasks in the team task board. Supports dependencies. ' +
4792
4795
  'Action "create": create a task. Action "claim": assign to yourself (atomic, race-safe). ' +