xai-live-search-mcp
Version:
š„ xAI Live Search integration for Claude Code via MCP - BEAST MODE!
470 lines (467 loc) ⢠20.5 kB
JavaScript
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