@vibecheckai/cli 3.9.1 → 4.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/README.md +1 -1
  2. package/bin/runners/context/generators/cursor-enhanced.js +99 -13
  3. package/mcp-server/.eslintrc.json +24 -0
  4. package/mcp-server/README.md +425 -135
  5. package/mcp-server/SPEC.md +583 -0
  6. package/mcp-server/configs/README.md +172 -0
  7. package/mcp-server/configs/claude-desktop-pro.json +31 -0
  8. package/mcp-server/configs/claude-desktop-with-workspace.json +25 -0
  9. package/mcp-server/configs/claude-desktop.json +19 -0
  10. package/mcp-server/configs/cursor-mcp.json +21 -0
  11. package/mcp-server/configs/windsurf-mcp.json +17 -0
  12. package/mcp-server/mcp-config.example.json +9 -0
  13. package/mcp-server/package-lock.json +1631 -0
  14. package/mcp-server/package.json +49 -34
  15. package/mcp-server/src/cli.ts +185 -0
  16. package/mcp-server/src/index.ts +85 -0
  17. package/mcp-server/src/server.ts +1933 -0
  18. package/mcp-server/src/services/cache-service.ts +466 -0
  19. package/mcp-server/src/services/cli-service.ts +345 -0
  20. package/mcp-server/src/services/context-manager.ts +717 -0
  21. package/mcp-server/src/services/firewall-service.ts +662 -0
  22. package/mcp-server/src/services/git-service.ts +671 -0
  23. package/mcp-server/src/services/index.ts +52 -0
  24. package/mcp-server/src/services/prompt-builder-service.ts +1031 -0
  25. package/mcp-server/src/services/session-service.ts +550 -0
  26. package/mcp-server/src/services/tier-service.ts +470 -0
  27. package/mcp-server/src/types.ts +351 -0
  28. package/mcp-server/tsconfig.json +16 -27
  29. package/package.json +6 -6
  30. package/mcp-server/.guardrail/audit/audit.log.jsonl +0 -2
  31. package/mcp-server/.specs/architecture.mdc +0 -90
  32. package/mcp-server/.specs/security.mdc +0 -30
  33. package/mcp-server/HARDENING_SUMMARY.md +0 -299
  34. package/mcp-server/agent-checkpoint.js +0 -364
  35. package/mcp-server/agent-firewall-interceptor.js +0 -500
  36. package/mcp-server/architect-tools.js +0 -707
  37. package/mcp-server/audit-mcp.js +0 -206
  38. package/mcp-server/authority-tools.js +0 -569
  39. package/mcp-server/codebase-architect-tools.js +0 -838
  40. package/mcp-server/conductor/conflict-resolver.js +0 -588
  41. package/mcp-server/conductor/execution-planner.js +0 -544
  42. package/mcp-server/conductor/index.js +0 -377
  43. package/mcp-server/conductor/lock-manager.js +0 -615
  44. package/mcp-server/conductor/request-queue.js +0 -550
  45. package/mcp-server/conductor/session-manager.js +0 -500
  46. package/mcp-server/conductor/tools.js +0 -510
  47. package/mcp-server/consolidated-tools.js +0 -1170
  48. package/mcp-server/deprecation-middleware.js +0 -282
  49. package/mcp-server/handlers/index.ts +0 -15
  50. package/mcp-server/handlers/tool-handler.ts +0 -593
  51. package/mcp-server/hygiene-tools.js +0 -428
  52. package/mcp-server/index-v1.js +0 -698
  53. package/mcp-server/index.js +0 -2940
  54. package/mcp-server/intelligence-tools.js +0 -664
  55. package/mcp-server/intent-drift-tools.js +0 -873
  56. package/mcp-server/intent-firewall-interceptor.js +0 -529
  57. package/mcp-server/lib/api-client.cjs +0 -13
  58. package/mcp-server/lib/cache-wrapper.cjs +0 -383
  59. package/mcp-server/lib/error-envelope.js +0 -138
  60. package/mcp-server/lib/executor.ts +0 -499
  61. package/mcp-server/lib/index.ts +0 -29
  62. package/mcp-server/lib/logger.cjs +0 -30
  63. package/mcp-server/lib/rate-limiter.js +0 -166
  64. package/mcp-server/lib/sandbox.test.ts +0 -519
  65. package/mcp-server/lib/sandbox.ts +0 -395
  66. package/mcp-server/lib/types.ts +0 -267
  67. package/mcp-server/logger.js +0 -173
  68. package/mcp-server/manifest.json +0 -473
  69. package/mcp-server/mdc-generator.js +0 -298
  70. package/mcp-server/premium-tools.js +0 -1275
  71. package/mcp-server/proof-tools.js +0 -571
  72. package/mcp-server/registry/tool-registry.js +0 -586
  73. package/mcp-server/registry/tools.json +0 -619
  74. package/mcp-server/registry.test.ts +0 -340
  75. package/mcp-server/test-mcp.js +0 -108
  76. package/mcp-server/test-tools.js +0 -36
  77. package/mcp-server/tests/tier-gating.test.js +0 -297
  78. package/mcp-server/tier-auth.js +0 -767
  79. package/mcp-server/tools/index.js +0 -72
  80. package/mcp-server/tools-reorganized.ts +0 -244
  81. package/mcp-server/tools-v3.js +0 -1004
  82. package/mcp-server/truth-context.js +0 -622
  83. package/mcp-server/truth-firewall-tools.js +0 -2183
  84. package/mcp-server/vibecheck-2.0-tools.js +0 -761
  85. package/mcp-server/vibecheck-mcp-server-3.2.0.tgz +0 -0
  86. package/mcp-server/vibecheck-tools.js +0 -1075
