monomind 2.5.4 → 2.5.6

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 (74) hide show
  1. package/README.md +10 -3
  2. package/package.json +1 -1
  3. package/packages/@monomind/cli/.claude/commands/mastermind/topology.md +121 -0
  4. package/packages/@monomind/cli/.claude/commands/mastermind.md +121 -102
  5. package/packages/@monomind/cli/.claude/helpers/control-start.cjs +22 -2
  6. package/packages/@monomind/cli/.claude/helpers/control-stop.cjs +95 -0
  7. package/packages/@monomind/cli/.claude/helpers/graphify-freshen.cjs +6 -0
  8. package/packages/@monomind/cli/.claude/helpers/hook-handler.cjs +13 -5
  9. package/packages/@monomind/cli/.claude/helpers/utils/monograph.cjs +33 -10
  10. package/packages/@monomind/cli/.claude/helpers/utils/system-pressure.cjs +46 -0
  11. package/packages/@monomind/cli/.claude/settings.json +448 -0
  12. package/packages/@monomind/cli/README.md +10 -3
  13. package/packages/@monomind/cli/bin/cli.js +16 -6
  14. package/packages/@monomind/cli/bin/mcp-server.js +17 -8
  15. package/packages/@monomind/cli/dist/src/commands/agent-lifecycle.js +1 -1
  16. package/packages/@monomind/cli/dist/src/commands/agent-ops.js +1 -1
  17. package/packages/@monomind/cli/dist/src/commands/cleanup.js +53 -1
  18. package/packages/@monomind/cli/dist/src/commands/config.js +1 -1
  19. package/packages/@monomind/cli/dist/src/commands/hooks-coverage-commands.js +1 -1
  20. package/packages/@monomind/cli/dist/src/commands/hooks.js +1 -1
  21. package/packages/@monomind/cli/dist/src/commands/index.js +4 -0
  22. package/packages/@monomind/cli/dist/src/commands/init-wizard.js +3 -3
  23. package/packages/@monomind/cli/dist/src/commands/init.js +2 -2
  24. package/packages/@monomind/cli/dist/src/commands/mcp.js +2 -2
  25. package/packages/@monomind/cli/dist/src/commands/memory-admin.js +8 -7
  26. package/packages/@monomind/cli/dist/src/commands/memory-list.js +2 -2
  27. package/packages/@monomind/cli/dist/src/commands/memory.d.ts +1 -1
  28. package/packages/@monomind/cli/dist/src/commands/memory.js +4 -4
  29. package/packages/@monomind/cli/dist/src/commands/neural-core.js +1 -1
  30. package/packages/@monomind/cli/dist/src/commands/performance.js +8 -9
  31. package/packages/@monomind/cli/dist/src/commands/status.js +2 -2
  32. package/packages/@monomind/cli/dist/src/commands/swarm.js +1 -1
  33. package/packages/@monomind/cli/dist/src/index.js +1 -1
  34. package/packages/@monomind/cli/dist/src/init/claudemd-generator.js +2 -2
  35. package/packages/@monomind/cli/dist/src/init/executor.js +13 -6
  36. package/packages/@monomind/cli/dist/src/init/helpers-generator.js +66 -3
  37. package/packages/@monomind/cli/dist/src/init/shared-instructions-generator.d.ts +1 -1
  38. package/packages/@monomind/cli/dist/src/init/shared-instructions-generator.js +1 -1
  39. package/packages/@monomind/cli/dist/src/init/statusline-generator.d.ts +1 -1
  40. package/packages/@monomind/cli/dist/src/init/statusline-generator.js +2 -2
  41. package/packages/@monomind/cli/dist/src/init/types.d.ts +1 -1
  42. package/packages/@monomind/cli/dist/src/knowledge/document-pipeline.d.ts +1 -1
  43. package/packages/@monomind/cli/dist/src/knowledge/document-pipeline.js +3 -3
  44. package/packages/@monomind/cli/dist/src/mcp-client.d.ts +13 -51
  45. package/packages/@monomind/cli/dist/src/mcp-client.js +79 -134
  46. package/packages/@monomind/cli/dist/src/mcp-server.js +4 -3
  47. package/packages/@monomind/cli/dist/src/mcp-tools/browser-tools.js +45 -19
  48. package/packages/@monomind/cli/dist/src/mcp-tools/guidance-tools.js +1 -1
  49. package/packages/@monomind/cli/dist/src/mcp-tools/hive-mind-tools.js +2 -2
  50. package/packages/@monomind/cli/dist/src/mcp-tools/hooks-intelligence.js +2 -2
  51. package/packages/@monomind/cli/dist/src/mcp-tools/knowledge-tools.js +1 -1
  52. package/packages/@monomind/cli/dist/src/mcp-tools/monograph-compat.js +2 -2
  53. package/packages/@monomind/cli/dist/src/mcp-tools/monograph-tools.js +22 -9
  54. package/packages/@monomind/cli/dist/src/mcp-tools/neural-tools.d.ts +1 -1
  55. package/packages/@monomind/cli/dist/src/mcp-tools/neural-tools.js +3 -3
  56. package/packages/@monomind/cli/dist/src/mcp-tools/system-tools.js +1 -1
  57. package/packages/@monomind/cli/dist/src/memory/embedding-operations.js +2 -2
  58. package/packages/@monomind/cli/dist/src/memory/hnsw-operations.js +53 -26
  59. package/packages/@monomind/cli/dist/src/memory/intelligence.js +2 -2
  60. package/packages/@monomind/cli/dist/src/memory/memory-bridge.d.ts +7 -3
  61. package/packages/@monomind/cli/dist/src/memory/memory-bridge.js +24 -10
  62. package/packages/@monomind/cli/dist/src/memory/memory-crud.js +2 -2
  63. package/packages/@monomind/cli/dist/src/memory/memory-initializer.d.ts +2 -2
  64. package/packages/@monomind/cli/dist/src/memory/memory-initializer.js +6 -6
  65. package/packages/@monomind/cli/dist/src/memory/memory-read.js +4 -4
  66. package/packages/@monomind/cli/dist/src/orgrt/daemon.js +12 -6
  67. package/packages/@monomind/cli/dist/src/orgrt/mailbox.d.ts +1 -0
  68. package/packages/@monomind/cli/dist/src/orgrt/mailbox.js +4 -0
  69. package/packages/@monomind/cli/dist/src/orgrt/policy.js +1 -1
  70. package/packages/@monomind/cli/dist/src/orgrt/server.js +25 -0
  71. package/packages/@monomind/cli/dist/src/ui/dashboard.html +327 -611
  72. package/packages/@monomind/cli/dist/src/ui/server.mjs +165 -22
  73. package/packages/@monomind/cli/package.json +1 -1
  74. package/scripts/growth-content-calendar.mjs +140 -0
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Auto-detects project profile and generates:
5
5
  * 1. .agents/shared_instructions.md — prepended to every agent prompt
