UNPKG

xai-live-search-mcp

Version:

šŸ”„ xAI Live Search integration for Claude Code via MCP - BEAST MODE!

470 lines (467 loc) • 20.5 kB
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; import { XAIClient } from './xai-client.js'; import { XAIConfigSchema, LiveSearchParamsSchema, ChatWithSearchParamsSchema, } from './types.js'; // šŸ”„ CRACKED-JACKED xAI Live Search MCP Server class XAILiveSearchServer { server; xaiClient = null; constructor() { this.server = new Server({ name: 'xai-live-search', version: '1.0.0', description: 'šŸš€ xAI Live Search integration for Claude Code - BEAST MODE activated!', }, { capabilities: { tools: {}, }, }); // Auto-configure from environment if available this.autoConfigureFromEnvSync(); this.setupToolHandlers(); this.setupErrorHandling(); } autoConfigureFromEnvSync() { // Try multiple sources for API key const apiKey = process.env.XAI_API_KEY || this.loadFromEnvFile(); console.error(`šŸ”§ DEBUG: XAI_API_KEY = ${apiKey ? 'SET' : 'NOT SET'}`); if (apiKey) { try { const config = XAIConfigSchema.parse({ apiKey, model: process.env.XAI_MODEL || 'grok-3-latest', maxTokens: parseInt(process.env.XAI_MAX_TOKENS || '4096'), temperature: parseFloat(process.env.XAI_TEMPERATURE || '0.7'), }); this.xaiClient = new XAIClient(config); console.error('šŸ”„ Auto-configured xAI client from environment! šŸ’Ŗ'); } catch (error) { console.error('āš ļø Failed to auto-configure from environment:', error); } } else { console.error('āš ļø No XAI_API_KEY found in environment or .env file'); } } loadFromEnvFile() { try { const fs = require('fs'); const path = require('path'); const os = require('os'); // Try multiple locations for .env file (expanded list) const possiblePaths = [ // Project directory path.join(__dirname, '../.env'), path.join(process.cwd(), '.env'), // Global MCP locations path.join(os.homedir(), '.local/share/claude-mcp-servers/.env'), path.join(os.homedir(), '.claude-mcp-servers/.env'), path.join(os.homedir(), '.config/claude-mcp-servers/.env'), // Global xAI config locations path.join(os.homedir(), '.xai/.env'), path.join(os.homedir(), '.config/xai/.env'), // NPM global location path.join(os.homedir(), '.npm-global/lib/node_modules/xai-live-search-mcp/.env'), ]; console.error(`šŸ”§ DEBUG: Checking ${possiblePaths.length} possible .env locations...`); for (const envPath of possiblePaths) { console.error(`šŸ”§ DEBUG: Checking ${envPath}`); if (fs.existsSync(envPath)) { console.error(`šŸ”§ DEBUG: Found .env file at ${envPath}`); const envContent = fs.readFileSync(envPath, 'utf8'); const match = envContent.match(/XAI_API_KEY=(.+)/); if (match) { const apiKey = match[1].trim().replace(/['"]/g, ''); // Remove quotes console.error(`šŸ”§ DEBUG: Successfully loaded API key from: ${envPath}`); return apiKey; } else { console.error(`šŸ”§ DEBUG: .env file exists but no XAI_API_KEY found`); } } } console.error(`šŸ”§ DEBUG: No .env file with XAI_API_KEY found in any location`); } catch (error) { console.error('āš ļø Failed to load from .env file:', error); } return undefined; } setupToolHandlers() { // šŸ’Ŗ List available tools this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: 'xai_live_search', description: 'Perform live web search using xAI\'s Grok with real-time data access. CRUSHES information gaps!', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query - what you want to find on the web', }, maxResults: { type: 'number', description: 'Maximum number of search results (1-20)', minimum: 1, maximum: 20, default: 10, }, searchType: { type: 'string', enum: ['web', 'news', 'x'], description: 'Type of search: web (general), news (recent news), x (X/Twitter posts)', default: 'web', }, includeTimestamp: { type: 'boolean', description: 'Include timestamps in results when available', default: true, }, }, required: ['query'], }, }, { name: 'xai_chat_with_search', description: 'Chat with xAI Grok using live search integration. Provides AI responses enhanced with real-time web data!', inputSchema: { type: 'object', properties: { message: { type: 'string', description: 'Your message/question for the AI assistant', }, returnCitations: { type: 'boolean', description: 'Include citations in the response', default: true, }, maxSearchResults: { type: 'number', description: 'Maximum search results to include (1-10)', minimum: 1, maximum: 10, default: 5, }, searchQuery: { type: 'string', description: 'Optional specific search query (defaults to extracting from message)', }, }, required: ['message'], }, }, { name: 'xai_configure', description: 'Configure xAI API settings. Set your API key and preferences.', inputSchema: { type: 'object', properties: { apiKey: { type: 'string', description: 'Your xAI API key (required)', }, model: { type: 'string', description: 'xAI model to use', default: 'grok-3', }, maxTokens: { type: 'number', description: 'Maximum tokens per request', default: 4096, }, temperature: { type: 'number', description: 'Response creativity (0.0-2.0)', minimum: 0, maximum: 2, default: 0.7, }, }, required: ['apiKey'], }, }, { name: 'xai_test_connection', description: 'Test connection to xAI API and verify authentication.', inputSchema: { type: 'object', properties: {}, }, }, ], }; }); // šŸš€ Handle tool calls this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; console.error(`šŸ”§ DEBUG: Tool call received - name: ${name}, args: ${JSON.stringify(args)}`); try { let result; switch (name) { case 'xai_configure': console.error('šŸ”§ DEBUG: Calling handleConfigure'); result = await this.handleConfigure(args); break; case 'xai_test_connection': console.error('šŸ”§ DEBUG: Calling handleTestConnection'); result = await this.handleTestConnection(); break; case 'xai_live_search': console.error('šŸ”§ DEBUG: Calling handleLiveSearch'); result = await this.handleLiveSearch(args); break; case 'xai_chat_with_search': console.error('šŸ”§ DEBUG: Calling handleChatWithSearch'); result = await this.handleChatWithSearch(args); break; default: throw new Error(`Unknown tool: ${name}`); } console.error(`šŸ”§ DEBUG: Tool ${name} completed successfully`); return result; } catch (error) { console.error(`šŸ”§ DEBUG: Tool ${name} failed with error:`, error); const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; const errorResult = { content: [ { type: 'text', text: `āŒ Error: ${errorMessage}`, }, ], isError: true, }; console.error(`šŸ”§ DEBUG: Returning error result: ${JSON.stringify(errorResult)}`); return errorResult; } }); } async handleConfigure(args) { try { const config = XAIConfigSchema.parse({ apiKey: args.apiKey, model: args.model || 'grok-3-latest', maxTokens: args.maxTokens || 4096, temperature: args.temperature || 0.7, }); // šŸ”„ SAVE TO PERSISTENT STORAGE await this.saveConfiguration(config); this.xaiClient = new XAIClient(config); // Test the connection const isConnected = await this.xaiClient.testConnection(); if (isConnected) { return { content: [ { type: 'text', text: 'šŸ”„ xAI configuration saved and tested successfully! Ready to CRUSH some searches! šŸ’Ŗ', }, ], }; } else { return { content: [ { type: 'text', text: 'āš ļø Configuration saved but connection test failed. Check your API key.', }, ], }; } } catch (error) { throw new Error(`Configuration failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } async saveConfiguration(config) { try { const fs = require('fs'); const path = require('path'); const os = require('os'); // Create .env content const envContent = `# xAI Live Search MCP Configuration # Generated on ${new Date().toISOString()} # Your xAI API Key XAI_API_KEY=${config.apiKey} # Configuration XAI_MODEL=${config.model} XAI_MAX_TOKENS=${config.maxTokens} XAI_TEMPERATURE=${config.temperature} XAI_BASE_URL=https://api.x.ai # MCP Server Configuration MCP_SERVER_NAME=xai-live-search MCP_SERVER_VERSION=1.0.9 `; // Priority order for saving - try the most accessible locations first const savePaths = [ // Project directory (highest priority) path.join(process.cwd(), '.env'), // Global MCP directory path.join(os.homedir(), '.local/share/claude-mcp-servers/.env'), // Fallback global locations path.join(os.homedir(), '.xai/.env'), path.join(os.homedir(), '.config/xai/.env'), ]; let saved = false; for (const envPath of savePaths) { try { // Ensure directory exists const dir = path.dirname(envPath); if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } // Write configuration fs.writeFileSync(envPath, envContent, 'utf8'); console.error(`šŸ”„ Configuration saved to: ${envPath}`); saved = true; break; } catch (error) { console.error(`āš ļø Failed to save to ${envPath}:`, error); continue; } } if (!saved) { throw new Error('Failed to save configuration to any location'); } } catch (error) { console.error('āŒ Failed to save configuration:', error); throw error; } } async handleTestConnection() { console.error('šŸ”§ DEBUG: handleTestConnection called'); try { if (!this.xaiClient) { console.error('šŸ”§ DEBUG: xaiClient is null/undefined - RETURNING ERROR AS SUCCESS'); return { content: [ { type: 'text', text: 'šŸ”§ DEBUG: xAI client not configured. Use xai_configure tool first. This is an error returned as success to bypass Claude Code bug #1067.', }, ], }; } console.error('šŸ”§ DEBUG: Calling xaiClient.testConnection()'); const isConnected = await this.xaiClient.testConnection(); console.error(`šŸ”§ DEBUG: testConnection result: ${isConnected}`); return { content: [ { type: 'text', text: isConnected ? 'āœ… xAI connection successful! Ready to search! šŸš€' : 'āŒ xAI connection failed. Check your API key and network.', }, ], }; } catch (error) { console.error('šŸ”§ DEBUG: handleTestConnection error - RETURNING AS SUCCESS:', error); return { content: [ { type: 'text', text: `šŸ”§ DEBUG ERROR (Claude Code Bug #1067): ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], }; } } async handleLiveSearch(args) { if (!this.xaiClient) { throw new Error('xAI client not configured. Use xai_configure tool first.'); } const params = LiveSearchParamsSchema.parse(args); const result = await this.xaiClient.liveSearch(params); let responseText = `šŸ” **Live Search Results for:** "${params.query}"\n\n`; responseText += `šŸ¤– **AI Response:**\n${result.response}\n\n`; if (result.citations.length > 0) { responseText += `šŸ“š **Citations:**\n`; responseText += result.citations.map((citation, index) => { return `${index + 1}. ${citation}`; }).join('\n'); responseText += '\n\n'; } responseText += `šŸ“Š **Usage:** ${result.usage.total_tokens} tokens (${result.usage.prompt_tokens} prompt + ${result.usage.completion_tokens} completion)`; responseText += '\n\nšŸš€ **Search completed successfully!**'; return { content: [ { type: 'text', text: responseText, }, ], }; } async handleChatWithSearch(args) { if (!this.xaiClient) { throw new Error('xAI client not configured. Use xai_configure tool first.'); } const params = ChatWithSearchParamsSchema.parse(args); const result = await this.xaiClient.chatWithSearch(params); let responseText = `šŸ¤– **AI Response:**\n${result.response}`; if (result.citations.length > 0 && params.returnCitations) { responseText += '\n\nšŸ“š **Citations:**\n'; responseText += result.citations.map((citation, index) => { return `${index + 1}. ${citation}`; }).join('\n'); } responseText += `\n\nšŸ“Š **Usage:** ${result.usage.total_tokens} tokens (${result.usage.prompt_tokens} prompt + ${result.usage.completion_tokens} completion)`; return { content: [ { type: 'text', text: responseText, }, ], }; } setupErrorHandling() { this.server.onerror = (error) => { console.error('[MCP Error]', error); }; process.on('SIGINT', async () => { await this.server.close(); process.exit(0); }); } async run() { const transport = new StdioServerTransport(); await this.server.connect(transport); // šŸ’Ŗ BEAST MODE messaging console.error('šŸ”„ CRACKED-JACKED xAI Live Search MCP Server ACTIVATED! šŸš€'); console.error('šŸ’Ŗ Ready to CRUSH information gaps with real-time search!'); } } // Check for setup command if (process.argv.includes('--setup') || process.argv.includes('setup')) { console.error('šŸ”„ Launching xAI Live Search MCP Setup Wizard...'); import('./setup-wizard.js').then(module => { return module.runSetupWizard(); }).catch(error => { console.error('Failed to launch setup wizard:', error); process.exit(1); }); } else { // šŸš€ Launch the BEAST! const server = new XAILiveSearchServer(); server.run().catch((error) => { console.error('šŸ’„ Server startup failed:', error); process.exit(1); }); } //# sourceMappingURL=index.js.map