Skip to main content
Glama
unifiedTools.js11.6 kB
import { analyzeWidget } from './widgetAnalyzer.js'; import { validateFlutterDocs } from './docsValidator.js'; import { analyzePubPackage } from './pubAnalyzer.js'; import { analyzePerformance } from './performanceAnalyzer.js'; import { suggestImprovements } from './improvementSuggester.js'; import { getCacheManager } from '../cache/cacheManager.js'; import { getTokenManager } from '../utils/tokenManager.js'; import { errorHandler } from '../utils/errorHandler.js'; import { getFlutterDocsService } from '../services/flutterDocsService.js'; import axios from 'axios'; import * as cheerio from 'cheerio'; const cache = getCacheManager(); const tokenManager = getTokenManager(); const docsService = getFlutterDocsService(); // Universal Flutter search tool export async function flutterSearch(args) { const { query, limit = 10, maxTokens = 4000 } = args; try { // Auto-detect query type const queryType = detectQueryType(query); // Search across multiple sources in parallel const searchPromises = []; if (queryType.includes('widget') || queryType.includes('class')) { searchPromises.push(searchFlutterDocs(query)); } if (queryType.includes('package')) { searchPromises.push(searchPubPackages(query)); } if (queryType.includes('code')) { searchPromises.push(searchCodeExamples(query)); } const results = await Promise.all(searchPromises); const flatResults = results.flat().slice(0, limit); // Format and truncate results const formattedResults = formatSearchResults(flatResults); const truncatedResults = tokenManager.smartTruncate(formattedResults, maxTokens); return { content: [ { type: 'text', text: JSON.stringify(truncatedResults, null, 2) } ] }; } catch (error) { return { content: [ { type: 'text', text: `Error searching Flutter resources: ${error.message}` } ] }; } } // Smart Flutter documentation and analysis tool export async function flutterAnalyze(args) { const { identifier, topic = 'all', maxTokens = 4000, includeExamples = true, includeAnalysis = true } = args; try { const identifierType = detectIdentifierType(identifier); const results = {}; // Fetch relevant information based on identifier type if (identifierType === 'widget' || identifierType === 'class') { if (topic === 'all' || topic === 'docs') { results.documentation = await fetchFlutterDocumentation(identifier); } if (includeAnalysis && (topic === 'all' || topic === 'analysis')) { // If code is provided, analyze it if (args.code) { results.analysis = await analyzeWidget({ widgetCode: args.code }); results.performance = await analyzePerformance({ code: args.code }); } } if (includeExamples && (topic === 'all' || topic === 'examples')) { results.examples = await fetchCodeExamples(identifier); } } else if (identifierType === 'package') { results.packageInfo = await analyzePubPackage({ packageName: identifier, checkDependencies: true, checkScores: true }); } // Generate improvement suggestions if code is provided if (args.code && includeAnalysis) { results.suggestions = await suggestImprovements({ code: args.code, focusAreas: ['performance', 'accessibility', 'maintainability'] }); } // Truncate results to fit token limit const truncatedResults = tokenManager.smartTruncate(results, maxTokens); return { content: [ { type: 'text', text: JSON.stringify(truncatedResults, null, 2) } ] }; } catch (error) { return { content: [ { type: 'text', text: `Error analyzing Flutter resource: ${error.message}` } ] }; } } // Health check and status tool export async function flutterStatus(args) { try { const status = { cache: cache.getStats(), errorHandling: errorHandler.getStatus(), timestamp: new Date().toISOString(), version: '2.0.0', capabilities: { search: true, analysis: true, documentation: true, packageAnalysis: true, performanceCheck: true, caching: true, tokenManagement: true } }; return { content: [ { type: 'text', text: JSON.stringify(status, null, 2) } ] }; } catch (error) { return { content: [ { type: 'text', text: `Error getting status: ${error.message}` } ] }; } } // Helper functions function detectQueryType(query) { const types = []; const lowerQuery = query.toLowerCase(); // Check for widget indicators if (lowerQuery.includes('widget') || lowerQuery.includes('container') || lowerQuery.includes('scaffold') || lowerQuery.includes('button')) { types.push('widget'); } // Check for package indicators if (lowerQuery.includes('package') || lowerQuery.includes('pub.dev') || lowerQuery.includes('dependency')) { types.push('package'); } // Check for code indicators if (lowerQuery.includes('example') || lowerQuery.includes('how to') || lowerQuery.includes('implement')) { types.push('code'); } // Default to all types if no specific indicator if (types.length === 0) { types.push('widget', 'package', 'code'); } return types; } function detectIdentifierType(identifier) { // Flutter widget patterns const flutterWidgets = ['Container', 'Scaffold', 'Text', 'Column', 'Row', 'ListView']; if (flutterWidgets.some(w => identifier.includes(w))) { return 'widget'; } // Dart core library patterns if (identifier.startsWith('dart:')) { return 'dart'; } // Package pattern if (identifier.includes(':') || identifier.includes('/')) { return 'package'; } // Check if it looks like a class name (PascalCase) if (/^[A-Z][a-zA-Z0-9]*$/.test(identifier)) { return 'class'; } return 'package'; // Default to package } async function searchFlutterDocs(query) { const cacheKey = { query, type: 'flutter_docs_search' }; const cached = await cache.get('flutterDocs', cacheKey); if (cached) return cached; try { const results = await docsService.searchFlutterDocs(query); await cache.set('flutterDocs', cacheKey, results); return results; } catch (error) { console.error('Error searching Flutter docs:', error); return []; } } async function searchPubPackages(query) { const cacheKey = { query, type: 'pub_search' }; const cached = await cache.get('pubPackage', cacheKey); if (cached) return cached; try { const response = await errorHandler.executeWithProtection( 'pub.dev', async () => { const res = await axios.get(`https://pub.dev/api/search?q=${encodeURIComponent(query)}`); return res.data; } ); const results = response.packages.map(pkg => ({ type: 'package', name: pkg.package, description: pkg.description, version: pkg.version, score: pkg.score })); await cache.set('pubPackage', cacheKey, results); return results; } catch (error) { console.error('Error searching pub packages:', error); return []; } } async function searchCodeExamples(query) { // This would search through Flutter cookbook, examples, etc. return [ { type: 'example', title: 'Basic Container Example', code: `Container( width: 200, height: 200, color: Colors.blue, child: Text('Hello World'), )`, source: 'Flutter Documentation' } ]; } async function fetchFlutterDocumentation(identifier) { const cacheKey = { identifier, type: 'flutter_doc' }; const cached = await cache.get('flutterDocs', cacheKey); if (cached) return cached; try { let documentation; // Determine the type and fetch accordingly if (identifier.startsWith('dart:')) { const [, library, className] = identifier.match(/dart:(\w+)\.(\w+)/) || []; documentation = await docsService.fetchDartClass(className, library); } else if (identifier.includes(':')) { // Package format: package:provider/provider.dart const [packagePart] = identifier.split('/'); const packageName = packagePart.split(':')[1]; documentation = await docsService.fetchPackageDocumentation(packageName); } else { // Flutter widget documentation = await docsService.fetchFlutterClass(identifier); } await cache.set('flutterDocs', cacheKey, documentation); return documentation; } catch (error) { console.error('Error fetching Flutter documentation:', error); return null; } } async function fetchCodeExamples(identifier) { // Fetch code examples for the identifier return [ { title: `${identifier} Example`, code: '// Example code here', description: 'Example usage' } ]; } function formatSearchResults(results) { return { totalResults: results.length, results: results.map(r => ({ type: r.type, name: r.name || r.title, description: r.description, relevance: r.score || 1.0, metadata: { url: r.url, version: r.version, source: r.source } })) }; } // Export consolidated tools export const unifiedTools = [ { name: 'flutter_search', description: 'Universal search across Flutter/Dart documentation, packages, and examples', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query' }, limit: { type: 'number', description: 'Maximum number of results (default: 10)' }, maxTokens: { type: 'number', description: 'Maximum tokens in response (default: 4000)' } }, required: ['query'] }, handler: flutterSearch }, { name: 'flutter_analyze', description: 'Smart Flutter documentation fetcher and code analyzer', inputSchema: { type: 'object', properties: { identifier: { type: 'string', description: 'Widget name, class name, or package identifier' }, code: { type: 'string', description: 'Optional code to analyze' }, topic: { type: 'string', description: 'Specific topic: all, docs, analysis, examples (default: all)' }, maxTokens: { type: 'number', description: 'Maximum tokens in response (default: 4000)' }, includeExamples: { type: 'boolean', description: 'Include code examples (default: true)' }, includeAnalysis: { type: 'boolean', description: 'Include code analysis (default: true)' } }, required: ['identifier'] }, handler: flutterAnalyze }, { name: 'flutter_status', description: 'Health check and cache statistics', inputSchema: { type: 'object', properties: {} }, handler: flutterStatus } ]; // Backward compatibility exports export { flutterSearch as searchFlutterResources }; export { flutterAnalyze as analyzeFlutterCode }; export { flutterStatus as healthCheck };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/dvillegastech/flutter_mcp_2'

If you have feedback or need assistance with the MCP directory API, please join our Discord server