6
- * 2. Memory seeds — pre-loaded into LanceDB so agents start with project best practices
6
+ * 2. Memory seeds — pre-loaded into SQLite so agents start with project best practices
7
7
  */
8
8
  import * as fs from 'node:fs';
9
9
  import * as path from 'node:path';
@@ -18,7 +18,7 @@ import type { InitOptions } from './types.js';
18
18
  * 🏗️ DDD Domains [●●○○○] 2/5 ⚡ HNSW 150x
19
19
  * 🤖 Swarm ◉ [ 5/15] 👥 2 🪝 10/17 🟢 CVE 3/3 💾 4MB 🧠 63%
20
20
  * 🔧 Architecture ADRs ●71% │ DDD ● 13% │ Security ●CLEAN
21
- * 📊 LanceDB Vectors ●3104⚡ │ Size 216KB │ Tests ●6 (~24 cases) │ MCP ●1/1
21
+ * 📊 SQLite Vectors ●3104⚡ │ Size 216KB │ Tests ●6 (~24 cases) │ MCP ●1/1
22
22
  */
23
23
  export declare function generateStatuslineScript(options: InitOptions): string;
24
24
  export declare function generateStatuslineHook(options: InitOptions): string;
@@ -17,7 +17,7 @@
17
17
  * 🏗️ DDD Domains [●●○○○] 2/5 ⚡ HNSW 150x
18
18
  * 🤖 Swarm ◉ [ 5/15] 👥 2 🪝 10/17 🟢 CVE 3/3 💾 4MB 🧠 63%
19
19
  * 🔧 Architecture ADRs ●71% │ DDD ● 13% │ Security ●CLEAN
20
- * 📊 LanceDB Vectors ●3104⚡ │ Size 216KB │ Tests ●6 (~24 cases) │ MCP ●1/1
20
+ * 📊 SQLite Vectors ●3104⚡ │ Size 216KB │ Tests ●6 (~24 cases) │ MCP ●1/1
21
21
  */
22
22
  export function generateStatuslineScript(options) {
23
23
  const maxAgents = options.runtime.maxAgents;
@@ -595,7 +595,7 @@ function getActiveAgent() {
595
595
  } catch { return null; }
596
596
  }
597
597
 
