couchloop-eq-mcp 2.0.2 → 2.0.3

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.
@@ -2,84 +2,109 @@
2
2
  * MCP Tools - Public API
3
3
  *
4
4
  * This module exports only the PRIMARY tools that users should see.
5
- * All granular tools are internal engines used by these primary tools.
5
+ * Consolidated from 10 4 tools for clarity and reduced LLM misrouting.
6
6
  *
7
- * PUBLIC TOOLS (10):
8
- * 0. couchloop - Intent router (discoverability layer for loose commands)
9
- * 1. guard - Invisible per-turn governance (called automatically by companion skill)
10
- * 2. verify - Pre-delivery verification (catches AI hallucinations, validates packages)
11
- * 2. status - Dashboard (session progress, history, context, protection)
12
- * 3. conversation - AI conversation with guided journeys and session memory
13
- * 4. brainstorm - Dev thinking partner (reflective questioning, architecture, trade-offs)
14
- * 5. code_review - Complete code analysis (security, quality, AI errors)
15
- * 6. package_audit - Complete dependency audit (validation, versions, upgrades)
16
- * 7. remember - Smart context capture (checkpoints, insights, decisions)
17
- * 8. protect - File protection and safety features
7
+ * PUBLIC TOOLS (4):
8
+ * 1. memory - HERO: save & recall insights, checkpoints, decisions (Supabase-backed)
9
+ * 2. conversation - Emotional support, guided journeys, crisis detection (shrink-chat backend)
10
+ * 3. review - Unified: code review + package audit + pre-delivery verification
11
+ * 4. status - Dashboard (session progress, history, context, preferences)
12
+ *
13
+ * INTERNAL (auto-triggered, not user-facing):
14
+ * - guard - Per-response governance (runs in withPolicy wrapper)
15
+ *
16
+ * REMOVED:
17
+ * - couchloop - Router added latency; LLMs route directly with good descriptions
18
+ * - brainstorm - Returned static system prompt; LLMs brainstorm natively
19
+ * - protect - Broken on Railway (read-only /app filesystem)
18
20
  */
19
- // V2 Orchestration imports
20
- import { couchloopV2Tool } from './couchloop-v2.js';
21
- import { registerTools } from './intent-router.js';
22
21
  import { ToolRegistry } from '../core/registry/registry.js';
23
- // Legacy tool imports (will be registered in V2 registry)
22
+ // Tool handler imports
24
23
  import { sendMessage } from './sendMessage.js';
25
24
  import { createSession, resumeSession } from './session.js';
26
25
  import { endSession } from './session-manager.js';
27
26
  import { handleComprehensiveCodeReview } from './comprehensive-code-review.js';
28
27
  import { handleComprehensivePackageAudit } from './comprehensive-package-audit.js';
29
28
  import { handleSmartContext } from './smart-context.js';
30
- import { protectFiles, getProtectionStatus, listBackups, rollbackFile, enableCodeFreeze, disableCodeFreeze, } from './protect-files.js';
31
29
  import { listJourneys, getJourneyStatus } from './journey.js';
32
30
  import { getCheckpoints } from './checkpoint.js';
33
31
  import { getInsights, getUserContext } from './insight.js';
34
- import { verifyTool } from './verify.js';
32
+ import { handleVerify } from './verify.js';
35
33
  import { statusTool } from './status.js';
36
- import { guardTool } from './guard.js';
37
34
  import { runToolWithPolicy } from '../policy/index.js';
38
35
  import { logger } from '../utils/logger.js';
39
36
  // ============================================================
40
- // PRIMARY TOOL DEFINITIONS
41
- // These are the only tools visible to users
37
+ // PRIMARY TOOL DEFINITIONS (4 public tools)
42
38
  // ============================================================
