@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.
@@ -0,0 +1,718 @@
1
+ /**
2
+ * Velt MCP Query Utilities
3
+ *
4
+ * Queries the Velt Docs MCP server for implementation patterns and best practices
5
+ *
6
+ * Velt Docs MCP Server: https://docs.velt.dev/mcp
7
+ */
8
+
9
+ import https from 'https';
10
+ import { URL } from 'url';
11
+ import fs from 'fs';
12
+ import path from 'path';
13
+
14
+ /**
15
+ * Queries Velt documentation for implementation patterns
16
+ *
17
+ * Tries three strategies in order:
18
+ * 1. Velt Docs MCP server (https://docs.velt.dev/mcp)
19
+ * 2. Direct URL fetch (https://docs.velt.dev/async-collaboration/comments/setup/freestyle)
20
+ * 3. Hardcoded fallback patterns
21
+ *
22
+ * @param {Object} params
23
+ * @param {string} params.question - Question to ask Velt MCP
24
+ * @returns {Promise<Object>} Query result with patterns
25
+ */
26
+ export async function queryVeltMCP({ question }) {
27
+ const query = question || 'How do I implement freestyle comments in Next.js app router?';
28
+ const veltDocsMCPUrl = 'https://docs.velt.dev/mcp';
29
+ const veltDocsUrl = 'https://docs.velt.dev/async-collaboration/comments/setup/freestyle';
30
+
31
+ console.error('🔍 Fetching Velt documentation for implementation patterns...');
32
+
33
+ // ========================================================================
34
+ // Strategy 1: Try Velt Docs MCP server first
35
+ // ========================================================================
36
+ console.error(' Strategy 1: Attempting Velt Docs MCP server...');
37
+ console.error(` MCP URL: ${veltDocsMCPUrl}`);
38
+
39
+ try {
40
+ if (typeof fetch !== 'undefined') {
41
+ // Node 18+ with native fetch
42
+ console.error(' Using native fetch (Node 18+)');
43
+
44
+ // Try to discover available tools
45
+ console.error(' Step 1: Discovering available tools...');
46
+ let toolsListResponse;
47
+ try {
48
+ toolsListResponse = await fetch(veltDocsMCPUrl, {
49
+ method: 'POST',
50
+ headers: {
51
+ 'Content-Type': 'application/json',
52
+ 'Accept': 'application/json',
53
+ },
54
+ body: JSON.stringify({
55
+ jsonrpc: '2.0',
56
+ method: 'tools/list',
57
+ id: Date.now(),
58
+ }),
59
+ signal: AbortSignal.timeout(5000), // Reduced to 5 seconds
60
+ });
61
+ } catch (listError) {
62
+ throw new Error(`Failed to connect to Velt Docs MCP: ${listError.message}`);
63
+ }
64
+
65
+ if (!toolsListResponse.ok) {
66
+ throw new Error(`HTTP ${toolsListResponse.status}: ${toolsListResponse.statusText}`);
67
+ }
68
+
69
+ const toolsList = await toolsListResponse.json();
70
+
71
+ if (toolsList.error) {
72
+ throw new Error(`MCP error: ${toolsList.error.message || 'Unknown error'}`);
73
+ }
74
+
75
+ const availableTools = toolsList.result?.tools || [];
76
+ console.error(` ✓ Found ${availableTools.length} available tools`);
77
+
78
+ if (availableTools.length > 0) {
79
+ console.error(` Tools: ${availableTools.map(t => t.name).join(', ')}`);
80
+ }
81
+
82
+ // Find the appropriate tool for querying documentation
83
+ const searchTool = availableTools.find(t =>
84
+ t.name.toLowerCase().includes('search') ||
85
+ t.name.toLowerCase().includes('query') ||
86
+ t.name.toLowerCase().includes('docs') ||
87
+ t.name.toLowerCase().includes('velt')
88
+ ) || availableTools[0];
89
+
90
+ if (!searchTool) {
91
+ throw new Error('No tools available in Velt Docs MCP server');
92
+ }
93
+
94
+ console.error(` Step 2: Calling tool: ${searchTool.name}`);
95
+
96
+ // Call the tool
97
+ const response = await fetch(veltDocsMCPUrl, {
98
+ method: 'POST',
99
+ headers: {
100
+ 'Content-Type': 'application/json',
101
+ 'Accept': 'application/json',
102
+ },
103
+ body: JSON.stringify({
104
+ jsonrpc: '2.0',
105
+ method: 'tools/call',
106
+ id: Date.now(),
107
+ params: {
108
+ name: searchTool.name,
109
+ arguments: {
110
+ query: query,
111
+ },
112
+ },
113
+ }),
114
+ signal: AbortSignal.timeout(5000), // Reduced to 5 seconds for faster failure
115
+ });
116
+
117
+ if (!response.ok) {
118
+ const errorText = await response.text();
119
+ throw new Error(`HTTP ${response.status}: ${response.statusText} - ${errorText}`);
120
+ }
121
+
122
+ const mcpResponse = await response.json();
123
+
124
+ // Parse the MCP response
125
+ if (mcpResponse.error) {
126
+ throw new Error(mcpResponse.error.message || 'MCP query failed');
127
+ }
128
+
129
+ // Extract patterns from the response
130
+ const result = mcpResponse.result || mcpResponse;
131
+ const patterns = extractPatternsFromMCPResponse(result);
132
+
133
+ // Show what patterns were found
134
+ const foundPatterns = [];
135
+ if (patterns.providerPattern) foundPatterns.push('VeltProvider');
136
+ if (patterns.commentsPattern) foundPatterns.push('VeltComments');
137
+ if (patterns.sidebarPattern) foundPatterns.push('VeltCommentsSidebar');
138
+ if (patterns.environmentPattern) foundPatterns.push('Environment variables');
139
+
140
+ console.error('✅ Successfully queried Velt Docs MCP server!');
141
+ console.error(` ✓ Found patterns: ${foundPatterns.length > 0 ? foundPatterns.join(', ') : 'Using fallback patterns'}`);
142
+ console.error(` ✓ Source: Velt Docs MCP (${veltDocsMCPUrl})`);
143
+
144
+ return {
145
+ success: true,
146
+ data: patterns,
147
+ query: query,
148
+ source: 'velt-docs-mcp',
149
+ message: `✅ Successfully queried Velt Docs MCP server and extracted patterns from real documentation`,
150
+ };
151
+ } else {
152
+ // Node < 18: Use https module for MCP
153
+ console.error(' Using https module (Node < 18)');
154
+
155
+ try {
156
+ const toolsList = await makeHttpsRequest(veltDocsMCPUrl, {
157
+ jsonrpc: '2.0',
158
+ method: 'tools/list',
159
+ id: Date.now(),
160
+ });
161
+
162
+ const toolName = toolsList.result?.tools?.find(t =>
163
+ t.name.toLowerCase().includes('search') ||
164
+ t.name.toLowerCase().includes('query') ||
165
+ t.name.toLowerCase().includes('docs')
166
+ )?.name || toolsList.result?.tools?.[0]?.name || 'query_docs';
167
+
168
+ console.error(` Discovered tool: ${toolName}`);
169
+
170
+ const mcpResponse = await makeHttpsRequest(veltDocsMCPUrl, {
171
+ jsonrpc: '2.0',
172
+ method: 'tools/call',
173
+ id: Date.now(),
174
+ params: {
175
+ name: toolName,
176
+ arguments: {
177
+ query: query,
178
+ },
179
+ },
180
+ });
181
+
182
+ if (mcpResponse.error) {
183
+ throw new Error(mcpResponse.error.message || 'MCP query failed');
184
+ }
185
+
186
+ const result = mcpResponse.result || mcpResponse;
187
+ const patterns = extractPatternsFromMCPResponse(result);
188
+
189
+ console.error('✅ Successfully queried Velt Docs MCP server!');
190
+ console.error(` ✓ Source: Velt Docs MCP (${veltDocsMCPUrl})`);
191
+
192
+ return {
193
+ success: true,
194
+ data: patterns,
195
+ query: query,
196
+ source: 'velt-docs-mcp',
197
+ message: `✅ Successfully queried Velt Docs MCP server and extracted patterns from real documentation`,
198
+ };
199
+ } catch (mcpError) {
200
+ throw mcpError;
201
+ }
202
+ }
203
+ } catch (mcpError) {
204
+ // ========================================================================
205
+ // Strategy 2: Fallback to direct URL fetch
206
+ // ========================================================================
207
+ console.error(` ❌ MCP server failed: ${mcpError.message}`);
208
+ console.error(' Strategy 2: Falling back to direct URL fetch...');
209
+ console.error(` URL: ${veltDocsUrl}`);
210
+
211
+ try {
212
+ // Fetch documentation page directly
213
+ console.error(` Fetching documentation page...`);
214
+
215
+ let htmlContent;
216
+
217
+ if (typeof fetch !== 'undefined') {
218
+ // Node 18+ with native fetch
219
+ console.error(' Using native fetch (Node 18+)');
220
+
221
+ const response = await fetch(veltDocsUrl, {
222
+ headers: {
223
+ 'Accept': 'text/html',
224
+ 'User-Agent': 'Velt-MCP-Installer/1.0',
225
+ },
226
+ signal: AbortSignal.timeout(5000), // Reduced to 5 seconds
227
+ });
228
+
229
+ if (!response.ok) {
230
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
231
+ }
232
+
233
+ htmlContent = await response.text();
234
+ } else {
235
+ // Node < 18: Use https module
236
+ console.error(' Using https module (Node < 18)');
237
+ htmlContent = await fetchHtmlPage(veltDocsUrl);
238
+ }
239
+
240
+ console.error(' ✓ Successfully fetched documentation page');
241
+
242
+ // Extract patterns from HTML content
243
+ console.error(' 📖 Extracting code patterns from documentation...');
244
+ const patterns = extractPatternsFromDocsContent(htmlContent, query);
245
+
246
+ // Show what patterns were found
247
+ const foundPatterns = [];
248
+ if (patterns.providerPattern?.code) foundPatterns.push('VeltProvider');
249
+ if (patterns.commentsPattern?.code) foundPatterns.push('VeltComments');
250
+ if (patterns.sidebarPattern?.code) foundPatterns.push('VeltCommentsSidebar');
251
+ if (patterns.environmentPattern?.code) foundPatterns.push('Environment variables');
252
+
253
+ console.error('✅ Successfully extracted patterns from Velt documentation!');
254
+ console.error(` ✓ Found patterns: ${foundPatterns.length > 0 ? foundPatterns.join(', ') : 'Using fallback patterns'}`);
255
+ console.error(` ✓ Source: Velt Docs URL (${veltDocsUrl})`);
256
+
257
+ return {
258
+ success: true,
259
+ data: patterns,
260
+ query: query,
261
+ source: 'velt-docs-url',
262
+ message: `✅ Successfully fetched and extracted patterns from Velt documentation (fallback from MCP)`,
263
+ };
264
+ } catch (urlError) {
265
+ // ========================================================================
266
+ // Strategy 3: Fallback to hardcoded patterns
267
+ // ========================================================================
268
+ console.error(` ❌ URL fetch failed: ${urlError.message}`);
269
+ console.error(' Strategy 3: Using fallback patterns based on known best practices');
270
+
271
+ const fallbackPatterns = getFallbackPatterns();
272
+
273
+ return {
274
+ success: true,
275
+ data: fallbackPatterns,
276
+ query: query,
277
+ source: 'fallback',
278
+ warning: 'Using fallback patterns. Both MCP and URL fetch failed.',
279
+ message: `⚠️ Using fallback patterns (MCP and URL fetch failed). Patterns are based on known best practices.`,
280
+ };
281
+ }
282
+ }
283
+ }
284
+
285
+ /**
286
+ * Makes HTTPS request using Node.js built-in https module
287
+ * (for Node < 18 compatibility)
288
+ *
289
+ * @param {string} url - URL to request
290
+ * @param {Object} data - JSON data to send
291
+ * @param {number} timeout - Timeout in milliseconds (default: 5000)
292
+ * @returns {Promise<Object>} Parsed JSON response
293
+ */
294
+ function makeHttpsRequest(url, data, timeout = 5000) {
295
+ // Wrap in Promise.race to guarantee timeout
296
+ const timeoutPromise = new Promise((_, reject) => {
297
+ setTimeout(() => {
298
+ reject(new Error(`Request timeout after ${timeout}ms`));
299
+ }, timeout);
300
+ });
301
+
302
+ const requestPromise = new Promise((resolve, reject) => {
303
+ const urlObj = new URL(url);
304
+ const postData = JSON.stringify(data);
305
+
306
+ const options = {
307
+ hostname: urlObj.hostname,
308
+ port: urlObj.port || 443,
309
+ path: urlObj.pathname,
310
+ method: 'POST',
311
+ headers: {
312
+ 'Content-Type': 'application/json',
313
+ 'Accept': 'application/json, text/event-stream',
314
+ 'Content-Length': Buffer.byteLength(postData),
315
+ },
316
+ timeout: timeout,
317
+ };
318
+
319
+ const req = https.request(options, (res) => {
320
+ let responseData = '';
321
+ let responseSize = 0;
322
+ const maxResponseSize = 10 * 1024 * 1024; // 10MB max
323
+
324
+ res.on('data', (chunk) => {
325
+ responseSize += chunk.length;
326
+
327
+ // Prevent memory exhaustion
328
+ if (responseSize > maxResponseSize) {
329
+ req.destroy();
330
+ reject(new Error(`Response too large (exceeded ${maxResponseSize} bytes)`));
331
+ return;
332
+ }
333
+
334
+ responseData += chunk;
335
+ });
336
+
337
+ res.on('end', () => {
338
+ try {
339
+ const parsed = JSON.parse(responseData);
340
+ resolve(parsed);
341
+ } catch (error) {
342
+ reject(new Error(`Failed to parse response: ${error.message}`));
343
+ }
344
+ });
345
+ });
346
+
347
+ req.on('error', (error) => {
348
+ reject(new Error(`Request error: ${error.message}`));
349
+ });
350
+
351
+ req.on('timeout', () => {
352
+ req.destroy();
353
+ reject(new Error('Socket timeout'));
354
+ });
355
+
356
+ req.write(postData);
357
+ req.end();
358
+ });
359
+
360
+ return Promise.race([requestPromise, timeoutPromise]);
361
+ }
362
+
363
+ /**
364
+ * Extracts code patterns from Velt MCP response
365
+ *
366
+ * @param {Object} mcpResult - Result from Velt Docs MCP server
367
+ * @returns {Object} Extracted patterns
368
+ */
369
+ function extractPatternsFromMCPResponse(mcpResult) {
370
+ // The MCP response contains documentation content
371
+ // We need to parse it to extract code patterns
372
+
373
+ const content = mcpResult?.content || [];
374
+ let patterns = {
375
+ summary: 'Freestyle comments implementation patterns from Velt documentation',
376
+ providerPattern: null,
377
+ commentsPattern: null,
378
+ sidebarPattern: null,
379
+ environmentPattern: null,
380
+ rawContent: content,
381
+ };
382
+
383
+ // Parse content to find code examples
384
+ for (const item of content) {
385
+ if (item.type === 'text') {
386
+ const text = item.text;
387
+
388
+ // Look for VeltProvider pattern
389
+ if (text.includes('VeltProvider') && !patterns.providerPattern) {
390
+ patterns.providerPattern = extractCodeBlock(text, 'VeltProvider');
391
+ }
392
+
393
+ // Look for VeltComments pattern
394
+ if (text.includes('VeltComments') && !text.includes('VeltCommentsSidebar') && !patterns.commentsPattern) {
395
+ patterns.commentsPattern = extractCodeBlock(text, 'VeltComments');
396
+ }
397
+
398
+ // Look for VeltCommentsSidebar pattern
399
+ if (text.includes('VeltCommentsSidebar') && !patterns.sidebarPattern) {
400
+ patterns.sidebarPattern = extractCodeBlock(text, 'VeltCommentsSidebar');
401
+ }
402
+
403
+ // Look for environment variables
404
+ if (text.includes('NEXT_PUBLIC_VELT_API_KEY') && !patterns.environmentPattern) {
405
+ patterns.environmentPattern = extractCodeBlock(text, 'NEXT_PUBLIC_VELT_API_KEY');
406
+ }
407
+ }
408
+ }
409
+
410
+ // If we didn't find patterns, use fallback
411
+ if (!patterns.providerPattern || !patterns.commentsPattern) {
412
+ const fallback = getFallbackPatterns();
413
+ return {
414
+ ...fallback,
415
+ source: 'mcp-with-fallback',
416
+ rawContent: content,
417
+ };
418
+ }
419
+
420
+ return patterns;
421
+ }
422
+
423
+ /**
424
+ * Extracts code block from text
425
+ *
426
+ * @param {string} text - Text to search
427
+ * @param {string} keyword - Keyword to find
428
+ * @returns {Object|null} Extracted code pattern
429
+ */
430
+ function extractCodeBlock(text, keyword) {
431
+ // Try to find code blocks containing the keyword
432
+ const codeBlockRegex = /```(?:tsx?|jsx?|javascript|typescript)?\n([\s\S]*?)```/g;
433
+ let match;
434
+
435
+ while ((match = codeBlockRegex.exec(text)) !== null) {
436
+ if (match[1].includes(keyword)) {
437
+ return {
438
+ code: match[1].trim(),
439
+ description: `Code pattern for ${keyword}`,
440
+ };
441
+ }
442
+ }
443
+
444
+ return null;
445
+ }
446
+
447
+ /**
448
+ * Fetches HTML page using https module (for Node < 18)
449
+ *
450
+ * @param {string} url - URL to fetch
451
+ * @returns {Promise<string>} HTML content
452
+ */
453
+ function fetchHtmlPage(url) {
454
+ return new Promise((resolve, reject) => {
455
+ const urlObj = new URL(url);
456
+
457
+ const options = {
458
+ hostname: urlObj.hostname,
459
+ port: urlObj.port || 443,
460
+ path: urlObj.pathname + (urlObj.search || ''),
461
+ method: 'GET',
462
+ headers: {
463
+ 'Accept': 'text/html',
464
+ 'User-Agent': 'Velt-MCP-Installer/1.0',
465
+ },
466
+ timeout: 5000, // Reduced to 5 seconds for faster failure
467
+ };
468
+
469
+ const req = https.request(options, (res) => {
470
+ let htmlContent = '';
471
+
472
+ res.on('data', (chunk) => {
473
+ htmlContent += chunk;
474
+ });
475
+
476
+ res.on('end', () => {
477
+ if (res.statusCode !== 200) {
478
+ reject(new Error(`HTTP ${res.statusCode}: ${res.statusMessage}`));
479
+ } else {
480
+ resolve(htmlContent);
481
+ }
482
+ });
483
+ });
484
+
485
+ req.on('error', (error) => {
486
+ reject(error);
487
+ });
488
+
489
+ req.on('timeout', () => {
490
+ req.destroy();
491
+ reject(new Error('Request timeout'));
492
+ });
493
+
494
+ req.end();
495
+ });
496
+ }
497
+
498
+ /**
499
+ * Extracts patterns from Velt documentation website content
500
+ *
501
+ * @param {string} htmlContent - HTML content from docs.velt.dev
502
+ * @param {string} query - Original query
503
+ * @returns {Object} Extracted patterns
504
+ */
505
+ function extractPatternsFromDocsContent(htmlContent, query) {
506
+ // Start with fallback patterns
507
+ const patterns = getFallbackPatterns();
508
+
509
+ // Try to extract code blocks from HTML
510
+ // Look for <pre><code> blocks or markdown code fences in the HTML
511
+
512
+ // Extract code blocks from HTML
513
+ const codeBlockRegex = /<pre[^>]*><code[^>]*>([\s\S]*?)<\/code><\/pre>/gi;
514
+ const codeBlocks = [];
515
+ let match;
516
+
517
+ while ((match = codeBlockRegex.exec(htmlContent)) !== null) {
518
+ const code = match[1]
519
+ .replace(/&lt;/g, '<')
520
+ .replace(/&gt;/g, '>')
521
+ .replace(/&amp;/g, '&')
522
+ .replace(/&quot;/g, '"')
523
+ .trim();
524
+
525
+ if (code.length > 20) { // Only keep substantial code blocks
526
+ codeBlocks.push(code);
527
+ }
528
+ }
529
+
530
+ // Try to find patterns in code blocks
531
+ for (const code of codeBlocks) {
532
+ // Look for VeltProvider pattern
533
+ if (code.includes('VeltProvider') && !patterns.providerPattern?.code) {
534
+ patterns.providerPattern = {
535
+ description: 'Wrap root layout with VeltProvider',
536
+ code: code,
537
+ location: 'app/layout.tsx',
538
+ };
539
+ }
540
+
541
+ // Look for VeltComments pattern
542
+ if (code.includes('VeltComments') && !code.includes('VeltCommentsSidebar') && !patterns.commentsPattern?.code) {
543
+ patterns.commentsPattern = {
544
+ description: 'Add VeltComments component to enable freestyle comments',
545
+ code: code,
546
+ location: 'app/page.tsx',
547
+ };
548
+ }
549
+
550
+ // Look for VeltCommentsSidebar pattern
551
+ if (code.includes('VeltCommentsSidebar') && !patterns.sidebarPattern?.code) {
552
+ patterns.sidebarPattern = {
553
+ description: 'Add VeltCommentsSidebar for comment UI',
554
+ code: code,
555
+ location: 'app/layout.tsx or components',
556
+ };
557
+ }
558
+
559
+ // Look for environment variables
560
+ if (code.includes('NEXT_PUBLIC_VELT_API_KEY') && !patterns.environmentPattern?.code) {
561
+ patterns.environmentPattern = {
562
+ description: 'Environment variables needed',
563
+ code: code,
564
+ location: '.env.local',
565
+ };
566
+ }
567
+ }
568
+
569
+ // Mark source if we found patterns
570
+ if (codeBlocks.length > 0) {
571
+ patterns.source = 'extracted-from-docs';
572
+ patterns.summary = 'Freestyle comments implementation patterns extracted from Velt documentation';
573
+ }
574
+
575
+ return patterns;
576
+ }
577
+
578
+ /**
579
+ * Detects libraries in the project by checking package.json
580
+ *
581
+ * @param {string} projectPath - Project root path
582
+ * @returns {Object} Library detection flags
583
+ */
584
+ export function detectLibraries(projectPath) {
585
+ const packageJsonPath = path.join(projectPath, 'package.json');
586
+
587
+ if (!fs.existsSync(packageJsonPath)) {
588
+ return {
589
+ hasReactFlow: false,
590
+ hasTiptap: false,
591
+ hasCodeMirror: false,
592
+ hasAgGrid: false,
593
+ hasTanStack: false,
594
+ };
595
+ }
596
+
597
+ try {
598
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
599
+ const allDeps = {
600
+ ...(packageJson.dependencies || {}),
601
+ ...(packageJson.devDependencies || {}),
602
+ };
603
+
604
+ // Check for ReactFlow (multiple possible package names)
605
+ const hasReactFlow = !!(
606
+ allDeps['reactflow'] ||
607
+ allDeps['@xyflow/react'] ||
608
+ allDeps['react-flow-renderer'] ||
609
+ allDeps['@reactflow/core']
610
+ );
611
+
612
+ // Check for Tiptap
613
+ const hasTiptap = !!(
614
+ allDeps['@tiptap/react'] ||
615
+ allDeps['@tiptap/core'] ||
616
+ allDeps['tiptap']
617
+ );
618
+
619
+ // Check for CodeMirror
620
+ const hasCodeMirror = !!(
621
+ allDeps['@codemirror/state'] ||
622
+ allDeps['@codemirror/view'] ||
623
+ allDeps['codemirror']
624
+ );
625
+
626
+ // Check for AG-Grid
627
+ const hasAgGrid = !!(
628
+ allDeps['ag-grid-react'] ||
629
+ allDeps['ag-grid-community'] ||
630
+ allDeps['ag-grid-enterprise']
631
+ );
632
+
633
+ // Check for TanStack Table
634
+ const hasTanStack = !!(
635
+ allDeps['@tanstack/react-table'] ||
636
+ allDeps['@tanstack/table-core']
637
+ );
638
+
639
+ return {
640
+ hasReactFlow,
641
+ hasTiptap,
642
+ hasCodeMirror,
643
+ hasAgGrid,
644
+ hasTanStack,
645
+ };
646
+ } catch (error) {
647
+ console.error(`Error detecting libraries: ${error.message}`);
648
+ return {
649
+ hasReactFlow: false,
650
+ hasTiptap: false,
651
+ hasCodeMirror: false,
652
+ hasAgGrid: false,
653
+ hasTanStack: false,
654
+ };
655
+ }
656
+ }
657
+
658
+ /**
659
+ * Fallback patterns if MCP server is unavailable
660
+ *
661
+ * @returns {Object} Hardcoded patterns
662
+ */
663
+ function getFallbackPatterns() {
664
+ return {
665
+ summary: 'Freestyle comments implementation patterns for Next.js App Router',
666
+ providerPattern: {
667
+ description: 'Wrap root layout with VeltProvider',
668
+ code: `import { VeltProvider } from '@veltdev/react';
669
+
670
+ export default function RootLayout({ children }) {
671
+ return (
672
+ <html>
673
+ <body>
674
+ <VeltProvider>
675
+ {children}
676
+ </VeltProvider>
677
+ </body>
678
+ </html>
679
+ );
680
+ }`,
681
+ location: 'app/layout.tsx',
682
+ },
683
+ commentsPattern: {
684
+ description: 'Add VeltComments component to enable freestyle comments',
685
+ code: `import { VeltComments } from '@veltdev/react';
686
+
687
+ export default function Page() {
688
+ return (
689
+ <div>
690
+ <VeltComments />
691
+ {/* Your page content */}
692
+ </div>
693
+ );
694
+ }`,
695
+ location: 'app/page.tsx',
696
+ },
697
+ sidebarPattern: {
698
+ description: 'Add VeltCommentsSidebar for comment UI',
699
+ code: `import { VeltCommentsSidebar } from '@veltdev/react';
700
+
701
+ export default function Layout({ children }) {
702
+ return (
703
+ <>
704
+ {children}
705
+ <VeltCommentsSidebar />
706
+ </>
707
+ );
708
+ }`,
709
+ location: 'app/layout.tsx or components',
710
+ },
711
+ environmentPattern: {
712
+ description: 'Environment variables needed',
713
+ code: `NEXT_PUBLIC_VELT_API_KEY=your_api_key_here`,
714
+ location: '.env.local',
715
+ },
716
+ };
717
+ }
718
+