598
- // Memory (LanceDB) stats — count real entries
598
+ // Memory (SQLite) stats — count real entries
599
599
  function getMemoryStats() {
600
600
  let vectorCount = 0;
601
601
  let dbSizeKB = 0;
@@ -66,7 +66,7 @@ export interface HooksConfig {
66
66
  export interface SkillsConfig {
67
67
  /** Include core skills (swarm, mastermind, monodesign, monomotion) */
68
68
  core: boolean;
69
- /** Include memory/LanceDB skills */
69
+ /** Include memory/SQLite skills */
70
70
  memory: boolean;
71
71
  /** Include GitHub integration skills */
72
72
  github: boolean;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Document Pipeline — wires text extraction, chunking, embedding, and LanceDB storage
2
+ * Document Pipeline — wires text extraction, chunking, embedding, and SQLite storage
3
3
  * into an end-to-end ingest/search/export pipeline for the Second Brain.
4
4
  *
5
5
  * @module v1/cli/knowledge/document-pipeline
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Document Pipeline — wires text extraction, chunking, embedding, and LanceDB storage
2
+ * Document Pipeline — wires text extraction, chunking, embedding, and SQLite storage
3
3
  * into an end-to-end ingest/search/export pipeline for the Second Brain.
4
4
  *
5
5
  * @module v1/cli/knowledge/document-pipeline
@@ -412,8 +412,8 @@ export function listDocuments(rootDir = process.cwd(), scope) {
412
412
  }
413
413
  export async function removeDocument(filePath, scope = 'shared', rootDir = process.cwd()) {
414
414
  removeMetadataEntry(rootDir, path.resolve(filePath), scope);
415
- // LanceDB cleanup: bridge doesn't expose delete-by-key, so metadata removal is sufficient.
416
- // Orphaned LanceDB entries get swept on next full re-index or TTL expiry.
415
+ // SQLite cleanup: bridge doesn't expose delete-by-key, so metadata removal is sufficient.
416
+ // Orphaned SQLite entries get swept on next full re-index or TTL expiry.
417
417
  }
418
418
  // ── OKF Export ─────────────────────────────────────────────────────
419
419
  export async function exportToOKF(outputDir, rootDir = process.cwd(), scope = 'shared') {
@@ -4,8 +4,8 @@
4
4
  * Thin wrapper for calling MCP tools from CLI commands.
5
5
  * Implements ADR-005: MCP-First API Design - CLI as thin wrapper around MCP tools
6
6
  *
7
- * This provides a simple interface for CLI commands to call MCP tools without
8
- * containing hardcoded business logic. All business logic lives in MCP tool handlers.
7
+ * Tool modules are lazy-loaded on first use to avoid pulling ~300 tools'
8
+ * transitive dependencies into the heap at import time.
9
9
  */
10
10
  import type { MCPTool } from './mcp-tools/types.js';
11
11
  export declare function isToolDisabled(toolName: string, cwd?: string): boolean;
@@ -19,76 +19,38 @@ export declare class MCPClientError extends Error {
19
19
  }
20
20
  /**
21
21
  * Call an MCP tool by name with input parameters
22
- *
23
- * @param toolName - Name of the MCP tool (e.g., 'agent_spawn', 'swarm_init')
24
- * @param input - Input parameters for the tool
25
- * @param context - Optional tool context
26
- * @returns Promise resolving to tool result
27
- * @throws {MCPClientError} If tool not found or execution fails
28
- *
29
- * @example
30
- * ```typescript
31
- * // Spawn an agent
32
- * const result = await callMCPTool('agent_spawn', {
33
- * agentType: 'coder',
34
- * priority: 'normal'
35
- * });
36
- *
37
- * // Initialize swarm
38
- * const swarm = await callMCPTool('swarm_init', {
39
- * topology: 'hierarchical-mesh',
40
- * maxAgents: 15
41
- * });
42
- * ```
43
22
  */
44
23
  export declare function callMCPTool<T = unknown>(toolName: string, input?: Record<string, unknown>, context?: Record<string, unknown>): Promise<T>;
45
24
  /**
46
25
  * Get tool metadata by name
47
- *
48
- * @param toolName - Name of the MCP tool
49
- * @returns Tool metadata or undefined if not found
50
26
  */
51
- export declare function getToolMetadata(toolName: string): Omit<MCPTool, 'handler'> | undefined;
27
+ export declare function getToolMetadata(toolName: string): Promise<Omit<MCPTool, 'handler'> | undefined>;
52
28
  /**
53
- * List all available MCP tools
54
- *
55
- * @param category - Optional category filter
56
- * @returns Array of tool metadata
29
+ * List all available MCP tools (loads all categories on first call)
57
30
  */
58
- export declare function listMCPTools(category?: string): Array<Omit<MCPTool, 'handler'> & {
31
+ export declare function listMCPTools(category?: string): Promise<Array<Omit<MCPTool, 'handler'> & {
59
32
  enabled: boolean;
60
- }>;
33
+ }>>;
61
34
  /**
62
35
  * Return all registered tools including their handler functions, excluding
63
- * any disabled via `mcp toggle`. Used by startHttpServer() to register tools
64
- * with the HTTP/WS MCP server, so a disabled tool is never exposed to
65
- * external MCP clients after the next server start.
36
+ * any disabled via `mcp toggle`. Loads all categories on first call.
66
37
  */
67
- export declare function getAllMCPTools(): MCPTool[];
38
+ export declare function getAllMCPTools(): Promise<MCPTool[]>;
68
39
  /**
69
- * Check if an MCP tool exists
70
- *
71
- * @param toolName - Name of the MCP tool
72
- * @returns True if tool exists
40
+ * Check if an MCP tool exists (checks loaded categories + known prefixes)
73
41
  */
74
- export declare function hasTool(toolName: string): boolean;
42
+ export declare function hasTool(toolName: string): Promise<boolean>;
75
43
  /**
76
44
  * Get all tool categories
77
- *
78
- * @returns Array of unique categories
79
45
  */
80
- export declare function getToolCategories(): string[];
46
+ export declare function getToolCategories(): Promise<string[]>;
81
47
  /**
82
48
  * Validate tool input against schema
83
- *
84
- * @param toolName - Name of the MCP tool
85
- * @param input - Input to validate
86
- * @returns Validation result with errors if any
87
49
  */
88
- export declare function validateToolInput(toolName: string, input: Record<string, unknown>): {
50
+ export declare function validateToolInput(toolName: string, input: Record<string, unknown>): Promise<{
89
51
  valid: boolean;
90
52
  errors?: string[];
91
- };
53
+ }>;
92
54
  declare const _default: {
93
55
  callMCPTool: typeof callMCPTool;
94
56
  getToolMetadata: typeof getToolMetadata;
@@ -4,51 +4,16 @@
4
4
  * Thin wrapper for calling MCP tools from CLI commands.
5
5
  * Implements ADR-005: MCP-First API Design - CLI as thin wrapper around MCP tools
6
6
  *
7
- * This provides a simple interface for CLI commands to call MCP tools without
8
- * containing hardcoded business logic. All business logic lives in MCP tool handlers.
7
+ * Tool modules are lazy-loaded on first use to avoid pulling ~300 tools'
8
+ * transitive dependencies into the heap at import time.
9
9
  */
10
10
  import { existsSync, readFileSync } from 'node:fs';
11
11
  import { join } from 'node:path';
12
- // Import MCP tool handlers from local package
13
- import { agentTools } from './mcp-tools/agent-tools.js';
14
- import { swarmTools } from './mcp-tools/swarm-tools.js';
15
- import { memoryTools } from './mcp-tools/memory-tools.js';
16
- import { configTools } from './mcp-tools/config-tools.js';
17
- import { hooksTools } from './mcp-tools/hooks-tools.js';
18
- import { taskTools } from './mcp-tools/task-tools.js';
19
- import { sessionTools } from './mcp-tools/session-tools.js';
20
- import { hiveMindTools } from './mcp-tools/hive-mind-tools.js';
21
- import { analyzeTools } from './mcp-tools/analyze-tools.js';
22
- import { embeddingsTools } from './mcp-tools/embeddings-tools.js';
23
- import { claimsTools } from './mcp-tools/claims-tools.js';
24
- import { securityTools } from './mcp-tools/security-tools.js';
25
- import { transferTools } from './mcp-tools/transfer-tools.js';
26
- // V2 Compatibility tools
27
- import { systemTools } from './mcp-tools/system-tools.js';
28
- import { terminalTools } from './mcp-tools/terminal-tools.js';
29
- import { neuralTools } from './mcp-tools/neural-tools.js';
30
- import { performanceTools } from './mcp-tools/performance-tools.js';
31
- import { githubTools } from './mcp-tools/github-tools.js';
32
- import { daaTools } from './mcp-tools/daa-tools.js';
33
- import { browserTools } from './mcp-tools/browser-tools.js';
34
- import { guidanceTools } from './mcp-tools/guidance-tools.js';
35
- import { autopilotTools } from './mcp-tools/autopilot-tools.js';
36
- // Knowledge graph tools (graphify — deprecated shims + monograph native)
37
- import { graphifyTools } from './mcp-tools/graphify-tools.js';
38
- import { monographTools } from './mcp-tools/monograph-tools.js';
39
- // Coverage-aware routing tools
40
- import { coverageRouterTools } from './monovector/coverage-tools.js';
41
- // Quality and Coherence core tools
42
- import { qualityTools } from './mcp-tools/quality-tools.js';
43
- import { coherenceTools } from './mcp-tools/coherence-tools.js';
44
- // Second Brain knowledge tools
45
- import { knowledgeTools } from './mcp-tools/knowledge-tools.js';
46
12
  /**
47
13
  * MCP Tool Registry
48
- * Maps tool names to their handler functions
14
+ * Maps tool names to their handler functions — populated lazily per category.
49
15
  */
50
16
  const TOOL_REGISTRY = new Map();
51
- // Register all tools — refuse silent overrides without an explicit override flag.
52
17
  function registerTools(tools, options = {}) {
53
18
  for (const tool of tools) {
54
19
  if (TOOL_REGISTRY.has(tool.name) && !options.override) {
@@ -57,46 +22,59 @@ function registerTools(tools, options = {}) {
57
22
  TOOL_REGISTRY.set(tool.name, tool);
58
23
  }
59
24
  }
60
- // Initialize registry with all available tools
61
- registerTools([
62
- ...agentTools,
63
- ...swarmTools,
64
- ...memoryTools,
65
- ...configTools,
66
- ...hooksTools,
67
- ...taskTools,
68
- ...sessionTools,
69
- ...hiveMindTools,
70
- ...analyzeTools,
71
- ...embeddingsTools,
72
- ...claimsTools,
73
- ...securityTools,
74
- ...transferTools,
75
- // V2 Compatibility tools
76
- ...systemTools,
77
- ...terminalTools,
78
- ...neuralTools,
79
- ...performanceTools,
80
- ...githubTools,
81
- ...daaTools,
82
- ...browserTools,
83
- // Guidance & discovery tools
84
- ...guidanceTools,
85
- // Autopilot persistent completion tools
86
- ...autopilotTools,
87
- // Knowledge graph native monograph tools (replaces Python graphify)
88
- ...monographTools,
89
- // Graphify deprecated shims proxy to monograph_* tools
90
- ...graphifyTools,
91
- // Coverage-aware routing tools
92
- ...coverageRouterTools,
93
- // Quality tools
94
- ...qualityTools,
95
- // Coherence tools
96
- ...coherenceTools,
97
- // Second Brain knowledge tools
98
- ...knowledgeTools,
99
- ]);
25
+ const CATEGORY_LOADERS = {
26
+ agent: async () => (await import('./mcp-tools/agent-tools.js')).agentTools,
27
+ swarm: async () => (await import('./mcp-tools/swarm-tools.js')).swarmTools,
28
+ memory: async () => (await import('./mcp-tools/memory-tools.js')).memoryTools,
29
+ config: async () => (await import('./mcp-tools/config-tools.js')).configTools,
30
+ hooks: async () => (await import('./mcp-tools/hooks-tools.js')).hooksTools,
31
+ task: async () => (await import('./mcp-tools/task-tools.js')).taskTools,
32
+ session: async () => (await import('./mcp-tools/session-tools.js')).sessionTools,
33
+ 'hive-mind': async () => (await import('./mcp-tools/hive-mind-tools.js')).hiveMindTools,
34
+ analyze: async () => (await import('./mcp-tools/analyze-tools.js')).analyzeTools,
35
+ embeddings: async () => (await import('./mcp-tools/embeddings-tools.js')).embeddingsTools,
36
+ claims: async () => (await import('./mcp-tools/claims-tools.js')).claimsTools,
37
+ monofence: async () => (await import('./mcp-tools/security-tools.js')).securityTools,
38
+ transfer: async () => (await import('./mcp-tools/transfer-tools.js')).transferTools,
39
+ system: async () => (await import('./mcp-tools/system-tools.js')).systemTools,
40
+ terminal: async () => (await import('./mcp-tools/terminal-tools.js')).terminalTools,
41
+ neural: async () => (await import('./mcp-tools/neural-tools.js')).neuralTools,
42
+ performance: async () => (await import('./mcp-tools/performance-tools.js')).performanceTools,
43
+ github: async () => (await import('./mcp-tools/github-tools.js')).githubTools,
44
+ daa: async () => (await import('./mcp-tools/daa-tools.js')).daaTools,
45
+ browser: async () => (await import('./mcp-tools/browser-tools.js')).browserTools,
46
+ guidance: async () => (await import('./mcp-tools/guidance-tools.js')).guidanceTools,
47
+ autopilot: async () => (await import('./mcp-tools/autopilot-tools.js')).autopilotTools,
48
+ monograph: async () => (await import('./mcp-tools/monograph-tools.js')).monographTools,
49
+ graphify: async () => (await import('./mcp-tools/graphify-tools.js')).graphifyTools,
50
+ coverage: async () => (await import('./monovector/coverage-tools.js')).coverageRouterTools,
51
+ quality: async () => (await import('./mcp-tools/quality-tools.js')).qualityTools,
52
+ coherence: async () => (await import('./mcp-tools/coherence-tools.js')).coherenceTools,
53
+ knowledge: async () => (await import('./mcp-tools/knowledge-tools.js')).knowledgeTools,
54
+ // system-tools.ts also exports tools with mcp_ and config_ prefixes
55
+ mcp: async () => (await import('./mcp-tools/system-tools.js')).systemTools,
56
+ };
57
+ const loadedCategories = new Set();
58
+ async function ensureCategory(category) {
59
+ if (loadedCategories.has(category))
60
+ return;
61
+ const loader = CATEGORY_LOADERS[category];
62
+ if (!loader)
63
+ return;
64
+ loadedCategories.add(category);
65
+ registerTools(await loader(), { override: true });
66
+ }
67
+ function categoryFromToolName(name) {
68
+ const idx = name.indexOf('_');
69
+ return idx > 0 ? name.slice(0, idx) : name;
70
+ }
71
+ let _allLoaded = false;
72
+ async function ensureAllLoaded() {
73
+ if (_allLoaded)
74
+ return;
75
+ _allLoaded = true;
76
+ await Promise.all(Object.keys(CATEGORY_LOADERS).map(cat => ensureCategory(cat)));
77
+ }
100
78
  /**
101
79
  * Disabled-tools registry (`mcp toggle`)
102
80
  *
@@ -136,30 +114,11 @@ export class MCPClientError extends Error {
136
114
  }
137
115
  /**
138
116
  * Call an MCP tool by name with input parameters
139
- *
140
- * @param toolName - Name of the MCP tool (e.g., 'agent_spawn', 'swarm_init')
141
- * @param input - Input parameters for the tool
142
- * @param context - Optional tool context
143
- * @returns Promise resolving to tool result
144
- * @throws {MCPClientError} If tool not found or execution fails
145
- *
146
- * @example
147
- * ```typescript
148
- * // Spawn an agent
149
- * const result = await callMCPTool('agent_spawn', {
150
- * agentType: 'coder',
151
- * priority: 'normal'
152
- * });
153
- *
154
- * // Initialize swarm
155
- * const swarm = await callMCPTool('swarm_init', {
156
- * topology: 'hierarchical-mesh',
157
- * maxAgents: 15
158
- * });
159
- * ```
160
117
  */
161
118
  export async function callMCPTool(toolName, input = {}, context) {
162
- // Look up tool in registry
119
+ // Lazy-load the tool's category if not yet loaded
120
+ const cat = categoryFromToolName(toolName);
121
+ await ensureCategory(cat);
163
122
  const tool = TOOL_REGISTRY.get(toolName);
164
123
  if (!tool) {
165
124
  throw new MCPClientError(`MCP tool not found: ${toolName}`, toolName);
@@ -168,27 +127,22 @@ export async function callMCPTool(toolName, input = {}, context) {
168
127
  throw new MCPClientError(`MCP tool '${toolName}' is disabled. Re-enable with: mcp toggle --enable ${toolName}`, toolName);
169
128
  }
170
129
  try {
171
- // Call the tool handler
172
130
  const result = await tool.handler(input, context);
173
131
  return result;
174
132
  }
175
133
  catch (error) {
176
- // Wrap and re-throw with context
177
134
  throw new MCPClientError(`Failed to execute MCP tool '${toolName}': ${error instanceof Error ? error.message : String(error)}`, toolName, error instanceof Error ? error : undefined);
178
135
  }
179
136
  }
180
137
  /**
181
138
  * Get tool metadata by name
182
- *
183
- * @param toolName - Name of the MCP tool
184
- * @returns Tool metadata or undefined if not found
185
139
  */
186
- export function getToolMetadata(toolName) {
140
+ export async function getToolMetadata(toolName) {
141
+ const cat = categoryFromToolName(toolName);
142
+ await ensureCategory(cat);
187
143
  const tool = TOOL_REGISTRY.get(toolName);
188
- if (!tool) {
144
+ if (!tool)
189
145
  return undefined;
190
- }
191
- // Return everything except the handler function
192
146
  return {
193
147
  name: tool.name,
194
148
  description: tool.description,
@@ -201,12 +155,10 @@ export function getToolMetadata(toolName) {
201
155
  };
202
156
  }
203
157
  /**
204
- * List all available MCP tools
205
- *
206
- * @param category - Optional category filter
207
- * @returns Array of tool metadata
158
+ * List all available MCP tools (loads all categories on first call)
208
159
  */
209
- export function listMCPTools(category) {
160
+ export async function listMCPTools(category) {
161
+ await ensureAllLoaded();
210
162
  const tools = Array.from(TOOL_REGISTRY.values());
211
163
  const disabled = loadDisabledTools();
212
164
  const filtered = category
@@ -226,45 +178,39 @@ export function listMCPTools(category) {
226
178
  }
227
179
  /**
228
180
  * Return all registered tools including their handler functions, excluding
229
- * any disabled via `mcp toggle`. Used by startHttpServer() to register tools
230
- * with the HTTP/WS MCP server, so a disabled tool is never exposed to
231
- * external MCP clients after the next server start.
181
+ * any disabled via `mcp toggle`. Loads all categories on first call.
232
182
  */
233
- export function getAllMCPTools() {
183
+ export async function getAllMCPTools() {
184
+ await ensureAllLoaded();
234
185
  const disabled = loadDisabledTools();
235
186
  return Array.from(TOOL_REGISTRY.values()).filter(t => !disabled.has(t.name));
236
187
  }
237
188
  /**
238
- * Check if an MCP tool exists
239
- *
240
- * @param toolName - Name of the MCP tool
241
- * @returns True if tool exists
189
+ * Check if an MCP tool exists (checks loaded categories + known prefixes)
242
190
  */
243
- export function hasTool(toolName) {
191
+ export async function hasTool(toolName) {
192
+ const cat = categoryFromToolName(toolName);
193
+ await ensureCategory(cat);
244
194
  return TOOL_REGISTRY.has(toolName);
245
195
  }
246
196
  /**
247
197
  * Get all tool categories
248
- *
249
- * @returns Array of unique categories
250
198
  */
251
- export function getToolCategories() {
199
+ export async function getToolCategories() {
200
+ await ensureAllLoaded();
252
201
  const categories = new Set();
253
202
  TOOL_REGISTRY.forEach(tool => {
254
- if (tool.category) {
203
+ if (tool.category)
255
204
  categories.add(tool.category);
256
- }
257
205
  });
258
206
  return Array.from(categories).sort();
259
207
  }
260
208
  /**
261
209
  * Validate tool input against schema
262
- *
263
- * @param toolName - Name of the MCP tool
264
- * @param input - Input to validate
265
- * @returns Validation result with errors if any
266
210
  */
267
- export function validateToolInput(toolName, input) {
211
+ export async function validateToolInput(toolName, input) {
212
+ const cat = categoryFromToolName(toolName);
213
+ await ensureCategory(cat);
268
214
  const tool = TOOL_REGISTRY.get(toolName);
269
215
  if (!tool) {
270
216
  return {
@@ -272,7 +218,6 @@ export function validateToolInput(toolName, input) {
272
218
  errors: [`Tool '${toolName}' not found`],
273
219
  };
274
220
  }
275
- // Basic validation - check required fields
276
221
  const schema = tool.inputSchema;
277
222
  const errors = [];
278
223
  if (schema.required && Array.isArray(schema.required)) {
@@ -405,6 +405,7 @@ export class MCPServerManager extends EventEmitter {
405
405
  process.stdin.on('end', () => { void shutdown('stdin closed'); });
406
406
  process.on('SIGINT', () => { void shutdown('Received SIGINT'); });
407
407
  process.on('SIGTERM', () => { void shutdown('Received SIGTERM'); });
408
+ process.on('SIGHUP', () => { void shutdown('Received SIGHUP'); });
408
409
  // Mark as ready immediately for stdio
409
410
  this.emit('ready');
410
411
  }
@@ -437,7 +438,7 @@ export class MCPServerManager extends EventEmitter {
437
438
  },
438
439
  };
439
440
  case 'tools/list':
440
- const tools = listMCPTools();
441
+ const tools = await listMCPTools();
441
442
  return {
442
443
  jsonrpc: '2.0',
443
444
  id: message.id,
@@ -484,7 +485,7 @@ export class MCPServerManager extends EventEmitter {
484
485
  };
485
486
  }
486
487
  }
487
- if (!hasTool(toolName)) {
488
+ if (!await hasTool(toolName)) {
488
489
  return {
489
490
  jsonrpc: '2.0',
490
491
  id: message.id,
@@ -709,7 +710,7 @@ export class MCPServerManager extends EventEmitter {
709
710
  // Without this the server only exposes its 4 built-in tools.
710
711
  try {
711
712
  const { getAllMCPTools } = await import('./mcp-client.js');
712
- const registered = mcpServer.registerTools(getAllMCPTools());
713
+ const registered = mcpServer.registerTools(await getAllMCPTools());
713
714
  console.error(`[monomind-mcp] HTTP/WS server registered ${registered.registered} tools`);
714
715
  }
715
716
  catch (e) {
@@ -5,24 +5,21 @@
5
5
  * Sessions are keyed by session ID; each maps to a persistent CDP connection
6
6
  * on the configured port (default: MONOBROWSE_CDP_PORT env var or 9222).
7
7
  */
8
- const MAX_BROWSER_SESSIONS = 100;
9
- const SESSION_TTL_MS = 30 * 60 * 1000; // 30 minutes
8
+ const MAX_BROWSER_SESSIONS = 5;
9
+ const SESSION_TTL_MS = 10 * 60 * 1000; // 10 minutes
10
10
  // Session registry for multi-session support (sessionId → connection)
11
11
  const browserSessions = new Map();
12
12
  const connectionCache = new Map();
13
- function pruneExpiredSessions() {
13
+ async function pruneExpiredSessions() {
14
14
  const cutoff = Date.now() - SESSION_TTL_MS;
15
+ const port = Number(process.env['MONOBROWSE_CDP_PORT'] ?? 9222);
15
16
  for (const [id, info] of browserSessions) {
16
17
  if (new Date(info.lastActivity).getTime() < cutoff) {
17
18
  browserSessions.delete(id);
18
19
  const conn = connectionCache.get(id);
19
- if (conn) {
20
- try {
21
- conn.client.close();
22
- }
23
- catch { /* ignore */ }
24
- }
25
20
  connectionCache.delete(id);
21
+ if (conn)
22
+ await closeTarget(conn, port);
26
23
  }
27
24
  }
28
25
  }
@@ -40,7 +37,9 @@ async function getConnection(sessionId) {
40
37
  return conn;
41
38
  }
42
39
  catch {
40
+ const port = Number(process.env['MONOBROWSE_CDP_PORT'] ?? 9222);
43
41
  connectionCache.delete(sessionId);
42
+ await closeTarget(conn, port);
44
43
  }
45
44
  }
46
45
  const port = Number(process.env['MONOBROWSE_CDP_PORT'] ?? 9222);
@@ -50,16 +49,34 @@ async function getConnection(sessionId) {
50
49
  connectionCache.set(sessionId, conn);
51
50
  return conn;
52
51
  }
53
- function releaseConnection(sessionId) {
52
+ async function releaseConnection(sessionId) {
54
53
  const conn = connectionCache.get(sessionId);
54
+ connectionCache.delete(sessionId);
55
+ browserSessions.delete(sessionId);
55
56
  if (conn) {
56
- try {
57
- conn.client.close();
57
+ const port = Number(process.env['MONOBROWSE_CDP_PORT'] ?? 9222);
58
+ // Close the actual Chrome tab — just closing the WebSocket leaves the
59
+ // renderer process alive and consuming ~250MB+ RAM each. Awaited (not
60
+ // fire-and-forget) so a burst of session churn can't pile up concurrent
61
+ // in-flight closes and leak renderers when one fails silently.
62
+ await closeTarget(conn, port);
63
+ }
64
+ }
65
+ async function closeTarget(conn, port) {
66
+ try {
67
+ // Get the target ID for this session so we can close the tab
68
+ const result = await conn.client.send('Target.getTargetInfo', {}, conn.cdpSessionId);
69
+ const targetId = result?.targetInfo?.targetId;
70
+ if (targetId) {
71
+ // Close via HTTP endpoint — works even if the CDP session is stale
72
+ await fetch(`http://127.0.0.1:${port}/json/close/${encodeURIComponent(targetId)}`, { method: 'GET' });
58
73
  }
59
- catch { /* ignore */ }
60
74
  }
61
- connectionCache.delete(sessionId);
62
- browserSessions.delete(sessionId);
75
+ catch { /* best-effort */ }
76
+ try {
77
+ conn.client.close();
78
+ }
79
+ catch { /* ignore */ }
63
80
  }
64
81
  function ok(data = {}) {
65
82
  return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...data }, null, 2) }] };
@@ -195,13 +212,22 @@ export const browserTools = [
195
212
  catch (e) {
196
213
  return fail(e.message);
197
214
  }
198
- pruneExpiredSessions();
215
+ await pruneExpiredSessions();
199
216
  if (!browserSessions.has(sessionId)) {
217
+ // Refuse new sessions when system memory is critical
218
+ try {
219
+ const os = await import('node:os');
220
+ const freeRatio = os.freemem() / os.totalmem();
221
+ if (freeRatio < 0.10) {
222
+ return fail(`System memory critical (${Math.round(freeRatio * 100)}% free). Close existing browser sessions first.`);
223
+ }
224
+ }
225
+ catch { /* non-critical */ }
200
226
  if (browserSessions.size >= MAX_BROWSER_SESSIONS) {
201
227
  const oldest = [...browserSessions.entries()]
202
228
  .sort((a, b) => a[1].lastActivity.localeCompare(b[1].lastActivity))[0];
203
229
  if (oldest)
204
- releaseConnection(oldest[0]);
230
+ await releaseConnection(oldest[0]);
205
231
  }
206
232
  browserSessions.set(sessionId, {
207
233
  sessionId,
@@ -332,7 +358,7 @@ export const browserTools = [
332
358
  catch (e) {
333
359
  return fail(e.message);
334
360
  }
335
- releaseConnection(sessionId);
361
+ await releaseConnection(sessionId);
336
362
  return ok({ session: sessionId });
337
363
  },
338
364
  },
@@ -1075,7 +1101,7 @@ export const browserTools = [
1075
1101
  tags: ['session'],
1076
1102
  inputSchema: { type: 'object', properties: {} },
1077
1103
  handler: async () => {
1078
- pruneExpiredSessions();
1104
+ await pruneExpiredSessions();
1079
1105
  const sessions = Array.from(browserSessions.values());
1080
1106
  return ok({ sessions, count: sessions.length });
1081
1107
  },
@@ -76,7 +76,7 @@ const CAPABILITY_CATALOG = {
76
76
  },
77
77
  'memory-knowledge': {
78
78
  name: 'Memory & Knowledge',
79
- description: 'Persistent memory with ANN vector search, LanceDB storage, and embeddings.',
79
+ description: 'Persistent memory with ANN vector search, SQLite storage, and embeddings.',
80
80
  tools: ['memory_store', 'memory_retrieve', 'memory_search', 'memory_list', 'memory_delete', 'memory_init', 'memory_export', 'memory_import', 'memory_stats', 'memory_compact', 'memory_namespace'],
81
81
  commands: ['memory store', 'memory retrieve', 'memory search', 'memory list', 'memory delete', 'memory init'],
82
82
  agents: ['swarm-memory-manager', 'v1-memory-specialist'],