@velt-js/mcp-installer 0.1.0

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/src/index.js ADDED
@@ -0,0 +1,754 @@
1
+ /**
2
+ * Velt MCP Installer Server
3
+ *
4
+ * Main MCP server implementation that exposes tools for Velt installation.
5
+ */
6
+
7
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
8
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
9
+ import {
10
+ CallToolRequestSchema,
11
+ ListToolsRequestSchema,
12
+ ListPromptsRequestSchema,
13
+ GetPromptRequestSchema,
14
+ } from '@modelcontextprotocol/sdk/types.js';
15
+ import { installVeltFreestyle } from './tools/orchestrator.js';
16
+ import { installVeltUnified } from './tools/unified-installer.js';
17
+ import { takeScreenshot } from './utils/screenshot.js';
18
+ import { detectCommentPlacement } from './utils/comment-detector.js';
19
+
20
+ /**
21
+ * Formats questionnaire for display in tool response
22
+ * @param {Object} questionnaire - Questionnaire object from installer
23
+ * @returns {string} Formatted markdown string
24
+ */
25
+ function formatQuestionnaire(questionnaire) {
26
+ const sections = [];
27
+
28
+ if (questionnaire.documentId) {
29
+ const q = questionnaire.documentId;
30
+ sections.push(`### A) Document ID\n**${q.question}**\n${q.options.map((o, i) => `${i + 1}. ${o.label}`).join('\n')}\n\n*Follow-up questions:*\n${q.followUp.map(f => `- ${f.question}`).join('\n')}`);
31
+ }
32
+
33
+ if (questionnaire.user) {
34
+ const q = questionnaire.user;
35
+ sections.push(`### B) User Identity\n**${q.question}**\n${q.options.map((o, i) => `${i + 1}. ${o.label}`).join('\n')}\n\n*Follow-up questions:*\n${q.followUp.map(f => `- ${f.question}`).join('\n')}`);
36
+ }
37
+
38
+ if (questionnaire.auth) {
39
+ const q = questionnaire.auth;
40
+ sections.push(`### C) Auth/JWT Token\n**${q.question}**\n${q.options.map((o, i) => `${i + 1}. ${o.label}`).join('\n')}\n\n*Follow-up questions (if token exists):*\n${q.followUp.map(f => `- ${f.question}`).join('\n')}`);
41
+ }
42
+
43
+ if (questionnaire.insertion) {
44
+ const q = questionnaire.insertion;
45
+ sections.push(`### D) Velt Initialization Location\n**${q.question}**\n${q.options.map((o, i) => `${i + 1}. ${o.label}`).join('\n')}\n\n*Follow-up questions:*\n${q.followUp.map(f => `- ${f.question}`).join('\n')}`);
46
+ }
47
+
48
+ return sections.join('\n\n');
49
+ }
50
+
51
+ /**
52
+ * Creates and starts the MCP server
53
+ */
54
+ export async function createServer() {
55
+ const server = new Server(
56
+ {
57
+ name: 'velt-installer',
58
+ version: '0.1.0',
59
+ },
60
+ {
61
+ capabilities: {
62
+ tools: {},
63
+ prompts: {}, // Enable prompts for user input
64
+ },
65
+ }
66
+ );
67
+
68
+ // List available tools
69
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
70
+ tools: [
71
+ {
72
+ name: 'take_project_screenshot',
73
+ description:
74
+ 'Takes a screenshot of the user\'s running Next.js application. ' +
75
+ 'WORKFLOW: This tool should be called FIRST in the Velt installation workflow to help identify where to add comments. ' +
76
+ 'REQUIREMENTS: ' +
77
+ '1) The user must have their Next.js dev server running (npm run dev) on localhost:3000 or another port. ' +
78
+ '2) After taking the screenshot, you should show it to the user and ask them to describe which part of their webpage they want to add comments to (e.g., "header", "sidebar", "main content area"). ' +
79
+ '3) Then ask them which type of comments they want: Freestyle (click anywhere) or Popover (attached to specific elements). ' +
80
+ '4) Use the detect_comment_placement tool with their responses to find the best files to modify. ' +
81
+ '5) Finally, use install_velt_freestyle or another installation tool to implement the selected comment type.',
82
+ inputSchema: {
83
+ type: 'object',
84
+ properties: {
85
+ url: {
86
+ type: 'string',
87
+ description: 'URL to screenshot (default: http://localhost:3000)',
88
+ },
89
+ width: {
90
+ type: 'number',
91
+ description: 'Viewport width in pixels (default: 1920)',
92
+ },
93
+ height: {
94
+ type: 'number',
95
+ description: 'Viewport height in pixels (default: 1080)',
96
+ },
97
+ fullPage: {
98
+ type: 'boolean',
99
+ description: 'Capture full page or just viewport (default: false)',
100
+ },
101
+ },
102
+ required: [],
103
+ },
104
+ },
105
+ {
106
+ name: 'detect_comment_placement',
107
+ description:
108
+ 'Analyzes the Next.js project structure to determine the best files and locations for placing Velt comments. ' +
109
+ 'WORKFLOW: This tool should be called AFTER taking a screenshot and getting user input about where they want comments. ' +
110
+ 'INPUT: Use the user\'s description of the target area (e.g., "header", "sidebar", "main content") and comment type (freestyle or popover). ' +
111
+ 'OUTPUT: Returns ranked list of candidate files with implementation guidance. ' +
112
+ 'USAGE: ' +
113
+ '1) Pass the targetDescription from what the user said about the screenshot ' +
114
+ '2) Pass the commentType based on user\'s choice (freestyle or popover) ' +
115
+ '3) Review the results with the user before proceeding to installation ' +
116
+ '4) Use the recommended placement information when calling installation tools',
117
+ inputSchema: {
118
+ type: 'object',
119
+ properties: {
120
+ projectPath: {
121
+ type: 'string',
122
+ description: 'Path to the Next.js project directory',
123
+ },
124
+ commentType: {
125
+ type: 'string',
126
+ enum: ['freestyle', 'popover'],
127
+ description: 'Type of comments to implement (freestyle or popover)',
128
+ },
129
+ targetDescription: {
130
+ type: 'string',
131
+ description: 'User\'s description of where to add comments (e.g., "header", "sidebar", "main content area"). This comes from the user\'s response when you showed them the screenshot.',
132
+ },
133
+ targetComponent: {
134
+ type: 'string',
135
+ description: 'Optional: specific component name if the user mentioned it',
136
+ },
137
+ },
138
+ required: ['projectPath', 'commentType'],
139
+ },
140
+ },
141
+ {
142
+ name: 'install_velt_interactive',
143
+ description:
144
+ '🌟 RECOMMENDED: Unified Velt installation with guided or CLI-only mode. ' +
145
+ '\n\n📚 PREREQUISITE: Install Velt Agent Skills via `npx skills add velt-js/agent-skills`' +
146
+ '\nThe generated plan references these skills as the PRIMARY implementation guide.' +
147
+ '\nSkills installed: velt-setup-best-practices, velt-comments-best-practices, velt-crdt-best-practices, velt-notifications-best-practices' +
148
+ '\n\n🚨🚨🚨 CRITICAL: ASK QUESTIONS ONE AT A TIME 🚨🚨🚨' +
149
+ '\nDO NOT dump all questions in one message.' +
150
+ '\nWait for user response before asking the next question.' +
151
+ '\nIf the user skips a question, INFORM them of the default value being used (e.g., "Using default: freestyle comments") before proceeding.' +
152
+ '\n\n=========================================' +
153
+ '\nWORKFLOW - ASK ONE QUESTION, WAIT, THEN NEXT:' +
154
+ '\n=========================================' +
155
+ '\n\nSTEP 1 - CONFIRM DIRECTORY:' +
156
+ '\n Ask ONLY: "Is this the correct project directory: [path]?"' +
157
+ '\n WAIT for user to confirm (yes/no).' +
158
+ '\n\nSTEP 2 - GET API KEY:' +
159
+ '\n Ask ONLY: "Please provide your Velt API Key (from https://console.velt.dev)"' +
160
+ '\n WAIT for user response.' +
161
+ '\n\nSTEP 3 - GET AUTH TOKEN:' +
162
+ '\n Ask ONLY: "Please provide your Velt Auth Token (from https://console.velt.dev)"' +
163
+ '\n WAIT for user response.' +
164
+ '\n\nSTEP 4 - FEATURE SELECTION (with SKIP option):' +
165
+ '\n Ask: "Select features to install OR type SKIP for CLI-only:"' +
166
+ '\n 📝 Comments (specify type: freestyle/popover/page/text/inline/tiptap/lexical/slate)' +
167
+ '\n 👥 Presence | 🖱️ Cursors | 🔔 Notifications | 🎥 Recorder' +
168
+ '\n 📄 CRDT (specify editor: tiptap/codemirror/blocknote)' +
169
+ '\n ⏭️ SKIP = CLI scaffolding only, no feature integration' +
170
+ '\n WAIT for user response.' +
171
+ '\n\n=========================================' +
172
+ '\n🚨 IF USER TYPES "SKIP": CLI-ONLY MODE 🚨' +
173
+ '\n=========================================' +
174
+ '\n Call tool with ONLY: projectPath, apiKey, authToken, mode="cli-only"' +
175
+ '\n DO NOT pass features or other params. Tool returns TODO checklist. DONE.' +
176
+ '\n\n=========================================' +
177
+ '\n🔷 IF USER SELECTS FEATURES: GUIDED MODE 🔷' +
178
+ '\n=========================================' +
179
+ '\n\n STEP 5 - VELTPROVIDER LOCATION:' +
180
+ '\n Ask: "Where should VeltProvider be installed? (app/page.tsx recommended). NOTE: ONLY recommend app/page.tsx. Do NOT suggest app/layout.tsx."' +
181
+ '\n WAIT for response.' +
182
+ '\n\n STEP 6 - CORNER POSITION:' +
183
+ '\n Ask: "Which corner for Velt features? (top-left/top-right/bottom-left/bottom-right)"' +
184
+ '\n WAIT for response.' +
185
+ '\n\n STEP 7 - FIRST TOOL CALL (runs CLI, returns status):' +
186
+ '\n Call with mode="guided", stage="plan", features, etc.' +
187
+ '\n Tool returns status="awaiting_discovery_consent"' +
188
+ '\n\n STEP 8 - DISCOVERY CONSENT:' +
189
+ '\n Tool response includes question about scanning codebase.' +
190
+ '\n Ask user: "Scan codebase for wiring info? [YES/NO]"' +
191
+ '\n WAIT for response.' +
192
+ '\n\n STEP 9A - IF USER SAYS YES (scan path):' +
193
+ '\n Call tool with discoveryConsent="yes"' +
194
+ '\n Tool returns status="awaiting_discovery_verification" with findings' +
195
+ '\n Show findings to user, ask: "Verify? [CONFIRM ALL / EDIT / UNSURE]"' +
196
+ '\n Call tool with discoveryVerification={status:"confirmed"|"edited", overrides?:{...}}' +
197
+ '\n\n STEP 9B - IF USER SAYS NO (manual path):' +
198
+ '\n Call tool with discoveryConsent="no"' +
199
+ '\n Tool returns status="awaiting_manual_wiring_answers" with questionnaire' +
200
+ '\n Ask questionnaire (DocumentId, User, Auth, Insertion) ONE SECTION AT A TIME' +
201
+ '\n Call tool with manualWiring={documentId:{...}, user:{...}, auth:{...}, insertion:{...}}' +
202
+ '\n\n STEP 10 - PLAN GENERATED:' +
203
+ '\n Tool returns status="plan_generated" with full plan' +
204
+ '\n Show plan, ask: "Would you like me to implement this?"' +
205
+ '\n WAIT for approval.' +
206
+ '\n\n STEP 11 - APPLY:' +
207
+ '\n Call with mode="guided", stage="apply", approved=true' +
208
+ '\n Execute plan, run full QA.' +
209
+ '\n\n=========================================' +
210
+ '\nTOOL RESPONSE STATUSES:' +
211
+ '\n=========================================' +
212
+ '\n• awaiting_discovery_consent - Need YES/NO for codebase scanning' +
213
+ '\n• awaiting_discovery_verification - Scan done, need CONFIRM/EDIT/UNSURE' +
214
+ '\n• awaiting_manual_wiring_answers - User said NO, need questionnaire answers' +
215
+ '\n• plan_generated - Plan ready with verified/manual wiring' +
216
+ '\n• cli_only_complete - SKIP path done' +
217
+ '\n• apply_complete - Apply stage done' +
218
+ '\n\n=========================================' +
219
+ '\nCRITICAL RULES:' +
220
+ '\n=========================================' +
221
+ '\n• ASK ONE QUESTION AT A TIME - never batch questions' +
222
+ '\n• Check tool response "status" field to know next action' +
223
+ '\n• NO plan without verified scan OR completed manual answers' +
224
+ '\n• If user says UNSURE, ask who can confirm - do NOT guess',
225
+ inputSchema: {
226
+ type: 'object',
227
+ properties: {
228
+ projectPath: {
229
+ type: 'string',
230
+ description: 'Path to the project directory - Must be confirmed by user',
231
+ },
232
+ apiKey: {
233
+ type: 'string',
234
+ description: 'Velt API Key from https://console.velt.dev - REQUIRED',
235
+ },
236
+ authToken: {
237
+ type: 'string',
238
+ description: 'Velt Auth Token from https://console.velt.dev - REQUIRED',
239
+ },
240
+ mode: {
241
+ type: 'string',
242
+ enum: ['guided', 'cli-only'],
243
+ description: 'Installation mode. Use "cli-only" if user typed SKIP. Default: "guided"',
244
+ },
245
+ stage: {
246
+ type: 'string',
247
+ enum: ['plan', 'apply'],
248
+ description: 'For guided mode: "plan" generates the plan, "apply" executes it. Default: "plan"',
249
+ },
250
+ approved: {
251
+ type: 'boolean',
252
+ description: 'Set to true when user approved the plan (for stage="apply").',
253
+ },
254
+ features: {
255
+ type: 'array',
256
+ items: {
257
+ type: 'string',
258
+ enum: ['comments', 'presence', 'cursors', 'notifications', 'recorder', 'crdt'],
259
+ },
260
+ description: 'Features to install (guided mode only).',
261
+ },
262
+ commentType: {
263
+ type: 'string',
264
+ enum: ['freestyle', 'popover', 'page', 'text', 'inline', 'tiptap', 'lexical', 'slate'],
265
+ description: 'Type of comments (if comments feature selected)',
266
+ },
267
+ crdtEditorType: {
268
+ type: 'string',
269
+ enum: ['tiptap', 'codemirror', 'blocknote', 'reactflow'],
270
+ description: 'CRDT editor type (if crdt feature selected)',
271
+ },
272
+ headerPosition: {
273
+ type: 'string',
274
+ enum: ['top-left', 'top-right', 'bottom-left', 'bottom-right'],
275
+ description: 'Corner position for Velt features. Default: "top-right"',
276
+ },
277
+ veltProviderLocation: {
278
+ type: 'string',
279
+ description: 'Where to install VeltProvider. Default and recommended: "app/page.tsx". Do NOT use app/layout.tsx.',
280
+ },
281
+ discoveryConsent: {
282
+ type: 'string',
283
+ enum: ['yes', 'no'],
284
+ description: 'User consent for automated codebase scanning. "yes"=run scan, "no"=use manual questionnaire.',
285
+ },
286
+ discoveryVerification: {
287
+ type: 'object',
288
+ description: 'Verification of scan results (after discoveryConsent="yes")',
289
+ properties: {
290
+ status: {
291
+ type: 'string',
292
+ enum: ['confirmed', 'edited', 'unsure'],
293
+ description: 'User verification status',
294
+ },
295
+ overrides: {
296
+ type: 'object',
297
+ description: 'Per-section overrides if status="edited"',
298
+ properties: {
299
+ documentId: { type: 'object' },
300
+ user: { type: 'object' },
301
+ auth: { type: 'object' },
302
+ insertion: { type: 'object' },
303
+ },
304
+ },
305
+ },
306
+ },
307
+ manualWiring: {
308
+ type: 'object',
309
+ description: 'Manual wiring answers (after discoveryConsent="no")',
310
+ properties: {
311
+ documentId: {
312
+ type: 'object',
313
+ properties: {
314
+ method: { type: 'string', enum: ['query-param', 'route-param', 'database', 'storage', 'other'] },
315
+ filePath: { type: 'string' },
316
+ variableName: { type: 'string' },
317
+ example: { type: 'string' },
318
+ unsure: { type: 'boolean' },
319
+ },
320
+ },
321
+ user: {
322
+ type: 'object',
323
+ properties: {
324
+ providerType: { type: 'string', enum: ['next-auth', 'clerk', 'firebase', 'supabase', 'custom-api', 'other'] },
325
+ filePath: { type: 'string' },
326
+ fields: { type: 'array', items: { type: 'string' } },
327
+ unsure: { type: 'boolean' },
328
+ },
329
+ },
330
+ auth: {
331
+ type: 'object',
332
+ properties: {
333
+ usesToken: { type: 'boolean' },
334
+ storage: { type: 'string', enum: ['cookie', 'localStorage', 'provider-sdk', 'none', 'unsure'] },
335
+ source: { type: 'string' },
336
+ refresh: { type: 'boolean' },
337
+ unsure: { type: 'boolean' },
338
+ },
339
+ },
340
+ insertion: {
341
+ type: 'object',
342
+ properties: {
343
+ locationType: { type: 'string', enum: ['root-layout', 'specific-page', 'editor-wrapper', 'other'] },
344
+ filePath: { type: 'string' },
345
+ unsure: { type: 'boolean' },
346
+ },
347
+ },
348
+ },
349
+ },
350
+ targetArea: {
351
+ type: 'string',
352
+ description: 'Optional: User description of where they want comments',
353
+ },
354
+ },
355
+ required: ['projectPath', 'apiKey', 'authToken'],
356
+ },
357
+ },
358
+ {
359
+ name: 'install_velt_freestyle',
360
+ description:
361
+ '⚠️ LEGACY: Basic Velt freestyle installation (no interactivity). ' +
362
+ 'USE install_velt_interactive instead for better user experience. ' +
363
+ 'This tool is kept for backward compatibility only.',
364
+ inputSchema: {
365
+ type: 'object',
366
+ properties: {
367
+ projectPath: {
368
+ type: 'string',
369
+ description: 'Path to the Next.js project directory',
370
+ },
371
+ },
372
+ required: ['projectPath'],
373
+ },
374
+ },
375
+ ],
376
+ }));
377
+
378
+ // List available prompts
379
+ server.setRequestHandler(ListPromptsRequestSchema, async () => ({
380
+ prompts: [
381
+ {
382
+ name: 'velt_configuration',
383
+ description: 'Collect Velt installation configuration (API key, auth token, directory)',
384
+ arguments: [
385
+ {
386
+ name: 'installDir',
387
+ description: 'Directory where Velt should be installed',
388
+ required: false,
389
+ },
390
+ {
391
+ name: 'apiKey',
392
+ description: 'Your Velt API Key (get from https://console.velt.dev)',
393
+ required: true,
394
+ },
395
+ {
396
+ name: 'authToken',
397
+ description: 'Velt Auth Token (optional, for advanced auth)',
398
+ required: false,
399
+ },
400
+ ],
401
+ },
402
+ ],
403
+ }));
404
+
405
+ // Handle prompt requests
406
+ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
407
+ const { name, arguments: args } = request.params;
408
+
409
+ if (name === 'velt_configuration') {
410
+ return {
411
+ messages: [
412
+ {
413
+ role: 'user',
414
+ content: {
415
+ type: 'text',
416
+ text: `Velt Installation Configuration
417
+
418
+ Please provide the following information:
419
+
420
+ 1. Installation Directory: ${args?.installDir || '.'}
421
+ 2. Velt API Key: ${args?.apiKey ? '***' + args.apiKey.slice(-4) : '(required)'}
422
+ 3. Auth Token: ${args?.authToken ? '***' + args.authToken.slice(-4) : '(optional)'}
423
+
424
+ Configuration will be used to install Velt with freestyle comments.`,
425
+ },
426
+ },
427
+ ],
428
+ };
429
+ }
430
+
431
+ throw new Error(`Unknown prompt: ${name}`);
432
+ });
433
+
434
+ // Handle tool calls
435
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
436
+ const { name, arguments: args } = request.params;
437
+
438
+ try {
439
+ switch (name) {
440
+ case 'take_project_screenshot': {
441
+ const screenshotResult = await takeScreenshot({
442
+ url: args?.url || 'http://localhost:3000',
443
+ width: args?.width || 1920,
444
+ height: args?.height || 1080,
445
+ fullPage: args?.fullPage || false,
446
+ });
447
+
448
+ if (!screenshotResult.success) {
449
+ return {
450
+ content: [
451
+ {
452
+ type: 'text',
453
+ text: JSON.stringify(
454
+ {
455
+ error: screenshotResult.error,
456
+ hint: screenshotResult.hint,
457
+ },
458
+ null,
459
+ 2
460
+ ),
461
+ },
462
+ ],
463
+ isError: true,
464
+ };
465
+ }
466
+
467
+ // Return screenshot as image content
468
+ return {
469
+ content: [
470
+ {
471
+ type: 'image',
472
+ data: screenshotResult.data.base64,
473
+ mimeType: screenshotResult.data.mimeType,
474
+ },
475
+ {
476
+ type: 'text',
477
+ text: JSON.stringify(
478
+ {
479
+ success: true,
480
+ message: `Screenshot captured from ${screenshotResult.data.url}`,
481
+ size: `${screenshotResult.data.width}x${screenshotResult.data.height}`,
482
+ instructions: 'Please show this screenshot to the user and ask them:\n1. Which part of the page they want to add comments to (e.g., header, sidebar, main content)\n2. What type of comments they want (Freestyle or Popover)\n\nThen use the detect_comment_placement tool with their answers.',
483
+ },
484
+ null,
485
+ 2
486
+ ),
487
+ },
488
+ ],
489
+ };
490
+ }
491
+
492
+ case 'detect_comment_placement': {
493
+ if (!args?.projectPath) {
494
+ throw new Error('projectPath is required');
495
+ }
496
+ if (!args?.commentType) {
497
+ throw new Error('commentType is required (freestyle or popover)');
498
+ }
499
+
500
+ const detectionResult = await detectCommentPlacement({
501
+ projectPath: args.projectPath,
502
+ commentType: args.commentType,
503
+ targetDescription: args?.targetDescription || '',
504
+ targetComponent: args?.targetComponent || '',
505
+ });
506
+
507
+ if (!detectionResult.success) {
508
+ return {
509
+ content: [
510
+ {
511
+ type: 'text',
512
+ text: JSON.stringify(
513
+ {
514
+ error: detectionResult.error,
515
+ },
516
+ null,
517
+ 2
518
+ ),
519
+ },
520
+ ],
521
+ isError: true,
522
+ };
523
+ }
524
+
525
+ return {
526
+ content: [
527
+ {
528
+ type: 'text',
529
+ text: JSON.stringify(detectionResult.data, null, 2),
530
+ },
531
+ ],
532
+ };
533
+ }
534
+
535
+ case 'install_velt_interactive': {
536
+ // Validate required parameters
537
+ if (!args?.projectPath) {
538
+ throw new Error('projectPath is required. Please ask the user: "Is this the correct Next.js project directory: [path]?"');
539
+ }
540
+ if (!args?.apiKey) {
541
+ throw new Error('apiKey is required. Please ask the user: "Please provide your Velt API Key (from https://console.velt.dev)"');
542
+ }
543
+ if (!args?.authToken) {
544
+ throw new Error('authToken is required. Please ask the user: "Please provide your Velt Auth Token (from https://console.velt.dev)"');
545
+ }
546
+
547
+ // Determine mode from args
548
+ const mode = args?.mode || 'guided';
549
+ const stage = args?.stage || 'plan';
550
+ const approved = args?.approved || false;
551
+
552
+ // Build params based on mode
553
+ // CRITICAL: CLI-only (SKIP) mode should NOT pass features or commentType
554
+ const installerParams = {
555
+ projectPath: args.projectPath,
556
+ apiKey: args.apiKey,
557
+ authToken: args.authToken,
558
+ mode,
559
+ stage,
560
+ approved,
561
+ server,
562
+ };
563
+
564
+ // Only add feature-related params for guided mode
565
+ if (mode === 'guided') {
566
+ installerParams.features = (args?.features?.length > 0) ? args.features : ['comments'];
567
+ installerParams.commentType = args?.commentType || 'freestyle';
568
+ installerParams.crdtEditorType = args?.crdtEditorType || null;
569
+ installerParams.headerPosition = args?.headerPosition || 'top-right';
570
+ installerParams.veltProviderLocation = args?.veltProviderLocation || 'app/page.tsx';
571
+
572
+ // NEW: Discovery consent and verification params
573
+ if (args?.discoveryConsent) {
574
+ installerParams.discoveryConsent = args.discoveryConsent;
575
+ }
576
+ if (args?.discoveryVerification) {
577
+ installerParams.discoveryVerification = args.discoveryVerification;
578
+ }
579
+ if (args?.manualWiring) {
580
+ installerParams.manualWiring = args.manualWiring;
581
+ }
582
+ }
583
+ // CLI-only mode: NO features, NO commentType, NO defaults
584
+ // Just run core CLI scaffold + basic QA
585
+
586
+ // Use the unified installer
587
+ const result = await installVeltUnified(installerParams);
588
+
589
+ // Handle CLI-only mode result
590
+ if (result.status === 'cli_only_complete' || result.mode === 'cli-only') {
591
+ return {
592
+ content: [
593
+ {
594
+ type: 'text',
595
+ text: result.report || JSON.stringify(result, null, 2),
596
+ },
597
+ ],
598
+ };
599
+ }
600
+
601
+ // Handle NEW intermediate statuses for discovery flow
602
+ if (result.status === 'awaiting_discovery_consent') {
603
+ const nextActionText = result.nextAction
604
+ ? `\n\n## 🔍 Next Action Required\n\n**${result.nextAction.question}**\n\n${result.nextAction.options.map(o => `- **${o.value.toUpperCase()}**: ${o.label}`).join('\n')}\n\n⚠️ Ask the user this question, then call the tool again with \`discoveryConsent\` set to their answer ("yes" or "no").`
605
+ : '';
606
+
607
+ return {
608
+ content: [
609
+ {
610
+ type: 'text',
611
+ text: `# CLI Complete - Discovery Consent Needed\n\n${result.message || 'CLI scaffolding complete. Ready to discover integration points.'}${nextActionText}\n\n---\n\n**Status:** ${result.status}\n**CLI Method:** ${result.cliMethod || 'unknown'}`,
612
+ },
613
+ ],
614
+ };
615
+ }
616
+
617
+ if (result.status === 'awaiting_discovery_verification') {
618
+ const findingsText = result.formattedFindings || JSON.stringify(result.discovery, null, 2);
619
+ const nextActionText = result.nextAction
620
+ ? `\n\n## ✅ Verification Required\n\n**${result.nextAction.question}**\n\n${result.nextAction.options.map(o => `- **${o.value.toUpperCase()}**: ${o.label}`).join('\n')}\n\n⚠️ Show the findings above to the user and ask them to verify. Then call the tool with \`discoveryVerification\` set to their response.`
621
+ : '';
622
+
623
+ return {
624
+ content: [
625
+ {
626
+ type: 'text',
627
+ text: `# Discovery Scan Complete - Verification Needed\n\n${findingsText}${nextActionText}\n\n---\n\n**Status:** ${result.status}`,
628
+ },
629
+ ],
630
+ };
631
+ }
632
+
633
+ if (result.status === 'awaiting_manual_wiring_answers') {
634
+ const questionnaireText = result.questionnaire
635
+ ? `\n\n## 📝 Manual Wiring Questionnaire\n\nAsk the user these questions ONE SECTION AT A TIME:\n\n${formatQuestionnaire(result.questionnaire)}\n\n⚠️ After collecting all answers, call the tool with \`manualWiring\` containing the user's responses.`
636
+ : '';
637
+
638
+ return {
639
+ content: [
640
+ {
641
+ type: 'text',
642
+ text: `# Manual Wiring Required\n\n${result.message || 'User declined codebase scanning. Please collect wiring information manually.'}${questionnaireText}\n\n---\n\n**Status:** ${result.status}`,
643
+ },
644
+ ],
645
+ };
646
+ }
647
+
648
+ // Handle guided mode - plan generated (after discovery complete)
649
+ if (result.status === 'plan_generated' || (result.mode === 'guided' && result.stage === 'plan' && result.plan)) {
650
+ const wiringSource = result.wiring?.source || 'unknown';
651
+ const todosText = result.wiring?.todos?.length > 0
652
+ ? `\n\n## ⚠️ TODOs (Need Developer Input)\n\n${result.wiring.todos.map(t => `- ${t}`).join('\n')}`
653
+ : '';
654
+
655
+ return {
656
+ content: [
657
+ {
658
+ type: 'text',
659
+ text: `# Installation Plan Generated\n\n**Wiring Source:** ${wiringSource}${todosText}\n\n${result.plan}\n\n---\n\n## 🎯 NEXT STEPS\n\n1. **PRESENT THIS PLAN TO THE USER** - Show them the plan above\n2. **ASK FOR APPROVAL** - Ask: "Would you like me to implement this plan?"\n3. **IF APPROVED** - Call this tool again with:\n - mode: "guided"\n - stage: "apply"\n - approved: true\n4. **EXECUTE THE PLAN** - Make the file edits described in the plan\n5. **CHECK DEV CONSOLE** - After implementation, check browser console for Velt errors\n\n---\n\n⚠️ **CRITICAL**: Do NOT make any file changes until the user approves this plan.\n\n---\n\n**IMPLEMENTATION RULES:**\n• Use installed **Agent Skills** as PRIMARY reference (velt-setup-best-practices, velt-comments-best-practices, velt-crdt-best-practices, velt-notifications-best-practices)\n• Use Docs URLs ONLY for features without skills (presence, cursors, recorder)\n• Use Velt Docs MCP ONLY for user follow-up questions after implementation\n• ONLY implement features the user requested\n• Use CLI-generated: authentication, user setup, document setup\n• DO NOT use VeltTools or ui-customization folder (unless requested)\n• DO NOT use CLI-generated component files as implementation reference`,
660
+ },
661
+ ],
662
+ };
663
+ }
664
+
665
+ // Handle guided mode - apply stage
666
+ if (result.status === 'apply_complete' || (result.mode === 'guided' && result.stage === 'apply')) {
667
+ const validationSummary = result.validation
668
+ ? `\n\n## Validation Results\n${result.validation.checks.map(c => `- ${c.status === 'pass' ? '✅' : '❌'} ${c.name}: ${c.message}`).join('\n')}\n\n**Score:** ${result.validation.score}`
669
+ : '';
670
+
671
+ return {
672
+ content: [
673
+ {
674
+ type: 'text',
675
+ text: `# Installation Apply Stage Complete\n\n${result.message}${validationSummary}\n\n## 🔍 Final Steps\n\n1. Start your development server: \`npm run dev\`\n2. Open browser DevTools Console (F12)\n3. Look for Velt messages and errors\n4. Test the installed features\n5. If errors occur, query Velt Docs MCP for solutions`,
676
+ },
677
+ ],
678
+ };
679
+ }
680
+
681
+ // Handle errors or unexpected states
682
+ if (result.status === 'error' || result.status === 'failed') {
683
+ return {
684
+ content: [
685
+ {
686
+ type: 'text',
687
+ text: JSON.stringify(result, null, 2),
688
+ },
689
+ ],
690
+ isError: true,
691
+ };
692
+ }
693
+
694
+ // Default: return full result
695
+ return {
696
+ content: [
697
+ {
698
+ type: 'text',
699
+ text: JSON.stringify(result, null, 2),
700
+ },
701
+ ],
702
+ };
703
+ }
704
+
705
+ case 'install_velt_freestyle':
706
+ // Validate projectPath is provided
707
+ if (!args?.projectPath) {
708
+ throw new Error('projectPath is required. Please ask the user which directory they want to install Velt in.');
709
+ }
710
+
711
+ const result = await installVeltFreestyle({
712
+ projectPath: args.projectPath,
713
+ apiKey: args?.apiKey || null, // Optional - will read from .env if not provided
714
+ authToken: args?.authToken || null, // Optional
715
+ });
716
+ return {
717
+ content: [
718
+ {
719
+ type: 'text',
720
+ text: JSON.stringify(result, null, 2),
721
+ },
722
+ ],
723
+ };
724
+
725
+ default:
726
+ throw new Error(`Unknown tool: ${name}`);
727
+ }
728
+ } catch (error) {
729
+ return {
730
+ content: [
731
+ {
732
+ type: 'text',
733
+ text: JSON.stringify(
734
+ {
735
+ error: error.message,
736
+ stack: error.stack,
737
+ },
738
+ null,
739
+ 2
740
+ ),
741
+ },
742
+ ],
743
+ isError: true,
744
+ };
745
+ }
746
+ });
747
+
748
+ // Connect to stdio transport
749
+ const transport = new StdioServerTransport();
750
+ await server.connect(transport);
751
+
752
+ console.error('Velt MCP Installer server running on stdio');
753
+ }
754
+