Skip to main content
Glama

WolframAlpha LLM MCP Server

by Garoth
wolfram-llm.ts12.8 kB
import axios from 'axios'; interface WolframLLMConfig { appId: string; } interface LLMQueryResult { success: boolean; error?: string; rawResponse?: unknown; result?: { query: string; interpretation?: string; result: string; sections: Array<{ title: string; content: string; }>; url?: string; getSectionByTitle(title: string): { title: string; content: string } | undefined; }; } export class WolframLLMService { private config: WolframLLMConfig; private baseUrl = 'https://www.wolframalpha.com/api/v1/llm-api'; constructor(config: WolframLLMConfig) { this.config = { appId: config.appId || process.env.WOLFRAM_LLM_APP_ID || '' }; } private parseQueryResponse(text: string): NonNullable<LLMQueryResult['result']> { const sections = text.split('\n\n'); const result: NonNullable<LLMQueryResult['result']> = { query: '', result: '', sections: [], getSectionByTitle(title: string) { return this.sections.find(s => s.title === title); } }; // Find Query section const querySection = sections.find(s => s.startsWith('Query:')); if (querySection) { result.query = JSON.parse(querySection.replace('Query:', '').trim()); } // Get all non-query sections const nonQuerySections = sections.filter(s => !s.startsWith('Query:')); // Simple approach: Just find the second "Assumption:" section and cut everything after it let processedSections = nonQuerySections; // Find the first assumption section const firstAssumptionIndex = nonQuerySections.findIndex(s => s.startsWith('Assumption:')); if (firstAssumptionIndex >= 0) { // Find the second assumption section const secondAssumptionIndex = nonQuerySections.findIndex((s, i) => i > firstAssumptionIndex && s.startsWith('Assumption:') ); // If we found a second assumption section, cut everything after it if (secondAssumptionIndex > 0) { // Keep only the content up to the second assumption processedSections = nonQuerySections.slice(0, secondAssumptionIndex); // Check if there's a URL section after the duplicate content const urlSection = nonQuerySections.find(s => s.startsWith('Wolfram|Alpha website result')); if (urlSection && !processedSections.includes(urlSection)) { processedSections.push(urlSection); } } } // Put the processed response (except Query) in result result.result = processedSections.join('\n\n').trim(); // Process sections let currentSection = ''; let currentContent: string[] = []; const processedSectionTitles = new Set<string>(); // Track section titles to avoid duplicates for (const section of processedSections) { if (section.startsWith('Wolfram|Alpha website result')) { const match = section.match(/https:\/\/.*?(?=\s|$)/); if (match) { result.url = match[0]; } } else if (section.trim()) { const lines = section.split('\n'); const firstLine = lines[0]; if (firstLine.includes(':')) { // If we have a previous section, save it if (currentSection && currentContent.length > 0 && !processedSectionTitles.has(currentSection)) { result.sections.push({ title: currentSection, content: currentContent.join('\n').trim() }); processedSectionTitles.add(currentSection); } // Start new section currentSection = firstLine.split(':')[0].trim(); // Add remaining content after the colon currentContent = [firstLine.split(':').slice(1).join(':').trim(), ...lines.slice(1)]; } else { // Continue adding to current section currentContent.push(...lines); } } } // Add the last section if exists if (currentSection && currentContent.length > 0 && !processedSectionTitles.has(currentSection)) { result.sections.push({ title: currentSection, content: currentContent.join('\n').trim() }); } // Ensure query is present if (!result.query) { throw new Error('Invalid response format: missing query'); } // If no result could be extracted, use error message if (!result.result) { throw new Error('Could not extract result from response'); } return result; } private parseSimplifiedResponse(text: string): NonNullable<LLMQueryResult['result']> { const sections = text.split('\n\n'); const result: NonNullable<LLMQueryResult['result']> = { query: '', result: '', sections: [], getSectionByTitle(title: string) { return this.sections.find(s => s.title === title); } }; // Find Query section const querySection = sections.find(s => s.startsWith('Query:')); if (querySection) { result.query = JSON.parse(querySection.replace('Query:', '').trim()); } const nonQuerySections = sections.filter(s => !s.startsWith('Query:')); // Simple approach: Just find the second "Assumption:" section and cut everything after it let processedSections = nonQuerySections; // Find the first assumption section const firstAssumptionIndex = nonQuerySections.findIndex(s => s.startsWith('Assumption:')); if (firstAssumptionIndex >= 0) { // Find the second assumption section const secondAssumptionIndex = nonQuerySections.findIndex((s, i) => i > firstAssumptionIndex && s.startsWith('Assumption:') ); // If we found a second assumption section, cut everything after it if (secondAssumptionIndex > 0) { // Keep only the content up to the second assumption processedSections = nonQuerySections.slice(0, secondAssumptionIndex); // Check if there's a URL section after the duplicate content const urlSection = nonQuerySections.find(s => s.startsWith('Wolfram|Alpha website result')); if (urlSection && !processedSections.includes(urlSection)) { processedSections.push(urlSection); } } } // Join all remaining sections and clean up const cleanText = processedSections.join('\n\n').trim(); result.result = cleanText; // Ensure query is present if (!result.query) { throw new Error('Invalid response format: missing query'); } // If no result could be extracted, use error message if (!result.result) { throw new Error('Could not extract result from response'); } return result; } /** * Query WolframAlpha's LLM API with a natural language query * Returns structured data optimized for LLM consumption */ async query(input: string): Promise<LLMQueryResult> { try { // Build query URL with parameters const params = new URLSearchParams({ appid: this.config.appId, input }); // Make request to LLM API const response = await axios.get(`${this.baseUrl}?${params.toString()}`); // Store raw response for error reporting const rawResponse = response.data; // Log raw response for debugging console.log('Raw API Response:', JSON.stringify(rawResponse, null, 2)); if (typeof rawResponse !== 'string') { console.error('Unexpected response format:', rawResponse); return { success: false, error: 'Invalid response format from WolframAlpha API', rawResponse }; } const result = this.parseQueryResponse(rawResponse); return { success: true, result }; } catch (error) { console.error('WolframAlpha LLM API Error:', error); // Get raw response if available let rawResponse: unknown; if (axios.isAxiosError(error) && error.response?.data) { rawResponse = error.response.data; console.error('Raw API Response:', rawResponse); } if (axios.isAxiosError(error) && error.response?.status === 501) { return { success: false, error: 'Input cannot be interpreted. Try rephrasing your query.', rawResponse }; } return { success: false, error: error instanceof Error ? error.message : 'Failed to query WolframAlpha LLM API', rawResponse }; } } /** * Get a simplified answer optimized for LLM context * Focuses on the most relevant information */ async getSimplifiedAnswer(input: string): Promise<LLMQueryResult> { try { const params = new URLSearchParams({ appid: this.config.appId, input, maxchars: '500' // Limit response length for simpler answers }); const response = await axios.get(`${this.baseUrl}?${params.toString()}`); // Store raw response for error reporting const rawResponse = response.data; console.log("Simplified Raw Response:", rawResponse); if (typeof rawResponse !== 'string') { console.error('Unexpected response format:', rawResponse); return { success: false, error: 'Invalid response format from WolframAlpha API', rawResponse }; } // Parse the simplified response const result = this.parseSimplifiedResponse(rawResponse); // We already have processed sections from parseSimplifiedResponse // No need to process them again, just use the sections from the result const processedSections = result.result.split('\n\n'); // Clear existing sections before processing result.sections = []; // Process sections let currentSection = ''; let currentContent: string[] = []; const processedSectionTitles = new Set<string>(); // Track section titles to avoid duplicates for (const section of processedSections) { if (section.startsWith('Wolfram|Alpha website result')) { const match = section.match(/https:\/\/.*?(?=\s|$)/); if (match) { result.url = match[0]; } } else if (section.trim()) { const lines = section.split('\n'); const firstLine = lines[0]; if (firstLine.includes(':')) { // If we have a previous section, save it if (currentSection && currentContent.length > 0 && !processedSectionTitles.has(currentSection)) { result.sections.push({ title: currentSection, content: currentContent.join('\n').trim() }); processedSectionTitles.add(currentSection); } // Start new section, preserving the full title currentSection = firstLine.split(':')[0].trim(); // Add remaining content after the colon currentContent = [firstLine.split(':').slice(1).join(':').trim(), ...lines.slice(1)]; } else { // Continue adding to current section currentContent.push(...lines); } } } // Add the last section if exists if (currentSection && currentContent.length > 0 && !processedSectionTitles.has(currentSection)) { result.sections.push({ title: currentSection, content: currentContent.join('\n').trim() }); } return { success: true, result }; } catch (error) { console.error('WolframAlpha LLM API Error:', error); // Get raw response if available let rawResponse: unknown; if (axios.isAxiosError(error) && error.response?.data) { rawResponse = error.response.data; console.error('Raw API Response:', rawResponse); } // Return the raw response in error cases return { success: false, error: error instanceof Error ? error.message : 'Failed to get simplified answer', rawResponse: error instanceof Error && axios.isAxiosError(error) ? error.response?.data : undefined }; } } /** * Validate API key by making a test query */ async validateApiKey(): Promise<boolean> { try { const params = new URLSearchParams({ appid: this.config.appId, input: '2+2' }); const response = await axios.get(`${this.baseUrl}?${params.toString()}`); return typeof response.data === 'string' && response.data.includes('Result:'); } catch (error) { if (axios.isAxiosError(error) && error.response?.data) { console.error('API Key Validation Error Response:', error.response.data); } return false; } } }

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/Garoth/wolframalpha-llm-mcp'

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