43
- // Brainstorm system prompt - reflective questioning to help developers arrive at their own solutions
44
- const BRAINSTORM_SYSTEM_PROMPT = `You are a reflective thinking partner for developers. Your primary role is to ask insightful questions that help developers discover their own best solution — but you also provide concrete analysis when they've narrowed down options.
45
-
46
- DETECT THE MODE:
47
- 1. EXPLORATION: User has a vague idea or open-ended problem Ask questions to help them think
48
- 2. COMPARISON: User presents 2-3 specific options (e.g., "Redis vs Memcached?") → Clarify context briefly, then provide analysis
49
-
50
- FOR EXPLORATION MODE:
51
- - Ask clarifying questions before suggesting anything
52
- - Surface assumptions they may not have questioned
53
- - Break complex problems into smaller, answerable pieces
54
- - Ask 1-3 focused questions per response (not a barrage)
55
-
56
- QUESTION PATTERNS:
57
- 1. SCOPE: "What problem are you really trying to solve?" / "Who is this for?"
58
- 2. CONSTRAINTS: "What's your timeline?" / "What existing systems does this need to work with?"
59
- 3. TRADE-OFFS: "If you had to choose between X and Y, which matters more?"
60
- 4. ASSUMPTIONS: "What are you assuming about the user?" / "Have you validated that?"
61
- 5. DECOMPOSITION: "What's the riskiest part?" / "What could you build first to learn more?"
62
-
63
- FOR COMPARISON MODE (user asks "A vs B?" or "should I use X or Y?"):
64
- 1. Ask 1-2 quick clarifying questions about their specific context (scale, team experience, existing stack)
65
- 2. Then provide a structured comparison:
66
- - Key differences that matter for their use case
67
- - When to choose each option
68
- - Your recommendation given what you know about their context
69
- - Caveats or "it depends" factors they should verify
70
- 3. Be direct. Don't just list pros/cons — give them an actionable recommendation with reasoning.
71
-
72
- RESPONSE STYLE:
73
- - Start with understanding, not solutioning
74
- - Summarize their thinking back periodically
75
- - When they present options, acknowledge you'll help them decide (not just explore forever)
76
- - Be concise — developers want signal, not fluff
77
-
78
- Remember: The best solutions come from the developer's own understanding of their context. Your job is to help them think clearly AND give them useful analysis when they're ready for it.`;
39
+ // ── 1. MEMORY (hero feature registered first) ─────────────────────────────
40
+ const memoryTool = {
41
+ definition: {
42
+ name: 'memory',
43
+ description: 'Save and retrieve context, insights, checkpoints, and decisions across conversations. Prevents AI amnesia. Use action "save" to store, "recall" to retrieve, "list" to browse.',
44
+ annotations: {
45
+ readOnlyHint: false,
46
+ destructiveHint: false,
47
+ idempotentHint: false,
48
+ openWorldHint: false,
49
+ },
50
+ inputSchema: {
51
+ type: 'object',
52
+ properties: {
53
+ content: {
54
+ type: 'string',
55
+ description: 'What to save or search for when recalling',
56
+ },
57
+ action: {
58
+ type: 'string',
59
+ enum: ['save', 'recall', 'list'],
60
+ description: 'save: store new context. recall: retrieve previously stored insights/checkpoints/decisions. list: browse all saved items.',
61
+ },
62
+ type: {
63
+ type: 'string',
64
+ enum: ['checkpoint', 'insight', 'decision', 'requirement', 'constraint', 'pattern'],
65
+ description: 'Type of context (for save action affects categorization)',
66
+ },
67
+ tags: {
68
+ type: 'array',
69
+ items: { type: 'string' },
70
+ description: 'Tags for categorization (for save action)',
71
+ },
72
+ session_id: {
73
+ type: 'string',
74
+ description: 'Session to associate with',
75
+ },
76
+ },
77
+ required: [],
78
+ },
79
+ },
80
+ handler: async (args) => {
81
+ const action = args.action || 'save';
82
+ const sessionId = args.session_id;
83
+ switch (action) {
84
+ case 'recall': {
85
+ const checkpoints = await getCheckpoints({ session_id: sessionId });
86
+ const insights = await getInsights({ session_id: sessionId, limit: 10 });
87
+ const userContext = await getUserContext({ include_recent_insights: true, include_session_history: true });
88
+ return { checkpoints, insights, user_context: userContext };
89
+ }
90
+ case 'list':
91
+ return getInsights({ session_id: sessionId, limit: 20 });
92
+ case 'save':
93
+ default:
94
+ return handleSmartContext({
95
+ content: args.content,
96
+ type: args.type || 'insight',
97
+ tags: args.tags,
98
+ session_id: args.session_id,
99
+ });
100
+ }
101
+ },
102
+ };
103
+ // ── 2. CONVERSATION ──────────────────────────────────────────────────────────
79
104
  const conversationTool = {
80
105
  definition: {
81
106
  name: 'conversation',
82
- description: 'Start or continue an AI conversation with built-in crisis detection, guided self-reflection journeys, and session memory. Includes brainstorm mode for dev ideation. Triggers: "end session", "start session", "wrap up", "done for now", "talk", "chat", "feeling", "stressed", "help me", "brainstorm", "think through", "map out feature".',
107
+ description: 'Emotional support, guided self-reflection journeys, and wellness conversations with crisis detection. Routes to therapeutic AI backend.',
83
108
  annotations: {
84
109
  readOnlyHint: false,
85
110
  destructiveHint: false,
@@ -91,12 +116,12 @@ const conversationTool = {
91
116
  properties: {
92
117
  message: {
93
118
  type: 'string',
94
- description: 'Your message',
119
+ description: 'Your message for the therapeutic conversation',
95
120
  },
96
121
  action: {
97
122
  type: 'string',
98
123
  enum: ['send', 'start', 'end', 'resume', 'status'],
99
- description: 'Action: send (default), start new session, end session, resume previous, or get status',
124
+ description: 'send (default), start new session, end session, resume previous, or get status',
100
125
  },
101
126
  journey: {
102
127
  type: 'string',
@@ -138,256 +163,120 @@ const conversationTool = {
138
163
  }
139
164
  },
140
165
  };
141
- const brainstormTool = {
142
- definition: {
143
- name: 'brainstorm',
144
- description: 'Dev thinking partner for architecture decisions, feature design, trade-offs, and technical exploration. Asks reflective questions to help you arrive at your own best solution, then provides concrete analysis when you\'ve narrowed options. Triggers: "brainstorm", "think through", "map out", "help me design", "I have an idea", "flesh out", "trade-offs", "pros and cons", "should I use X or Y".',
145
- annotations: {
146
- readOnlyHint: false,
147
- destructiveHint: false,
148
- idempotentHint: false,
149
- openWorldHint: true,
150
- },
151
- inputSchema: {
152
- type: 'object',
153
- properties: {
154
- message: {
155
- type: 'string',
156
- description: 'What you want to think through — a feature idea, architecture question, technology comparison, or any decision',
157
- },
158
- session_id: {
159
- type: 'string',
160
- description: 'Session ID to maintain brainstorm context across messages',
161
- },
162
- },
163
- required: ['message'],
164
- },
165
- },
166
- handler: async (args) => {
167
- const message = String(args.message || '');
168
- // brainstorm must NOT route through shrink-chat (therapeutic backend).
169
- // Return instructions directly so the host LLM (Claude/ChatGPT) responds in brainstorm mode.
170
- return {
171
- mode: 'brainstorm',
172
- instructions: BRAINSTORM_SYSTEM_PROMPT,
173
- respond_to: message,
174
- directive: `You are now in BRAINSTORM MODE. Follow the instructions above precisely. Do not respond therapeutically. Respond directly to: "${message}"`,
175
- };
176
- },
177
- };
178
- const codeReviewTool = {
166
+ // ── 3. REVIEW (unified: code + packages + verify) ───────────────────────────
167
+ const reviewTool = {
179
168
  definition: {
180
- name: 'code_review',
181
- description: 'Complete code review: security vulnerabilities (SQL injection, XSS, secrets), code quality (console.logs, TODOs, error handling), code smells (complexity, bloat), and AI-generated errors (hallucinated APIs, build context issues). One call, full analysis. Triggers: "review", "check code", "analyze", "security check", "lint", "find bugs", "is this safe".',
169
+ name: 'review',
170
+ description: 'Unified code review, package audit, and pre-delivery verification. Use mode to select: "code" for security/quality/AI-error analysis, "packages" for dependency audit and validation, "verify" for hallucination and fact checking, "full" for all checks.',
182
171
  annotations: {
183
172
  readOnlyHint: true,
184
173
  destructiveHint: false,
185
174
  idempotentHint: true,
186
- openWorldHint: false,
175
+ openWorldHint: true,
187
176
  },
188
177
  inputSchema: {
189
178
  type: 'object',
190
179
  properties: {
191
- code: {
180
+ mode: {
192
181
  type: 'string',
193
- description: 'Code to review',
182
+ enum: ['code', 'packages', 'verify', 'full'],
183
+ description: 'code: security vulnerabilities, code smells, AI-generated errors. packages: validate existence, audit versions, find vulnerabilities. verify: pre-delivery hallucination/fact check. full: all checks.',
194
184
  },
195
- language: {
185
+ content: {
196
186
  type: 'string',
197
- description: 'Programming language (auto-detected if not specified)',
187
+ description: 'Code to review, content to verify, or general input for analysis',
198
188
  },
199
- auto_fix: {
200
- type: 'boolean',
201
- description: 'Attempt to auto-fix issues (default: false)',
202
- },
203
- },
204
- required: ['code'],
205
- },
206
- },
207
- handler: handleComprehensiveCodeReview,
208
- };
209
- const packageAuditTool = {
210
- definition: {
211
- name: 'package_audit',
212
- description: 'Complete dependency audit: validates packages exist and are legitimate (catches typosquatting), checks for outdated versions and security vulnerabilities, generates upgrade reports with migration guides and breaking changes. Triggers: "audit", "check dependencies", "outdated", "vulnerable packages", "upgrade", "npm audit", "security scan".',
213
- annotations: {
214
- readOnlyHint: true,
215
- destructiveHint: false,
216
- idempotentHint: true,
217
- openWorldHint: true,
218
- },
219
- inputSchema: {
220
- type: 'object',
221
- properties: {
222
189
  packages: {
223
190
  type: 'array',
224
191
  items: { type: 'string' },
225
- description: 'Package names to audit',
192
+ description: 'Package names to audit (for packages mode)',
193
+ },
194
+ language: {
195
+ type: 'string',
196
+ description: 'Programming language (auto-detected if not specified)',
226
197
  },
227
198
  registry: {
228
199
  type: 'string',
229
200
  enum: ['npm', 'pypi', 'maven', 'cargo', 'go', 'nuget', 'gem'],
230
201
  description: 'Package registry (default: npm)',
231
202
  },
232
- },
233
- required: ['packages'],
234
- },
235
- },
236
- handler: handleComprehensivePackageAudit,
237
- };
238
- const rememberTool = {
239
- definition: {
240
- name: 'remember',
241
- description: 'Capture and preserve important context from conversations. Automatically routes to the right storage: checkpoints for progress, insights for realizations, context for technical decisions. Prevents AI amnesia across conversations. Triggers: "save", "remember this", "checkpoint", "note", "don\'t forget", "keep track", "save progress", "log this".',
242
- annotations: {
243
- readOnlyHint: false,
244
- destructiveHint: false,
245
- idempotentHint: false,
246
- openWorldHint: false,
247
- },
248
- inputSchema: {
249
- type: 'object',
250
- properties: {
251
- content: {
252
- type: 'string',
253
- description: 'What to remember',
254
- },
255
- type: {
256
- type: 'string',
257
- enum: ['checkpoint', 'insight', 'decision', 'requirement', 'constraint', 'pattern'],
258
- description: 'Type of context (affects where it\'s stored)',
259
- },
260
- tags: {
261
- type: 'array',
262
- items: { type: 'string' },
263
- description: 'Tags for categorization',
264
- },
265
- action: {
266
- type: 'string',
267
- enum: ['save', 'recall', 'list'],
268
- description: 'Action: save (default), recall previous context, or list saved items',
269
- },
270
- session_id: {
271
- type: 'string',
272
- description: 'Session to associate with',
203
+ auto_fix: {
204
+ type: 'boolean',
205
+ description: 'Attempt to auto-fix issues (default: false, code mode only)',
273
206
  },
274
207
  },
275
- required: ['content'],
208
+ required: ['mode'],
276
209
  },
277
210
  },
278
211
  handler: async (args) => {
279
- const action = args.action || 'save';
280
- const sessionId = args.session_id;
281
- switch (action) {
282
- case 'recall': {
283
- // Get checkpoints and insights
284
- const checkpoints = await getCheckpoints({ session_id: sessionId });
285
- const insights = await getInsights({ session_id: sessionId, limit: 10 });
286
- const userContext = await getUserContext({ include_recent_insights: true, include_session_history: true });
287
- return {
288
- checkpoints,
289
- insights,
290
- user_context: userContext,
291
- };
292
- }
293
- case 'list':
294
- return getInsights({ session_id: sessionId, limit: 20 });
295
- case 'save':
296
- default:
297
- return handleSmartContext({
298
- content: args.content,
299
- type: args.type || 'insight',
300
- tags: args.tags,
301
- session_id: args.session_id,
212
+ const mode = args.mode;
213
+ switch (mode) {
214
+ case 'code':
215
+ if (!args.content) {
216
+ return { success: false, error: 'content is required for code review mode' };
217
+ }
218
+ return handleComprehensiveCodeReview({
219
+ code: args.content,
220
+ language: args.language,
221
+ auto_fix: args.auto_fix,
302
222
  });
303
- }
304
- },
305
- };
306
- const protectTool = {
307
- definition: {
308
- name: 'protect',
309
- description: 'File protection and safety: prevent accidental deletions, create automatic backups, rollback changes, enable code freeze mode. Essential for safe AI-assisted development. Triggers: "backup", "protect", "freeze", "rollback", "undo", "restore", "safe mode".',
310
- annotations: {
311
- readOnlyHint: false,
312
- destructiveHint: false,
313
- idempotentHint: false,
314
- openWorldHint: false,
315
- },
316
- inputSchema: {
317
- type: 'object',
318
- properties: {
319
- action: {
320
- type: 'string',
321
- enum: ['check', 'backup', 'rollback', 'freeze', 'unfreeze', 'status', 'history'],
322
- description: 'Action to perform',
323
- },
324
- path: {
325
- type: 'string',
326
- description: 'File path (for check, backup, rollback)',
327
- },
328
- operation: {
329
- type: 'string',
330
- enum: ['delete', 'overwrite', 'move'],
331
- description: 'Operation type (for check)',
332
- },
333
- backup_id: {
334
- type: 'string',
335
- description: 'Backup ID (for rollback)',
336
- },
337
- },
338
- required: ['action'],
339
- },
340
- },
341
- handler: async (args) => {
342
- const action = args.action;
343
- // path is required for check and backup — validate before delegating
344
- if ((action === 'check' || action === 'backup') && !args.path) {
345
- return {
346
- success: false,
347
- error: `path is required for action='${action}'`,
348
- action,
349
- };
350
- }
351
- switch (action) {
352
- case 'check':
353
- return protectFiles({
354
- operation: args.operation,
355
- path: args.path,
223
+ case 'packages':
224
+ if (!args.packages) {
225
+ return { success: false, error: 'packages array is required for packages mode' };
226
+ }
227
+ return handleComprehensivePackageAudit({
228
+ packages: args.packages,
229
+ registry: args.registry,
356
230
  });
357
- case 'status':
358
- return getProtectionStatus({});
359
- case 'history':
360
- return listBackups({});
361
- case 'rollback':
362
- return rollbackFile({ backup_id: args.backup_id });
363
- case 'freeze':
364
- return enableCodeFreeze({});
365
- case 'unfreeze':
366
- return disableCodeFreeze({});
367
- case 'backup':
368
- // Create a backup by doing a protected check
369
- return protectFiles({
370
- operation: 'overwrite',
371
- path: args.path,
231
+ case 'verify':
232
+ if (!args.content) {
233
+ return { success: false, error: 'content is required for verify mode' };
234
+ }
235
+ return handleVerify({
236
+ type: 'all',
237
+ content: args.content,
238
+ language: args.language,
239
+ registry: args.registry,
372
240
  });
241
+ case 'full': {
242
+ const results = {};
243
+ if (args.content) {
244
+ results.code_review = await handleComprehensiveCodeReview({
245
+ code: args.content,
246
+ language: args.language,
247
+ auto_fix: args.auto_fix,
248
+ });
249
+ results.verification = await handleVerify({
250
+ type: 'all',
251
+ content: args.content,
252
+ language: args.language,
253
+ });
254
+ }
255
+ if (args.packages) {
256
+ results.package_audit = await handleComprehensivePackageAudit({
257
+ packages: args.packages,
258
+ registry: args.registry,
259
+ });
260
+ }
261
+ if (!args.content && !args.packages) {
262
+ return { success: false, error: 'content or packages required for full mode' };
263
+ }
264
+ return { success: true, mode: 'full', results };
265
+ }
373
266
  default:
374
- return { error: `Unknown action: ${action}` };
267
+ return { success: false, error: `Unknown mode: ${mode}. Use: code, packages, verify, or full` };
375
268
  }
376
269
  },
377
270
  };
378
271
  // ============================================================
379
- // EXPORT ONLY PRIMARY TOOLS
272
+ // EXPORT ONLY PRIMARY TOOLS (4 tools)
380
273
  // ============================================================
381
274
  // ─────────────────────────────────────────────────────────────────────────────
382
275
  // Policy wrapper helpers
383
276
  // ─────────────────────────────────────────────────────────────────────────────
384
277
  /**
385
278
  * Wrap a tool handler so every call goes through:
386
- * execute → sanitize → verify-if-required → normalize → log
387
- *
388
- * The wrapped handler is used for BOTH direct MCP calls and intent-router
389
- * (couchloop) delegated calls, since registerTools() stores references to
390
- * these same handler functions.
279
+ * execute → sanitize → guard-if-clinical → verify-if-required → normalize → log
391
280
  */
392
281
  function withPolicy(toolName, handler, routedVia = 'direct') {
393
282
  return async (args, _routedVia) => {
@@ -402,30 +291,21 @@ function withPolicy(toolName, handler, routedVia = 'direct') {
402
291
  };
403
292
  }
404
293
  export async function setupTools() {
405
- // Register tools with V2 registry for health tracking
406
294
  const registry = ToolRegistry.getInstance();
407
- // Domain-specific tools — handlers wrapped with the policy layer.
295
+ // 4 public tools — memory first (hero feature), then conversation, review, status
408
296
  const rawDomainTools = [
409
- guardTool,
410
- verifyTool,
411
- statusTool,
297
+ memoryTool,
412
298
  conversationTool,
413
- brainstormTool,
414
- codeReviewTool,
415
- packageAuditTool,
416
- rememberTool,
417
- protectTool,
299
+ reviewTool,
300
+ statusTool,
418
301
  ];
419
302
  const domainTools = rawDomainTools.map((tool) => {
420
303
  const wrappedHandler = withPolicy(tool.definition.name, tool.handler);
421
- // Register real handler into V2 registry.
422
- // Uses existing metadata if already registered (from initializeToolRegistry),
423
- // otherwise falls back to a minimal metadata stub so health tracking still works.
424
304
  const toolName = tool.definition.name;
425
305
  const existing = registry.getTool(toolName);
426
306
  const metadata = existing?.metadata ?? {
427
307
  toolName,
428
- version: '2.0.0',
308
+ version: '2.1.0',
429
309
  capabilities: [],
430
310
  latencyProfile: { p50Ms: 500, p95Ms: 1000 },
431
311
  constraints: { idempotent: false, safeParallel: false, supportsCache: false },
@@ -437,18 +317,9 @@ export async function setupTools() {
437
317
  handler: wrappedHandler,
438
318
  };
439
319
  });
440
- // Register domain tools for legacy compatibility (will be removed later)
441
- registerTools(domainTools);
442
- // V2 ORCHESTRATION: couchloopV2 replaces the old intent router
443
- // It now handles routing through the new modular pipeline:
444
- // Request → Classify → Policy → Plan → Execute → Compose
445
- const tools = [
446
- couchloopV2Tool, // V2 orchestration with 100% rollout!
447
- ...domainTools,
448
- ];
449
- logger.info(`🚀 V2 ORCHESTRATION ACTIVE: ${tools.length} primary MCP tools`);
450
- logger.info('Architecture: Request → Classify → Policy → Plan → Execute → Compose');
451
- logger.info('Performance: 60%+ direct routing, 33% faster P95 latency');
320
+ // No router tool LLMs route directly with clear descriptions
321
+ const tools = [...domainTools];
322
+ logger.info(`Registered ${tools.length} public MCP tools: ${tools.map(t => t.definition.name).join(', ')}`);
452
323
  return tools;
453
324
  }
454
325
  // Also export for internal use