@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.
- package/README.md +1 -1
- package/bin/runners/context/generators/cursor-enhanced.js +99 -13
- package/mcp-server/.eslintrc.json +24 -0
- package/mcp-server/README.md +425 -135
- package/mcp-server/SPEC.md +583 -0
- package/mcp-server/configs/README.md +172 -0
- package/mcp-server/configs/claude-desktop-pro.json +31 -0
- package/mcp-server/configs/claude-desktop-with-workspace.json +25 -0
- package/mcp-server/configs/claude-desktop.json +19 -0
- package/mcp-server/configs/cursor-mcp.json +21 -0
- package/mcp-server/configs/windsurf-mcp.json +17 -0
- package/mcp-server/mcp-config.example.json +9 -0
- package/mcp-server/package-lock.json +1631 -0
- package/mcp-server/package.json +49 -34
- package/mcp-server/src/cli.ts +185 -0
- package/mcp-server/src/index.ts +85 -0
- package/mcp-server/src/server.ts +1933 -0
- package/mcp-server/src/services/cache-service.ts +466 -0
- package/mcp-server/src/services/cli-service.ts +345 -0
- package/mcp-server/src/services/context-manager.ts +717 -0
- package/mcp-server/src/services/firewall-service.ts +662 -0
- package/mcp-server/src/services/git-service.ts +671 -0
- package/mcp-server/src/services/index.ts +52 -0
- package/mcp-server/src/services/prompt-builder-service.ts +1031 -0
- package/mcp-server/src/services/session-service.ts +550 -0
- package/mcp-server/src/services/tier-service.ts +470 -0
- package/mcp-server/src/types.ts +351 -0
- package/mcp-server/tsconfig.json +16 -27
- package/package.json +6 -6
- package/mcp-server/.guardrail/audit/audit.log.jsonl +0 -2
- package/mcp-server/.specs/architecture.mdc +0 -90
- package/mcp-server/.specs/security.mdc +0 -30
- package/mcp-server/HARDENING_SUMMARY.md +0 -299
- package/mcp-server/agent-checkpoint.js +0 -364
- package/mcp-server/agent-firewall-interceptor.js +0 -500
- package/mcp-server/architect-tools.js +0 -707
- package/mcp-server/audit-mcp.js +0 -206
- package/mcp-server/authority-tools.js +0 -569
- package/mcp-server/codebase-architect-tools.js +0 -838
- package/mcp-server/conductor/conflict-resolver.js +0 -588
- package/mcp-server/conductor/execution-planner.js +0 -544
- package/mcp-server/conductor/index.js +0 -377
- package/mcp-server/conductor/lock-manager.js +0 -615
- package/mcp-server/conductor/request-queue.js +0 -550
- package/mcp-server/conductor/session-manager.js +0 -500
- package/mcp-server/conductor/tools.js +0 -510
- package/mcp-server/consolidated-tools.js +0 -1170
- package/mcp-server/deprecation-middleware.js +0 -282
- package/mcp-server/handlers/index.ts +0 -15
- package/mcp-server/handlers/tool-handler.ts +0 -593
- package/mcp-server/hygiene-tools.js +0 -428
- package/mcp-server/index-v1.js +0 -698
- package/mcp-server/index.js +0 -2940
- package/mcp-server/intelligence-tools.js +0 -664
- package/mcp-server/intent-drift-tools.js +0 -873
- package/mcp-server/intent-firewall-interceptor.js +0 -529
- package/mcp-server/lib/api-client.cjs +0 -13
- package/mcp-server/lib/cache-wrapper.cjs +0 -383
- package/mcp-server/lib/error-envelope.js +0 -138
- package/mcp-server/lib/executor.ts +0 -499
- package/mcp-server/lib/index.ts +0 -29
- package/mcp-server/lib/logger.cjs +0 -30
- package/mcp-server/lib/rate-limiter.js +0 -166
- package/mcp-server/lib/sandbox.test.ts +0 -519
- package/mcp-server/lib/sandbox.ts +0 -395
- package/mcp-server/lib/types.ts +0 -267
- package/mcp-server/logger.js +0 -173
- package/mcp-server/manifest.json +0 -473
- package/mcp-server/mdc-generator.js +0 -298
- package/mcp-server/premium-tools.js +0 -1275
- package/mcp-server/proof-tools.js +0 -571
- package/mcp-server/registry/tool-registry.js +0 -586
- package/mcp-server/registry/tools.json +0 -619
- package/mcp-server/registry.test.ts +0 -340
- package/mcp-server/test-mcp.js +0 -108
- package/mcp-server/test-tools.js +0 -36
- package/mcp-server/tests/tier-gating.test.js +0 -297
- package/mcp-server/tier-auth.js +0 -767
- package/mcp-server/tools/index.js +0 -72
- package/mcp-server/tools-reorganized.ts +0 -244
- package/mcp-server/tools-v3.js +0 -1004
- package/mcp-server/truth-context.js +0 -622
- package/mcp-server/truth-firewall-tools.js +0 -2183
- package/mcp-server/vibecheck-2.0-tools.js +0 -761
- package/mcp-server/vibecheck-mcp-server-3.2.0.tgz +0 -0
- 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
|
+
}
|