@@ -0,0 +1,1933 @@
1
+ /**
2
+ * VibeCheck MCP Server
3
+ * Provides tools for Agent Firewall, Prompt Builder, and CLI integration
4
+ *
5
+ * Tier Model v4.0:
6
+ * - FREE ($0/mo): Inspect & Observe - 11 commands
7
+ * - PRO ($49/mo): Fix, Prove & Enforce - 12 commands
8
+ */
9
+
10
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
11
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
12
+ import {
13
+ CallToolRequestSchema,
14
+ ListToolsRequestSchema,
15
+ ListResourcesRequestSchema,
16
+ ReadResourceRequestSchema,
17
+ ErrorCode,
18
+ McpError,
19
+ } from '@modelcontextprotocol/sdk/types.js';
20
+
21
+ import {
22
+ CliService,
23
+ FirewallService,
24
+ PromptBuilderService,
25
+ TierService,
26
+ SessionService,
27
+ GitService,
28
+ CacheService,
29
+ ContextManager,
30
+ } from './services/index.js';
31
+ import type { McpServerConfig, McpServerState, FirewallMode } from './types.js';
32
+
33
+ export class VibecheckMcpServer {
34
+ private server: Server;
35
+ private cliService: CliService;
36
+ private firewallService: FirewallService;
37
+ private promptBuilderService: PromptBuilderService;
38
+ private tierService: TierService;
39
+ private sessionService: SessionService;
40
+ private gitService: GitService;
41
+ private cacheService: CacheService;
42
+ private contextManager: ContextManager;
43
+ private config: McpServerConfig;
44
+ private state: McpServerState;
45
+
46
+ constructor(config: McpServerConfig = {}) {
47
+ this.config = config;
48
+ const workspacePath = config.workspacePath || process.cwd();
49
+
50
+ // Initialize core services
51
+ this.cliService = new CliService(workspacePath, config.cliPath);
52
+ this.firewallService = new FirewallService(this.cliService);
53
+ this.promptBuilderService = new PromptBuilderService(workspacePath);
54
+ this.tierService = new TierService();
55
+
56
+ // Initialize enhanced services
57
+ this.sessionService = new SessionService(workspacePath);
58
+ this.gitService = new GitService(workspacePath);
59
+ this.cacheService = new CacheService({ persistToDisk: true });
60
+ this.contextManager = new ContextManager(workspacePath);
61
+
62
+ // Initialize state
63
+ this.state = {
64
+ initialized: false,
65
+ cliAvailable: false,
66
+ firewallMode: config.defaultFirewallMode || 'off',
67
+ sessionId: `mcp_${Date.now()}`,
68
+ startTime: new Date().toISOString(),
69
+ };
70
+
71
+ // Create MCP server
72
+ this.server = new Server(
73
+ {
74
+ name: 'vibecheck-mcp-server',
75
+ version: '1.0.0',
76
+ },
77
+ {
78
+ capabilities: {
79
+ tools: {},
80
+ resources: {},
81
+ },
82
+ }
83
+ );
84
+
85
+ this.setupHandlers();
86
+ }
87
+
88
+ /**
89
+ * Check if a tool is allowed for the current tier
90
+ * Returns error response if not allowed
91
+ */
92
+ private checkTierAccess(toolName: string): { allowed: boolean; errorResponse?: { content: { type: 'text'; text: string }[]; isError: boolean } } {
93
+ const result = this.tierService.checkToolAccess(toolName);
94
+
95
+ if (result.allowed) {
96
+ return { allowed: true };
97
+ }
98
+
99
+ return {
100
+ allowed: false,
101
+ errorResponse: {
102
+ content: [{
103
+ type: 'text' as const,
104
+ text: this.tierService.formatTierError(result),
105
+ }],
106
+ isError: true,
107
+ },
108
+ };
109
+ }
110
+
111
+ private setupHandlers(): void {
112
+ // List available tools
113
+ this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
114
+ tools: [
115
+ // ═══════════════════════════════════════════════════════════════════════
116
+ // CLI Tools
117
+ // ═══════════════════════════════════════════════════════════════════════
118
+ {
119
+ name: 'vibecheck_doctor',
120
+ description: '[FREE] Run health check on the project. Checks CLI installation, environment, and project setup.',
121
+ inputSchema: {
122
+ type: 'object',
123
+ properties: {},
124
+ required: [],
125
+ },
126
+ },
127
+ {
128
+ name: 'vibecheck_audit',
129
+ description: '[FREE] Run a comprehensive security audit on the codebase. Returns findings with severity levels. FREE: 100 scans/month, 1000 files/scan. PRO: unlimited.',
130
+ inputSchema: {
131
+ type: 'object',
132
+ properties: {
133
+ path: {
134
+ type: 'string',
135
+ description: 'Optional path to audit (defaults to workspace root)',
136
+ },
137
+ severity: {
138
+ type: 'string',
139
+ enum: ['critical', 'high', 'medium', 'low'],
140
+ description: 'Minimum severity level to report',
141
+ },
142
+ },
143
+ required: [],
144
+ },
145
+ },
146
+ {
147
+ name: 'vibecheck_ship',
148
+ description: '[PRO] Get a ship verdict - whether the code is ready to deploy. Returns SHIP, WARN, or BLOCK.',
149
+ inputSchema: {
150
+ type: 'object',
151
+ properties: {},
152
+ required: [],
153
+ },
154
+ },
155
+ {
156
+ name: 'vibecheck_forge',
157
+ description: '[FREE] Generate AI rules (.cursorrules, CLAUDE.md) from the codebase analysis.',
158
+ inputSchema: {
159
+ type: 'object',
160
+ properties: {
161
+ output: {
162
+ type: 'string',
163
+ description: 'Output path for generated rules',
164
+ },
165
+ format: {
166
+ type: 'string',
167
+ enum: ['cursor', 'claude', 'all'],
168
+ description: 'Format of rules to generate',
169
+ },
170
+ },
171
+ required: [],
172
+ },
173
+ },
174
+ {
175
+ name: 'vibecheck_fix',
176
+ description: '[PRO] Plan or apply fixes for security findings. Use plan mode first to preview changes.',
177
+ inputSchema: {
178
+ type: 'object',
179
+ properties: {
180
+ mode: {
181
+ type: 'string',
182
+ enum: ['plan', 'apply'],
183
+ description: 'Whether to plan or apply fixes',
184
+ },
185
+ missionId: {
186
+ type: 'string',
187
+ description: 'Specific finding ID to fix',
188
+ },
189
+ },
190
+ required: ['mode'],
191
+ },
192
+ },
193
+ {
194
+ name: 'vibecheck_checkpoint',
195
+ description: '[PRO] Create, restore, or list code checkpoints (snapshots).',
196
+ inputSchema: {
197
+ type: 'object',
198
+ properties: {
199
+ action: {
200
+ type: 'string',
201
+ enum: ['create', 'restore', 'list'],
202
+ description: 'Checkpoint action',
203
+ },
204
+ id: {
205
+ type: 'string',
206
+ description: 'Checkpoint ID (for restore)',
207
+ },
208
+ },
209
+ required: ['action'],
210
+ },
211
+ },
212
+ {
213
+ name: 'vibecheck_packs',
214
+ description: '[FREE] Generate report bundle. FREE: HTML, MD, JSON. PRO: + SARIF, CSV, PDF.',
215
+ inputSchema: {
216
+ type: 'object',
217
+ properties: {
218
+ format: {
219
+ type: 'string',
220
+ enum: ['html', 'zip', 'json', 'sarif', 'csv', 'pdf'],
221
+ description: 'Output format (sarif, csv, pdf require PRO)',
222
+ },
223
+ },
224
+ required: [],
225
+ },
226
+ },
227
+ {
228
+ name: 'vibecheck_reality',
229
+ description: '[PRO] Run browser-based reality testing on a URL.',
230
+ inputSchema: {
231
+ type: 'object',
232
+ properties: {
233
+ url: {
234
+ type: 'string',
235
+ description: 'URL to test',
236
+ },
237
+ headless: {
238
+ type: 'boolean',
239
+ description: 'Run in headless mode',
240
+ },
241
+ },
242
+ required: ['url'],
243
+ },
244
+ },
245
+
246
+ // ═══════════════════════════════════════════════════════════════════════
247
+ // Firewall Tools
248
+ // ═══════════════════════════════════════════════════════════════════════
249
+ {
250
+ name: 'firewall_status',
251
+ description: '[FREE] Get current firewall status including mode, violation count, and current intent.',
252
+ inputSchema: {
253
+ type: 'object',
254
+ properties: {},
255
+ required: [],
256
+ },
257
+ },
258
+ {
259
+ name: 'firewall_set_mode',
260
+ description: '[FREE/PRO] Set firewall mode. "off" and "observe" are FREE. "enforce" requires PRO subscription.',
261
+ inputSchema: {
262
+ type: 'object',
263
+ properties: {
264
+ mode: {
265
+ type: 'string',
266
+ enum: ['off', 'observe', 'enforce'],
267
+ description: 'Firewall mode (enforce requires PRO)',
268
+ },
269
+ },
270
+ required: ['mode'],
271
+ },
272
+ },
273
+ {
274
+ name: 'firewall_set_intent',
275
+ description: '[PRO] Set the current intent before making code changes. Required in enforce mode.',
276
+ inputSchema: {
277
+ type: 'object',
278
+ properties: {
279
+ summary: {
280
+ type: 'string',
281
+ description: 'Brief description of what you intend to do',
282
+ },
283
+ constraints: {
284
+ type: 'array',
285
+ items: { type: 'string' },
286
+ description: 'List of constraints/boundaries for this intent',
287
+ },
288
+ template: {
289
+ type: 'string',
290
+ description: 'Use a predefined template instead of custom summary',
291
+ },
292
+ },
293
+ required: [],
294
+ },
295
+ },
296
+ {
297
+ name: 'firewall_get_intent',
298
+ description: '[PRO] Get the current intent that was set.',
299
+ inputSchema: {
300
+ type: 'object',
301
+ properties: {},
302
+ required: [],
303
+ },
304
+ },
305
+ {
306
+ name: 'firewall_clear_intent',
307
+ description: '[PRO] Clear the current intent.',
308
+ inputSchema: {
309
+ type: 'object',
310
+ properties: {},
311
+ required: [],
312
+ },
313
+ },
314
+ {
315
+ name: 'firewall_check',
316
+ description: '[PRO] Run a comprehensive shield check on the codebase.',
317
+ inputSchema: {
318
+ type: 'object',
319
+ properties: {},
320
+ required: [],
321
+ },
322
+ },
323
+ {
324
+ name: 'firewall_verify_claim',
325
+ description: '[PRO] Verify an AI claim against the current codebase state and intent.',
326
+ inputSchema: {
327
+ type: 'object',
328
+ properties: {
329
+ claim: {
330
+ type: 'string',
331
+ description: 'The claim to verify (e.g., "I only added a new route")',
332
+ },
333
+ context: {
334
+ type: 'string',
335
+ description: 'Additional context about the claim',
336
+ },
337
+ files: {
338
+ type: 'array',
339
+ items: { type: 'string' },
340
+ description: 'Files involved in the claim',
341
+ },
342
+ },
343
+ required: ['claim'],
344
+ },
345
+ },
346
+ {
347
+ name: 'firewall_gate_action',
348
+ description: '[PRO] Check if an action is allowed by the firewall. Use before performing sensitive operations.',
349
+ inputSchema: {
350
+ type: 'object',
351
+ properties: {
352
+ action: {
353
+ type: 'string',
354
+ description: 'Description of the action to perform',
355
+ },
356
+ category: {
357
+ type: 'string',
358
+ enum: ['read', 'write', 'execute', 'network', 'sensitive'],
359
+ description: 'Category of the action',
360
+ },
361
+ },
362
+ required: ['action', 'category'],
363
+ },
364
+ },
365
+ {
366
+ name: 'firewall_get_templates',
367
+ description: '[PRO] Get available intent templates for common tasks.',
368
+ inputSchema: {
369
+ type: 'object',
370
+ properties: {},
371
+ required: [],
372
+ },
373
+ },
374
+
375
+ // ═══════════════════════════════════════════════════════════════════════
376
+ // Prompt Builder Tools (FREE)
377
+ // ═══════════════════════════════════════════════════════════════════════
378
+ {
379
+ name: 'prompt_get_templates',
380
+ description: '[FREE] Get all available prompt templates, optionally filtered by category.',
381
+ inputSchema: {
382
+ type: 'object',
383
+ properties: {
384
+ category: {
385
+ type: 'string',
386
+ description: 'Filter by category (authentication, api, frontend, etc.)',
387
+ },
388
+ },
389
+ required: [],
390
+ },
391
+ },
392
+ {
393
+ name: 'prompt_get_categories',
394
+ description: '[FREE] Get all prompt categories with template counts.',
395
+ inputSchema: {
396
+ type: 'object',
397
+ properties: {},
398
+ required: [],
399
+ },
400
+ },
401
+ {
402
+ name: 'prompt_detect_template',
403
+ description: '[FREE] Detect the best template for a given user input.',
404
+ inputSchema: {
405
+ type: 'object',
406
+ properties: {
407
+ input: {
408
+ type: 'string',
409
+ description: 'User input to analyze',
410
+ },
411
+ },
412
+ required: ['input'],
413
+ },
414
+ },
415
+ {
416
+ name: 'prompt_build',
417
+ description: '[FREE] Build a production-ready prompt from a template.',
418
+ inputSchema: {
419
+ type: 'object',
420
+ properties: {
421
+ templateId: {
422
+ type: 'string',
423
+ description: 'Template ID to use',
424
+ },
425
+ userInput: {
426
+ type: 'string',
427
+ description: 'Original user request',
428
+ },
429
+ answers: {
430
+ type: 'object',
431
+ description: 'Answers to template context questions',
432
+ },
433
+ },
434
+ required: ['templateId', 'userInput'],
435
+ },
436
+ },
437
+ {
438
+ name: 'prompt_get_context',
439
+ description: '[FREE] Detect workspace context (framework, database, etc.) from project files.',
440
+ inputSchema: {
441
+ type: 'object',
442
+ properties: {},
443
+ required: [],
444
+ },
445
+ },
446
+ {
447
+ name: 'prompt_get_suggestions',
448
+ description: '[FREE] Get smart suggestions for improving a prompt.',
449
+ inputSchema: {
450
+ type: 'object',
451
+ properties: {
452
+ input: {
453
+ type: 'string',
454
+ description: 'Current prompt or user input',
455
+ },
456
+ },
457
+ required: ['input'],
458
+ },
459
+ },
460
+
461
+ // ═══════════════════════════════════════════════════════════════════════
462
+ // Tier Info Tool (FREE)
463
+ // ═══════════════════════════════════════════════════════════════════════
464
+ {
465
+ name: 'vibecheck_tier',
466
+ description: 'Get current subscription tier info, usage limits, and available features. FREE tier includes 11 commands for inspect & observe. PRO tier ($49/mo) includes 12 additional commands for fix, prove & enforce.',
467
+ inputSchema: {
468
+ type: 'object',
469
+ properties: {},
470
+ required: [],
471
+ },
472
+ },
473
+
474
+ // ═══════════════════════════════════════════════════════════════════════
475
+ // Session Management Tools (FREE)
476
+ // ═══════════════════════════════════════════════════════════════════════
477
+ {
478
+ name: 'session_info',
479
+ description: '[FREE] Get current session information including metrics, trust score, and activity history.',
480
+ inputSchema: {
481
+ type: 'object',
482
+ properties: {},
483
+ required: [],
484
+ },
485
+ },
486
+ {
487
+ name: 'session_metrics',
488
+ description: '[FREE] Get detailed session metrics including tool call statistics, latency, and success rates.',
489
+ inputSchema: {
490
+ type: 'object',
491
+ properties: {},
492
+ required: [],
493
+ },
494
+ },
495
+ {
496
+ name: 'session_history',
497
+ description: '[FREE] Get recent tool call history for the current session.',
498
+ inputSchema: {
499
+ type: 'object',
500
+ properties: {
501
+ limit: {
502
+ type: 'number',
503
+ description: 'Maximum number of entries to return (default: 20)',
504
+ },
505
+ },
506
+ required: [],
507
+ },
508
+ },
509
+ {
510
+ name: 'session_health',
511
+ description: '[FREE] Check session health status including trust score and any issues.',
512
+ inputSchema: {
513
+ type: 'object',
514
+ properties: {},
515
+ required: [],
516
+ },
517
+ },
518
+
519
+ // ═══════════════════════════════════════════════════════════════════════
520
+ // Git Integration Tools (FREE)
521
+ // ═══════════════════════════════════════════════════════════════════════
522
+ {
523
+ name: 'git_status',
524
+ description: '[FREE] Get comprehensive git status including branch, staged/unstaged changes, and ahead/behind counts.',
525
+ inputSchema: {
526
+ type: 'object',
527
+ properties: {},
528
+ required: [],
529
+ },
530
+ },
531
+ {
532
+ name: 'git_diff',
533
+ description: '[FREE] Get diff of current changes with risk analysis.',
534
+ inputSchema: {
535
+ type: 'object',
536
+ properties: {
537
+ staged: {
538
+ type: 'boolean',
539
+ description: 'Get staged changes only (default: false)',
540
+ },
541
+ },
542
+ required: [],
543
+ },
544
+ },
545
+ {
546
+ name: 'git_diff_intent_check',
547
+ description: '[PRO] Check if current git changes match the declared intent. Identifies constraint violations.',
548
+ inputSchema: {
549
+ type: 'object',
550
+ properties: {
551
+ staged: {
552
+ type: 'boolean',
553
+ description: 'Check staged changes only (default: false)',
554
+ },
555
+ },
556
+ required: [],
557
+ },
558
+ },
559
+ {
560
+ name: 'git_commits',
561
+ description: '[FREE] Get recent commit history.',
562
+ inputSchema: {
563
+ type: 'object',
564
+ properties: {
565
+ count: {
566
+ type: 'number',
567
+ description: 'Number of commits to retrieve (default: 10)',
568
+ },
569
+ },
570
+ required: [],
571
+ },
572
+ },
573
+ {
574
+ name: 'git_branches',
575
+ description: '[FREE] List all git branches.',
576
+ inputSchema: {
577
+ type: 'object',
578
+ properties: {},
579
+ required: [],
580
+ },
581
+ },
582
+ {
583
+ name: 'git_file_history',
584
+ description: '[FREE] Get commit history for a specific file.',
585
+ inputSchema: {
586
+ type: 'object',
587
+ properties: {
588
+ file: {
589
+ type: 'string',
590
+ description: 'File path (relative to workspace)',
591
+ },
592
+ count: {
593
+ type: 'number',
594
+ description: 'Number of commits to retrieve (default: 10)',
595
+ },
596
+ },
597
+ required: ['file'],
598
+ },
599
+ },
600
+ {
601
+ name: 'git_snapshot',
602
+ description: '[PRO] Create a git stash snapshot of current changes.',
603
+ inputSchema: {
604
+ type: 'object',
605
+ properties: {
606
+ message: {
607
+ type: 'string',
608
+ description: 'Optional message for the snapshot',
609
+ },
610
+ },
611
+ required: [],
612
+ },
613
+ },
614
+
615
+ // ═══════════════════════════════════════════════════════════════════════
616
+ // Context Management Tools (FREE)
617
+ // ═══════════════════════════════════════════════════════════════════════
618
+ {
619
+ name: 'context_project',
620
+ description: '[FREE] Get comprehensive project structure analysis including framework, language, directories, and statistics.',
621
+ inputSchema: {
622
+ type: 'object',
623
+ properties: {},
624
+ required: [],
625
+ },
626
+ },
627
+ {
628
+ name: 'context_window',
629
+ description: '[FREE] Get a smart context window of relevant files for a given query or task.',
630
+ inputSchema: {
631
+ type: 'object',
632
+ properties: {
633
+ query: {
634
+ type: 'string',
635
+ description: 'Query or task description to find relevant files',
636
+ },
637
+ maxFiles: {
638
+ type: 'number',
639
+ description: 'Maximum number of files (default: 50)',
640
+ },
641
+ categories: {
642
+ type: 'array',
643
+ items: { type: 'string' },
644
+ description: 'Filter by categories: config, entry, api, component, utility, test, style, documentation',
645
+ },
646
+ includeContent: {
647
+ type: 'boolean',
648
+ description: 'Include file contents (default: false)',
649
+ },
650
+ },
651
+ required: [],
652
+ },
653
+ },
654
+ {
655
+ name: 'context_find_files',
656
+ description: '[FREE] Find files matching a pattern in the workspace.',
657
+ inputSchema: {
658
+ type: 'object',
659
+ properties: {
660
+ pattern: {
661
+ type: 'string',
662
+ description: 'Regex pattern to match file paths',
663
+ },
664
+ },
665
+ required: ['pattern'],
666
+ },
667
+ },
668
+ {
669
+ name: 'context_related_files',
670
+ description: '[FREE] Find files related to a given file based on imports and dependencies.',
671
+ inputSchema: {
672
+ type: 'object',
673
+ properties: {
674
+ file: {
675
+ type: 'string',
676
+ description: 'File path (relative to workspace)',
677
+ },
678
+ depth: {
679
+ type: 'number',
680
+ description: 'Depth of dependency traversal (default: 1)',
681
+ },
682
+ },
683
+ required: ['file'],
684
+ },
685
+ },
686
+ {
687
+ name: 'context_file_info',
688
+ description: '[FREE] Get detailed information about a specific file including category, relevance, and optionally content.',
689
+ inputSchema: {
690
+ type: 'object',
691
+ properties: {
692
+ file: {
693
+ type: 'string',
694
+ description: 'File path (relative to workspace)',
695
+ },
696
+ includeContent: {
697
+ type: 'boolean',
698
+ description: 'Include file content (default: false)',
699
+ },
700
+ },
701
+ required: ['file'],
702
+ },
703
+ },
704
+
705
+ // ═══════════════════════════════════════════════════════════════════════
706
+ // Cache & Performance Tools (FREE)
707
+ // ═══════════════════════════════════════════════════════════════════════
708
+ {
709
+ name: 'cache_stats',
710
+ description: '[FREE] Get cache statistics including hit rate, size, and entry count.',
711
+ inputSchema: {
712
+ type: 'object',
713
+ properties: {},
714
+ required: [],
715
+ },
716
+ },
717
+ {
718
+ name: 'cache_clear',
719
+ description: '[FREE] Clear the cache. Use tag to clear specific entries.',
720
+ inputSchema: {
721
+ type: 'object',
722
+ properties: {
723
+ tag: {
724
+ type: 'string',
725
+ description: 'Optional tag to clear specific cache entries',
726
+ },
727
+ },
728
+ required: [],
729
+ },
730
+ },
731
+
732
+ // ═══════════════════════════════════════════════════════════════════════
733
+ // Health & Diagnostics Tools (FREE)
734
+ // ═══════════════════════════════════════════════════════════════════════
735
+ {
736
+ name: 'health_check',
737
+ description: '[FREE] Run comprehensive health check on the MCP server and all services.',
738
+ inputSchema: {
739
+ type: 'object',
740
+ properties: {},
741
+ required: [],
742
+ },
743
+ },
744
+ ],
745
+ }));
746
+
747
+ // Handle tool calls
748
+ this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
749
+ const { name, arguments: args } = request.params;
750
+
751
+ try {
752
+ // ═══════════════════════════════════════════════════════════════════════
753
+ // TIER CHECK - Enforce FREE/PRO tiers
754
+ // ═══════════════════════════════════════════════════════════════════════
755
+ const tierCheck = this.checkTierAccess(name);
756
+ if (!tierCheck.allowed && tierCheck.errorResponse) {
757
+ return tierCheck.errorResponse;
758
+ }
759
+
760
+ switch (name) {
761
+ // ═══════════════════════════════════════════════════════════════════════
762
+ // CLI Tools
763
+ // ═══════════════════════════════════════════════════════════════════════
764
+
765
+ // FREE: doctor, audit, forge, packs
766
+ case 'vibecheck_doctor':
767
+ return this.handleDoctor();
768
+ case 'vibecheck_audit':
769
+ return this.handleAudit(args as { path?: string; severity?: string });
770
+ case 'vibecheck_forge':
771
+ return this.handleForge(args as { output?: string; format?: string });
772
+ case 'vibecheck_packs':
773
+ return this.handlePacks(args as { format?: string });
774
+
775
+ // PRO: ship, fix, checkpoint, reality
776
+ case 'vibecheck_ship':
777
+ return this.handleShip();
778
+ case 'vibecheck_fix':
779
+ return this.handleFix(args as { mode: string; missionId?: string });
780
+ case 'vibecheck_checkpoint':
781
+ return this.handleCheckpoint(args as { action: string; id?: string });
782
+ case 'vibecheck_reality':
783
+ return this.handleReality(args as { url: string; headless?: boolean });
784
+
785
+ // ═══════════════════════════════════════════════════════════════════════
786
+ // Firewall Tools (PRO for enforce mode, FREE for observe)
787
+ // ═══════════════════════════════════════════════════════════════════════
788
+ case 'firewall_status':
789
+ return this.handleFirewallStatus();
790
+ case 'firewall_set_mode':
791
+ return this.handleFirewallSetMode(args as { mode: string });
792
+ case 'firewall_set_intent':
793
+ return this.handleFirewallSetIntent(args as { summary?: string; constraints?: string[]; template?: string });
794
+ case 'firewall_get_intent':
795
+ return this.handleFirewallGetIntent();
796
+ case 'firewall_clear_intent':
797
+ return this.handleFirewallClearIntent();
798
+ case 'firewall_check':
799
+ return this.handleFirewallCheck();
800
+ case 'firewall_verify_claim':
801
+ return this.handleFirewallVerifyClaim(args as { claim: string; context?: string; files?: string[] });
802
+ case 'firewall_gate_action':
803
+ return this.handleFirewallGateAction(args as { action: string; category: string });
804
+ case 'firewall_get_templates':
805
+ return this.handleFirewallGetTemplates();
806
+
807
+ // ═══════════════════════════════════════════════════════════════════════
808
+ // Prompt Builder Tools (FREE - helpers for forge)
809
+ // ═══════════════════════════════════════════════════════════════════════
810
+ case 'prompt_get_templates':
811
+ return this.handlePromptGetTemplates(args as { category?: string });
812
+ case 'prompt_get_categories':
813
+ return this.handlePromptGetCategories();
814
+ case 'prompt_detect_template':
815
+ return this.handlePromptDetectTemplate(args as { input: string });
816
+ case 'prompt_build':
817
+ return this.handlePromptBuild(args as { templateId: string; userInput: string; answers?: Record<string, unknown> });
818
+ case 'prompt_get_context':
819
+ return this.handlePromptGetContext();
820
+ case 'prompt_get_suggestions':
821
+ return this.handlePromptGetSuggestions(args as { input: string });
822
+
823
+ // ═══════════════════════════════════════════════════════════════════════
824
+ // Tier Info Tool (FREE)
825
+ // ═══════════════════════════════════════════════════════════════════════
826
+ case 'vibecheck_tier':
827
+ return this.handleTierInfo();
828
+
829
+ // ═══════════════════════════════════════════════════════════════════════
830
+ // Session Management Tools
831
+ // ═══════════════════════════════════════════════════════════════════════
832
+ case 'session_info':
833
+ return this.handleSessionInfo();
834
+ case 'session_metrics':
835
+ return this.handleSessionMetrics();
836
+ case 'session_history':
837
+ return this.handleSessionHistory(args as { limit?: number });
838
+ case 'session_health':
839
+ return this.handleSessionHealth();
840
+
841
+ // ═══════════════════════════════════════════════════════════════════════
842
+ // Git Integration Tools
843
+ // ═══════════════════════════════════════════════════════════════════════
844
+ case 'git_status':
845
+ return this.handleGitStatus();
846
+ case 'git_diff':
847
+ return this.handleGitDiff(args as { staged?: boolean });
848
+ case 'git_diff_intent_check':
849
+ return this.handleGitDiffIntentCheck(args as { staged?: boolean });
850
+ case 'git_commits':
851
+ return this.handleGitCommits(args as { count?: number });
852
+ case 'git_branches':
853
+ return this.handleGitBranches();
854
+ case 'git_file_history':
855
+ return this.handleGitFileHistory(args as { file: string; count?: number });
856
+ case 'git_snapshot':
857
+ return this.handleGitSnapshot(args as { message?: string });
858
+
859
+ // ═══════════════════════════════════════════════════════════════════════
860
+ // Context Management Tools
861
+ // ═══════════════════════════════════════════════════════════════════════
862
+ case 'context_project':
863
+ return this.handleContextProject();
864
+ case 'context_window':
865
+ return this.handleContextWindow(args as { query?: string; maxFiles?: number; categories?: string[]; includeContent?: boolean });
866
+ case 'context_find_files':
867
+ return this.handleContextFindFiles(args as { pattern: string });
868
+ case 'context_related_files':
869
+ return this.handleContextRelatedFiles(args as { file: string; depth?: number });
870
+ case 'context_file_info':
871
+ return this.handleContextFileInfo(args as { file: string; includeContent?: boolean });
872
+
873
+ // ═══════════════════════════════════════════════════════════════════════
874
+ // Cache & Health Tools
875
+ // ═══════════════════════════════════════════════════════════════════════
876
+ case 'cache_stats':
877
+ return this.handleCacheStats();
878
+ case 'cache_clear':
879
+ return this.handleCacheClear(args as { tag?: string });
880
+ case 'health_check':
881
+ return this.handleHealthCheck();
882
+
883
+ default:
884
+ throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
885
+ }
886
+ } catch (error) {
887
+ if (error instanceof McpError) throw error;
888
+ const message = error instanceof Error ? error.message : 'Unknown error';
889
+ return {
890
+ content: [{ type: 'text', text: `Error: ${message}` }],
891
+ isError: true,
892
+ };
893
+ }
894
+ });
895
+
896
+ // List resources
897
+ this.server.setRequestHandler(ListResourcesRequestSchema, async () => ({
898
+ resources: [
899
+ {
900
+ uri: 'vibecheck://status',
901
+ name: 'Server Status',
902
+ description: 'Current MCP server status and configuration',
903
+ mimeType: 'application/json',
904
+ },
905
+ {
906
+ uri: 'vibecheck://tier',
907
+ name: 'Subscription Tier',
908
+ description: 'Current subscription tier, limits, and usage',
909
+ mimeType: 'application/json',
910
+ },
911
+ {
912
+ uri: 'vibecheck://session',
913
+ name: 'Session Info',
914
+ description: 'Current session information, metrics, and trust score',
915
+ mimeType: 'application/json',
916
+ },
917
+ {
918
+ uri: 'vibecheck://firewall/stats',
919
+ name: 'Firewall Statistics',
920
+ description: 'Firewall violation and action statistics',
921
+ mimeType: 'application/json',
922
+ },
923
+ {
924
+ uri: 'vibecheck://firewall/log',
925
+ name: 'Firewall Action Log',
926
+ description: 'Recent firewall action log entries',
927
+ mimeType: 'application/json',
928
+ },
929
+ {
930
+ uri: 'vibecheck://git/status',
931
+ name: 'Git Status',
932
+ description: 'Current git repository status',
933
+ mimeType: 'application/json',
934
+ },
935
+ {
936
+ uri: 'vibecheck://context',
937
+ name: 'Workspace Context',
938
+ description: 'Detected workspace context (framework, database, etc.)',
939
+ mimeType: 'application/json',
940
+ },
941
+ {
942
+ uri: 'vibecheck://project',
943
+ name: 'Project Structure',
944
+ description: 'Comprehensive project structure analysis',
945
+ mimeType: 'application/json',
946
+ },
947
+ {
948
+ uri: 'vibecheck://health',
949
+ name: 'Health Status',
950
+ description: 'MCP server health check status',
951
+ mimeType: 'application/json',
952
+ },
953
+ {
954
+ uri: 'vibecheck://cache',
955
+ name: 'Cache Statistics',
956
+ description: 'Cache hit rate and statistics',
957
+ mimeType: 'application/json',
958
+ },
959
+ ],
960
+ }));
961
+
962
+ // Read resources
963
+ this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
964
+ const { uri } = request.params;
965
+
966
+ switch (uri) {
967
+ case 'vibecheck://status':
968
+ return {
969
+ contents: [{
970
+ uri,
971
+ mimeType: 'application/json',
972
+ text: JSON.stringify({
973
+ ...this.state,
974
+ tier: this.tierService.getTier(),
975
+ }, null, 2),
976
+ }],
977
+ };
978
+
979
+ case 'vibecheck://tier':
980
+ return {
981
+ contents: [{
982
+ uri,
983
+ mimeType: 'application/json',
984
+ text: JSON.stringify(this.tierService.getTierInfo(), null, 2),
985
+ }],
986
+ };
987
+
988
+ case 'vibecheck://firewall/stats':
989
+ return {
990
+ contents: [{
991
+ uri,
992
+ mimeType: 'application/json',
993
+ text: JSON.stringify(this.firewallService.getStats(), null, 2),
994
+ }],
995
+ };
996
+
997
+ case 'vibecheck://firewall/log':
998
+ return {
999
+ contents: [{
1000
+ uri,
1001
+ mimeType: 'application/json',
1002
+ text: JSON.stringify(this.firewallService.getActionLog(50), null, 2),
1003
+ }],
1004
+ };
1005
+
1006
+ case 'vibecheck://context':
1007
+ const context = await this.promptBuilderService.detectWorkspaceContext();
1008
+ return {
1009
+ contents: [{
1010
+ uri,
1011
+ mimeType: 'application/json',
1012
+ text: JSON.stringify(context, null, 2),
1013
+ }],
1014
+ };
1015
+
1016
+ case 'vibecheck://session':
1017
+ return {
1018
+ contents: [{
1019
+ uri,
1020
+ mimeType: 'application/json',
1021
+ text: JSON.stringify(this.sessionService.getSessionSummary(), null, 2),
1022
+ }],
1023
+ };
1024
+
1025
+ case 'vibecheck://git/status':
1026
+ const gitStatus = await this.gitService.getStatus();
1027
+ return {
1028
+ contents: [{
1029
+ uri,
1030
+ mimeType: 'application/json',
1031
+ text: JSON.stringify(gitStatus, null, 2),
1032
+ }],
1033
+ };
1034
+
1035
+ case 'vibecheck://project':
1036
+ const project = await this.contextManager.getProjectStructure();
1037
+ return {
1038
+ contents: [{
1039
+ uri,
1040
+ mimeType: 'application/json',
1041
+ text: JSON.stringify(project, null, 2),
1042
+ }],
1043
+ };
1044
+
1045
+ case 'vibecheck://health':
1046
+ const checks = [];
1047
+ checks.push({ name: 'CLI', available: await this.cliService.isAvailable() });
1048
+ checks.push({ name: 'Git', available: await this.gitService.isRepo() });
1049
+ checks.push({ name: 'Session', healthy: this.sessionService.isSessionHealthy().healthy });
1050
+ return {
1051
+ contents: [{
1052
+ uri,
1053
+ mimeType: 'application/json',
1054
+ text: JSON.stringify({ checks, timestamp: new Date().toISOString() }, null, 2),
1055
+ }],
1056
+ };
1057
+
1058
+ case 'vibecheck://cache':
1059
+ return {
1060
+ contents: [{
1061
+ uri,
1062
+ mimeType: 'application/json',
1063
+ text: JSON.stringify(this.cacheService.getStats(), null, 2),
1064
+ }],
1065
+ };
1066
+
1067
+ default:
1068
+ throw new McpError(ErrorCode.InvalidRequest, `Unknown resource: ${uri}`);
1069
+ }
1070
+ });
1071
+ }
1072
+
1073
+ // ═══════════════════════════════════════════════════════════════════════════════
1074
+ // CLI Tool Handlers
1075
+ // ═══════════════════════════════════════════════════════════════════════════════
1076
+
1077
+ private async handleDoctor() {
1078
+ const result = await this.cliService.doctor();
1079
+ return {
1080
+ content: [{
1081
+ type: 'text' as const,
1082
+ text: result.success
1083
+ ? JSON.stringify(result.data, null, 2)
1084
+ : `Doctor check failed: ${result.error}`,
1085
+ }],
1086
+ };
1087
+ }
1088
+
1089
+ private async handleAudit(args: { path?: string; severity?: string }) {
1090
+ // ═══════════════════════════════════════════════════════════════════════════
1091
+ // TIER CHECK: Check scan limit for FREE tier
1092
+ // ═══════════════════════════════════════════════════════════════════════════
1093
+ const scanLimit = this.tierService.checkScanLimit();
1094
+ if (!scanLimit.allowed) {
1095
+ return {
1096
+ content: [{
1097
+ type: 'text' as const,
1098
+ text: this.tierService.formatTierError(scanLimit),
1099
+ }],
1100
+ isError: true,
1101
+ };
1102
+ }
1103
+
1104
+ const result = await this.cliService.audit(args);
1105
+
1106
+ // Increment scan count on successful audit
1107
+ if (result.success) {
1108
+ this.tierService.incrementScanCount();
1109
+ }
1110
+
1111
+ return {
1112
+ content: [{
1113
+ type: 'text' as const,
1114
+ text: result.success
1115
+ ? JSON.stringify(result.data, null, 2)
1116
+ : `Audit failed: ${result.error}`,
1117
+ }],
1118
+ };
1119
+ }
1120
+
1121
+ private async handleShip() {
1122
+ const result = await this.cliService.ship();
1123
+ return {
1124
+ content: [{
1125
+ type: 'text' as const,
1126
+ text: result.success
1127
+ ? JSON.stringify(result.data, null, 2)
1128
+ : `Ship check failed: ${result.error}`,
1129
+ }],
1130
+ };
1131
+ }
1132
+
1133
+ private async handleForge(args: { output?: string; format?: string }) {
1134
+ const result = await this.cliService.forge(args);
1135
+ return {
1136
+ content: [{
1137
+ type: 'text' as const,
1138
+ text: result.success
1139
+ ? JSON.stringify(result.data, null, 2)
1140
+ : `Forge failed: ${result.error}`,
1141
+ }],
1142
+ };
1143
+ }
1144
+
1145
+ private async handleFix(args: { mode: string; missionId?: string }) {
1146
+ const result = args.mode === 'plan'
1147
+ ? await this.cliService.fixPlan()
1148
+ : await this.cliService.fixApply({ missionId: args.missionId });
1149
+ return {
1150
+ content: [{
1151
+ type: 'text' as const,
1152
+ text: result.success
1153
+ ? JSON.stringify(result.data, null, 2)
1154
+ : `Fix ${args.mode} failed: ${result.error}`,
1155
+ }],
1156
+ };
1157
+ }
1158
+
1159
+ private async handleCheckpoint(args: { action: string; id?: string }) {
1160
+ const result = await this.cliService.checkpoint(
1161
+ args.action as 'create' | 'restore' | 'list',
1162
+ args.id
1163
+ );
1164
+ return {
1165
+ content: [{
1166
+ type: 'text' as const,
1167
+ text: result.success
1168
+ ? JSON.stringify(result.data, null, 2)
1169
+ : `Checkpoint ${args.action} failed: ${result.error}`,
1170
+ }],
1171
+ };
1172
+ }
1173
+
1174
+ private async handlePacks(args: { format?: string }) {
1175
+ const format = (args.format as 'html' | 'zip' | 'json' | 'sarif' | 'csv' | 'pdf') || 'html';
1176
+
1177
+ // ═══════════════════════════════════════════════════════════════════════════
1178
+ // TIER CHECK: Check report format for FREE tier
1179
+ // ═══════════════════════════════════════════════════════════════════════════
1180
+ const formatCheck = this.tierService.checkReportFormat(format);
1181
+ if (!formatCheck.allowed) {
1182
+ return {
1183
+ content: [{
1184
+ type: 'text' as const,
1185
+ text: this.tierService.formatTierError(formatCheck),
1186
+ }],
1187
+ isError: true,
1188
+ };
1189
+ }
1190
+
1191
+ const result = await this.cliService.packs(format as 'html' | 'zip' | 'json');
1192
+ return {
1193
+ content: [{
1194
+ type: 'text' as const,
1195
+ text: result.success
1196
+ ? JSON.stringify(result.data, null, 2)
1197
+ : `Packs generation failed: ${result.error}`,
1198
+ }],
1199
+ };
1200
+ }
1201
+
1202
+ private async handleReality(args: { url: string; headless?: boolean }) {
1203
+ const result = await this.cliService.reality(args.url, { headless: args.headless });
1204
+ return {
1205
+ content: [{
1206
+ type: 'text' as const,
1207
+ text: result.success
1208
+ ? JSON.stringify(result.data, null, 2)
1209
+ : `Reality test failed: ${result.error}`,
1210
+ }],
1211
+ };
1212
+ }
1213
+
1214
+ // ═══════════════════════════════════════════════════════════════════════════════
1215
+ // Firewall Tool Handlers
1216
+ // ═══════════════════════════════════════════════════════════════════════════════
1217
+
1218
+ private async handleFirewallStatus() {
1219
+ const status = await this.firewallService.getStatus();
1220
+ return {
1221
+ content: [{
1222
+ type: 'text' as const,
1223
+ text: JSON.stringify(status, null, 2),
1224
+ }],
1225
+ };
1226
+ }
1227
+
1228
+ private async handleFirewallSetMode(args: { mode: string }) {
1229
+ const mode = args.mode as FirewallMode;
1230
+
1231
+ // ═══════════════════════════════════════════════════════════════════════════
1232
+ // TIER CHECK: Enforce mode requires PRO subscription
1233
+ // ═══════════════════════════════════════════════════════════════════════════
1234
+ if (mode === 'enforce') {
1235
+ const tierCheck = this.tierService.checkFirewallEnforceMode();
1236
+ if (!tierCheck.allowed) {
1237
+ return {
1238
+ content: [{
1239
+ type: 'text' as const,
1240
+ text: this.tierService.formatTierError(tierCheck) +
1241
+ '\n\n💡 FREE tier supports "observe" mode, which logs actions without blocking.',
1242
+ }],
1243
+ isError: true,
1244
+ };
1245
+ }
1246
+ }
1247
+
1248
+ const success = await this.firewallService.setMode(mode);
1249
+ this.state.firewallMode = mode;
1250
+ return {
1251
+ content: [{
1252
+ type: 'text' as const,
1253
+ text: success
1254
+ ? `Firewall mode set to: ${mode}` +
1255
+ (mode === 'enforce' ? ' (PRO feature)' : '')
1256
+ : `Failed to set firewall mode to: ${mode}`,
1257
+ }],
1258
+ };
1259
+ }
1260
+
1261
+ private async handleFirewallSetIntent(args: { summary?: string; constraints?: string[]; template?: string }) {
1262
+ let success: boolean;
1263
+
1264
+ if (args.template) {
1265
+ success = await this.firewallService.setIntentFromTemplate(args.template, args.summary);
1266
+ } else if (args.summary) {
1267
+ success = await this.firewallService.setIntent(args.summary, args.constraints || []);
1268
+ } else {
1269
+ return {
1270
+ content: [{
1271
+ type: 'text' as const,
1272
+ text: 'Error: Either summary or template is required',
1273
+ }],
1274
+ isError: true,
1275
+ };
1276
+ }
1277
+
1278
+ const intent = this.firewallService.getCurrentIntent();
1279
+ return {
1280
+ content: [{
1281
+ type: 'text' as const,
1282
+ text: success
1283
+ ? `Intent set:\n${JSON.stringify(intent, null, 2)}`
1284
+ : 'Failed to set intent',
1285
+ }],
1286
+ };
1287
+ }
1288
+
1289
+ private async handleFirewallGetIntent() {
1290
+ const intent = await this.firewallService.getIntent();
1291
+ return {
1292
+ content: [{
1293
+ type: 'text' as const,
1294
+ text: intent
1295
+ ? JSON.stringify(intent, null, 2)
1296
+ : 'No intent is currently set',
1297
+ }],
1298
+ };
1299
+ }
1300
+
1301
+ private async handleFirewallClearIntent() {
1302
+ const success = await this.firewallService.clearIntent();
1303
+ return {
1304
+ content: [{
1305
+ type: 'text' as const,
1306
+ text: success ? 'Intent cleared' : 'Failed to clear intent',
1307
+ }],
1308
+ };
1309
+ }
1310
+
1311
+ private async handleFirewallCheck() {
1312
+ const result = await this.firewallService.check();
1313
+ return {
1314
+ content: [{
1315
+ type: 'text' as const,
1316
+ text: result
1317
+ ? JSON.stringify(result, null, 2)
1318
+ : 'Shield check failed',
1319
+ }],
1320
+ };
1321
+ }
1322
+
1323
+ private async handleFirewallVerifyClaim(args: { claim: string; context?: string; files?: string[] }) {
1324
+ const result = await this.firewallService.verifyClaim({
1325
+ claim: args.claim,
1326
+ context: args.context,
1327
+ files: args.files,
1328
+ });
1329
+ return {
1330
+ content: [{
1331
+ type: 'text' as const,
1332
+ text: JSON.stringify(result, null, 2),
1333
+ }],
1334
+ };
1335
+ }
1336
+
1337
+ private handleFirewallGateAction(args: { action: string; category: string }) {
1338
+ const result = this.firewallService.gateAction(
1339
+ args.action,
1340
+ args.category as 'read' | 'write' | 'execute' | 'network' | 'sensitive'
1341
+ );
1342
+ return {
1343
+ content: [{
1344
+ type: 'text' as const,
1345
+ text: JSON.stringify(result, null, 2),
1346
+ }],
1347
+ };
1348
+ }
1349
+
1350
+ private handleFirewallGetTemplates() {
1351
+ const templates = this.firewallService.getTemplates();
1352
+ return {
1353
+ content: [{
1354
+ type: 'text' as const,
1355
+ text: JSON.stringify(templates, null, 2),
1356
+ }],
1357
+ };
1358
+ }
1359
+
1360
+ // ═══════════════════════════════════════════════════════════════════════════════
1361
+ // Prompt Builder Tool Handlers
1362
+ // ═══════════════════════════════════════════════════════════════════════════════
1363
+
1364
+ private handlePromptGetTemplates(args: { category?: string }) {
1365
+ const templates = args.category
1366
+ ? this.promptBuilderService.getTemplatesByCategory(args.category as any)
1367
+ : this.promptBuilderService.getTemplates();
1368
+
1369
+ // Return simplified template info
1370
+ const simplified = templates.map(t => ({
1371
+ id: t.id,
1372
+ name: t.name,
1373
+ category: t.category,
1374
+ description: t.description,
1375
+ icon: t.icon,
1376
+ popularity: t.popularity,
1377
+ contextQuestions: t.contextQuestions.map(q => ({
1378
+ id: q.id,
1379
+ label: q.label,
1380
+ type: q.type,
1381
+ required: q.required,
1382
+ })),
1383
+ }));
1384
+
1385
+ return {
1386
+ content: [{
1387
+ type: 'text' as const,
1388
+ text: JSON.stringify(simplified, null, 2),
1389
+ }],
1390
+ };
1391
+ }
1392
+
1393
+ private handlePromptGetCategories() {
1394
+ const categories = this.promptBuilderService.getCategories();
1395
+ return {
1396
+ content: [{
1397
+ type: 'text' as const,
1398
+ text: JSON.stringify(categories, null, 2),
1399
+ }],
1400
+ };
1401
+ }
1402
+
1403
+ private handlePromptDetectTemplate(args: { input: string }) {
1404
+ const template = this.promptBuilderService.detectTemplate(args.input);
1405
+ return {
1406
+ content: [{
1407
+ type: 'text' as const,
1408
+ text: template
1409
+ ? JSON.stringify({
1410
+ id: template.id,
1411
+ name: template.name,
1412
+ description: template.description,
1413
+ category: template.category,
1414
+ contextQuestions: template.contextQuestions,
1415
+ }, null, 2)
1416
+ : 'No matching template found',
1417
+ }],
1418
+ };
1419
+ }
1420
+
1421
+ private async handlePromptBuild(args: { templateId: string; userInput: string; answers?: Record<string, unknown> }) {
1422
+ const result = await this.promptBuilderService.buildPrompt(
1423
+ args.templateId,
1424
+ args.userInput,
1425
+ (args.answers || {}) as Record<string, string | string[]>
1426
+ );
1427
+
1428
+ return {
1429
+ content: [{
1430
+ type: 'text' as const,
1431
+ text: result
1432
+ ? JSON.stringify({
1433
+ id: result.id,
1434
+ quality: result.quality,
1435
+ expandedPrompt: result.expandedPrompt,
1436
+ }, null, 2)
1437
+ : `Template not found: ${args.templateId}`,
1438
+ }],
1439
+ };
1440
+ }
1441
+
1442
+ private async handlePromptGetContext() {
1443
+ const context = await this.promptBuilderService.detectWorkspaceContext();
1444
+ return {
1445
+ content: [{
1446
+ type: 'text' as const,
1447
+ text: JSON.stringify(context, null, 2),
1448
+ }],
1449
+ };
1450
+ }
1451
+
1452
+ private handlePromptGetSuggestions(args: { input: string }) {
1453
+ const suggestions = this.promptBuilderService.getSmartSuggestions(args.input);
1454
+ return {
1455
+ content: [{
1456
+ type: 'text' as const,
1457
+ text: JSON.stringify(suggestions, null, 2),
1458
+ }],
1459
+ };
1460
+ }
1461
+
1462
+ // ═══════════════════════════════════════════════════════════════════════════════
1463
+ // Tier Info Handler
1464
+ // ═══════════════════════════════════════════════════════════════════════════════
1465
+
1466
+ private handleTierInfo() {
1467
+ const tierInfo = this.tierService.getTierInfo();
1468
+ const isPro = tierInfo.isPro;
1469
+
1470
+ const freeCommands = [
1471
+ 'link', 'kickoff', 'doctor', 'watch', 'forge',
1472
+ 'audit', 'auth', 'safelist', 'labs', 'packs', 'ci'
1473
+ ];
1474
+
1475
+ const proCommands = [
1476
+ 'intent', 'approve', 'shield', 'launch', 'reality',
1477
+ 'prove', 'ship', 'seal', 'fix', 'polish', 'mcp', 'checkpoint'
1478
+ ];
1479
+
1480
+ const output = {
1481
+ currentTier: tierInfo.tier.toUpperCase(),
1482
+ isPro,
1483
+ pricing: isPro ? '$49/mo (active)' : '$0/mo (FREE)',
1484
+
1485
+ limits: {
1486
+ scansPerMonth: tierInfo.limits.scansPerMonth === Infinity ? 'Unlimited' : tierInfo.limits.scansPerMonth,
1487
+ filesPerScan: tierInfo.limits.filesPerScan === Infinity ? 'Unlimited' : tierInfo.limits.filesPerScan,
1488
+ reportFormats: tierInfo.limits.reportFormats,
1489
+ firewallMode: tierInfo.limits.firewallMode,
1490
+ },
1491
+
1492
+ usage: {
1493
+ scansThisMonth: tierInfo.usage.scans,
1494
+ scanLimit: tierInfo.usage.limit === Infinity ? 'Unlimited' : tierInfo.usage.limit,
1495
+ resetsAt: tierInfo.usage.resetsAt,
1496
+ },
1497
+
1498
+ commands: {
1499
+ free: {
1500
+ count: freeCommands.length,
1501
+ focus: 'Inspect & Observe',
1502
+ list: freeCommands,
1503
+ },
1504
+ pro: {
1505
+ count: proCommands.length,
1506
+ focus: 'Fix, Prove & Enforce',
1507
+ list: proCommands,
1508
+ available: isPro,
1509
+ },
1510
+ },
1511
+
1512
+ upgradeUrl: isPro ? null : 'https://vibecheckai.dev/pricing',
1513
+ };
1514
+
1515
+ return {
1516
+ content: [{
1517
+ type: 'text' as const,
1518
+ text: JSON.stringify(output, null, 2),
1519
+ }],
1520
+ };
1521
+ }
1522
+
1523
+ // ═══════════════════════════════════════════════════════════════════════════════
1524
+ // Session Management Handlers
1525
+ // ═══════════════════════════════════════════════════════════════════════════════
1526
+
1527
+ private handleSessionInfo() {
1528
+ const summary = this.sessionService.getSessionSummary();
1529
+ return {
1530
+ content: [{
1531
+ type: 'text' as const,
1532
+ text: summary
1533
+ ? JSON.stringify(summary, null, 2)
1534
+ : 'No active session',
1535
+ }],
1536
+ };
1537
+ }
1538
+
1539
+ private handleSessionMetrics() {
1540
+ const metrics = this.sessionService.getDetailedMetrics();
1541
+ return {
1542
+ content: [{
1543
+ type: 'text' as const,
1544
+ text: metrics
1545
+ ? JSON.stringify(metrics, null, 2)
1546
+ : 'No metrics available',
1547
+ }],
1548
+ };
1549
+ }
1550
+
1551
+ private handleSessionHistory(args: { limit?: number }) {
1552
+ const history = this.sessionService.getToolCallHistory(args.limit || 20);
1553
+ return {
1554
+ content: [{
1555
+ type: 'text' as const,
1556
+ text: JSON.stringify(history, null, 2),
1557
+ }],
1558
+ };
1559
+ }
1560
+
1561
+ private handleSessionHealth() {
1562
+ const health = this.sessionService.isSessionHealthy();
1563
+ const session = this.sessionService.getSession();
1564
+
1565
+ return {
1566
+ content: [{
1567
+ type: 'text' as const,
1568
+ text: JSON.stringify({
1569
+ healthy: health.healthy,
1570
+ issues: health.issues,
1571
+ trustScore: session?.securityContext.trustScore || 0,
1572
+ status: session?.status || 'unknown',
1573
+ }, null, 2),
1574
+ }],
1575
+ };
1576
+ }
1577
+
1578
+ // ═══════════════════════════════════════════════════════════════════════════════
1579
+ // Git Integration Handlers
1580
+ // ═══════════════════════════════════════════════════════════════════════════════
1581
+
1582
+ private async handleGitStatus() {
1583
+ const status = await this.gitService.getStatus();
1584
+ return {
1585
+ content: [{
1586
+ type: 'text' as const,
1587
+ text: JSON.stringify(status, null, 2),
1588
+ }],
1589
+ };
1590
+ }
1591
+
1592
+ private async handleGitDiff(args: { staged?: boolean }) {
1593
+ const analysis = await this.gitService.analyzeDiff(args.staged || false);
1594
+ return {
1595
+ content: [{
1596
+ type: 'text' as const,
1597
+ text: JSON.stringify(analysis, null, 2),
1598
+ }],
1599
+ };
1600
+ }
1601
+
1602
+ private async handleGitDiffIntentCheck(args: { staged?: boolean }) {
1603
+ const intent = this.firewallService.getCurrentIntent();
1604
+
1605
+ if (!intent) {
1606
+ return {
1607
+ content: [{
1608
+ type: 'text' as const,
1609
+ text: JSON.stringify({
1610
+ error: 'No intent set. Set an intent first using firewall_set_intent.',
1611
+ suggestion: 'Use firewall_get_templates to see available intent templates.',
1612
+ }, null, 2),
1613
+ }],
1614
+ isError: true,
1615
+ };
1616
+ }
1617
+
1618
+ const check = await this.gitService.checkDiffAgainstIntent(
1619
+ { summary: intent.summary, constraints: intent.constraints },
1620
+ args.staged || false
1621
+ );
1622
+
1623
+ // Record in session
1624
+ this.sessionService.recordClaimVerification(check.matches);
1625
+
1626
+ return {
1627
+ content: [{
1628
+ type: 'text' as const,
1629
+ text: JSON.stringify({
1630
+ intent: intent.summary,
1631
+ constraints: intent.constraints,
1632
+ result: check,
1633
+ }, null, 2),
1634
+ }],
1635
+ };
1636
+ }
1637
+
1638
+ private async handleGitCommits(args: { count?: number }) {
1639
+ const commits = await this.gitService.getRecentCommits(args.count || 10);
1640
+ return {
1641
+ content: [{
1642
+ type: 'text' as const,
1643
+ text: JSON.stringify(commits, null, 2),
1644
+ }],
1645
+ };
1646
+ }
1647
+
1648
+ private async handleGitBranches() {
1649
+ const branches = await this.gitService.getBranches();
1650
+ return {
1651
+ content: [{
1652
+ type: 'text' as const,
1653
+ text: JSON.stringify(branches, null, 2),
1654
+ }],
1655
+ };
1656
+ }
1657
+
1658
+ private async handleGitFileHistory(args: { file: string; count?: number }) {
1659
+ const history = await this.gitService.getFileHistory(args.file, args.count || 10);
1660
+ return {
1661
+ content: [{
1662
+ type: 'text' as const,
1663
+ text: JSON.stringify(history, null, 2),
1664
+ }],
1665
+ };
1666
+ }
1667
+
1668
+ private async handleGitSnapshot(args: { message?: string }) {
1669
+ const result = await this.gitService.createSnapshot(args.message);
1670
+
1671
+ if (result.success) {
1672
+ this.sessionService.recordStateChange({
1673
+ type: 'checkpoint_created',
1674
+ before: 'working',
1675
+ after: result.stashId || 'stash',
1676
+ metadata: { message: args.message },
1677
+ });
1678
+ }
1679
+
1680
+ return {
1681
+ content: [{
1682
+ type: 'text' as const,
1683
+ text: JSON.stringify(result, null, 2),
1684
+ }],
1685
+ };
1686
+ }
1687
+
1688
+ // ═══════════════════════════════════════════════════════════════════════════════
1689
+ // Context Management Handlers
1690
+ // ═══════════════════════════════════════════════════════════════════════════════
1691
+
1692
+ private async handleContextProject() {
1693
+ const structure = await this.contextManager.getProjectStructure();
1694
+ return {
1695
+ content: [{
1696
+ type: 'text' as const,
1697
+ text: JSON.stringify(structure, null, 2),
1698
+ }],
1699
+ };
1700
+ }
1701
+
1702
+ private async handleContextWindow(args: {
1703
+ query?: string;
1704
+ maxFiles?: number;
1705
+ categories?: string[];
1706
+ includeContent?: boolean;
1707
+ }) {
1708
+ const window = await this.contextManager.getContextWindow({
1709
+ query: args.query,
1710
+ maxFiles: args.maxFiles,
1711
+ categories: args.categories as any,
1712
+ includeContent: args.includeContent,
1713
+ });
1714
+
1715
+ return {
1716
+ content: [{
1717
+ type: 'text' as const,
1718
+ text: JSON.stringify({
1719
+ totalFiles: window.files.length,
1720
+ totalSize: window.totalSize,
1721
+ totalTokensEstimate: window.totalTokensEstimate,
1722
+ truncated: window.truncated,
1723
+ files: window.files.map(f => ({
1724
+ path: f.relativePath,
1725
+ category: f.category,
1726
+ language: f.language,
1727
+ relevanceScore: f.relevanceScore,
1728
+ size: f.size,
1729
+ ...(f.content ? { contentPreview: f.content.substring(0, 200) + '...' } : {}),
1730
+ })),
1731
+ }, null, 2),
1732
+ }],
1733
+ };
1734
+ }
1735
+
1736
+ private async handleContextFindFiles(args: { pattern: string }) {
1737
+ const files = await this.contextManager.findFiles(args.pattern);
1738
+ return {
1739
+ content: [{
1740
+ type: 'text' as const,
1741
+ text: JSON.stringify(files.map(f => ({
1742
+ path: f.relativePath,
1743
+ category: f.category,
1744
+ language: f.language,
1745
+ size: f.size,
1746
+ })), null, 2),
1747
+ }],
1748
+ };
1749
+ }
1750
+
1751
+ private async handleContextRelatedFiles(args: { file: string; depth?: number }) {
1752
+ const related = await this.contextManager.getRelatedFiles(args.file, args.depth || 1);
1753
+ return {
1754
+ content: [{
1755
+ type: 'text' as const,
1756
+ text: JSON.stringify(related.map(f => ({
1757
+ path: f.relativePath,
1758
+ category: f.category,
1759
+ language: f.language,
1760
+ relevanceScore: f.relevanceScore,
1761
+ })), null, 2),
1762
+ }],
1763
+ };
1764
+ }
1765
+
1766
+ private async handleContextFileInfo(args: { file: string; includeContent?: boolean }) {
1767
+ const info = await this.contextManager.getFileContext(args.file, args.includeContent);
1768
+
1769
+ if (!info) {
1770
+ return {
1771
+ content: [{
1772
+ type: 'text' as const,
1773
+ text: `File not found: ${args.file}`,
1774
+ }],
1775
+ isError: true,
1776
+ };
1777
+ }
1778
+
1779
+ return {
1780
+ content: [{
1781
+ type: 'text' as const,
1782
+ text: JSON.stringify({
1783
+ path: info.relativePath,
1784
+ category: info.category,
1785
+ language: info.language,
1786
+ size: info.size,
1787
+ lastModified: info.lastModified,
1788
+ relevanceScore: info.relevanceScore,
1789
+ ...(info.content ? { content: info.content } : {}),
1790
+ }, null, 2),
1791
+ }],
1792
+ };
1793
+ }
1794
+
1795
+ // ═══════════════════════════════════════════════════════════════════════════════
1796
+ // Cache & Health Handlers
1797
+ // ═══════════════════════════════════════════════════════════════════════════════
1798
+
1799
+ private handleCacheStats() {
1800
+ const stats = this.cacheService.getStats();
1801
+ return {
1802
+ content: [{
1803
+ type: 'text' as const,
1804
+ text: JSON.stringify(stats, null, 2),
1805
+ }],
1806
+ };
1807
+ }
1808
+
1809
+ private handleCacheClear(args: { tag?: string }) {
1810
+ let count = 0;
1811
+
1812
+ if (args.tag) {
1813
+ count = this.cacheService.invalidateByTag(args.tag);
1814
+ } else {
1815
+ this.cacheService.clear();
1816
+ count = -1; // All cleared
1817
+ }
1818
+
1819
+ return {
1820
+ content: [{
1821
+ type: 'text' as const,
1822
+ text: count === -1
1823
+ ? 'Cache cleared completely'
1824
+ : `Cleared ${count} cache entries with tag: ${args.tag}`,
1825
+ }],
1826
+ };
1827
+ }
1828
+
1829
+ private async handleHealthCheck() {
1830
+ const checks: { name: string; status: 'pass' | 'warn' | 'fail'; message: string }[] = [];
1831
+
1832
+ // Check CLI
1833
+ const cliAvailable = await this.cliService.isAvailable();
1834
+ checks.push({
1835
+ name: 'CLI',
1836
+ status: cliAvailable ? 'pass' : 'warn',
1837
+ message: cliAvailable ? 'VibeCheck CLI is available' : 'CLI not found (some features limited)',
1838
+ });
1839
+
1840
+ // Check Git
1841
+ const isGitRepo = await this.gitService.isRepo();
1842
+ checks.push({
1843
+ name: 'Git',
1844
+ status: isGitRepo ? 'pass' : 'warn',
1845
+ message: isGitRepo ? 'Git repository detected' : 'Not a git repository',
1846
+ });
1847
+
1848
+ // Check Session
1849
+ const sessionHealth = this.sessionService.isSessionHealthy();
1850
+ checks.push({
1851
+ name: 'Session',
1852
+ status: sessionHealth.healthy ? 'pass' : 'warn',
1853
+ message: sessionHealth.healthy
1854
+ ? 'Session is healthy'
1855
+ : `Issues: ${sessionHealth.issues.join(', ')}`,
1856
+ });
1857
+
1858
+ // Check Cache
1859
+ const cacheStats = this.cacheService.getStats();
1860
+ checks.push({
1861
+ name: 'Cache',
1862
+ status: 'pass',
1863
+ message: `Hit rate: ${cacheStats.hitRate}, entries: ${cacheStats.totalEntries}`,
1864
+ });
1865
+
1866
+ // Check Tier
1867
+ const tierInfo = this.tierService.getTierInfo();
1868
+ checks.push({
1869
+ name: 'Tier',
1870
+ status: 'pass',
1871
+ message: `${tierInfo.tier.toUpperCase()} tier - ${tierInfo.usage.scans}/${tierInfo.usage.limit} scans used`,
1872
+ });
1873
+
1874
+ // Check Firewall
1875
+ const firewallStatus = await this.firewallService.getStatus();
1876
+ checks.push({
1877
+ name: 'Firewall',
1878
+ status: 'pass',
1879
+ message: `Mode: ${firewallStatus.mode}, Intent: ${firewallStatus.currentIntent ? 'set' : 'not set'}`,
1880
+ });
1881
+
1882
+ const allPassing = checks.every(c => c.status === 'pass');
1883
+ const hasFailures = checks.some(c => c.status === 'fail');
1884
+
1885
+ return {
1886
+ content: [{
1887
+ type: 'text' as const,
1888
+ text: JSON.stringify({
1889
+ overall: hasFailures ? 'unhealthy' : (allPassing ? 'healthy' : 'degraded'),
1890
+ checks,
1891
+ timestamp: new Date().toISOString(),
1892
+ }, null, 2),
1893
+ }],
1894
+ };
1895
+ }
1896
+
1897
+ // ═══════════════════════════════════════════════════════════════════════════════
1898
+ // Server Lifecycle
1899
+ // ═══════════════════════════════════════════════════════════════════════════════
1900
+
1901
+ async initialize(): Promise<void> {
1902
+ // Check CLI availability
1903
+ this.state.cliAvailable = await this.cliService.isAvailable();
1904
+ if (this.state.cliAvailable) {
1905
+ this.state.cliVersion = await this.cliService.getVersion() || undefined;
1906
+ }
1907
+
1908
+ // Get initial firewall status
1909
+ const status = await this.firewallService.getStatus();
1910
+ this.state.firewallMode = status.mode;
1911
+ this.state.currentIntent = status.currentIntent;
1912
+
1913
+ this.state.initialized = true;
1914
+ }
1915
+
1916
+ async run(): Promise<void> {
1917
+ await this.initialize();
1918
+
1919
+ const transport = new StdioServerTransport();
1920
+ await this.server.connect(transport);
1921
+
1922
+ // Log to stderr (not stdout which is used for MCP protocol)
1923
+ console.error('VibeCheck MCP Server running on stdio');
1924
+ console.error(`CLI available: ${this.state.cliAvailable}`);
1925
+ if (this.state.cliVersion) {
1926
+ console.error(`CLI version: ${this.state.cliVersion}`);
1927
+ }
1928
+ }
1929
+
1930
+ getState(): McpServerState {
1931
+ return this.state;
1932
+ }
1933
+ }