Skip to main content
Glama

DeepResearch MCP

by ameeralns
research.ts5.77 kB
import { ResearchState, SearchResult, ResearchReport } from '../types/index.js'; // import { searchTavily } from './tavily.js'; import { searchWeb } from './firecrawl.js'; import { analyzeResearch, generateReport } from './openai.js'; // Store all research sessions const researchSessions = new Map<string, ResearchState>(); /** * Initialize a new research session * * @param sessionId Unique session identifier * @param query Research query * @param depth Maximum research depth * @returns New research state */ export function initializeResearch( sessionId: string, query: string, depth: number = 3 ): ResearchState { const researchState: ResearchState = { query, depth, currentDepth: 0, topics: [], findings: [], nextSearchTopic: query, // Initial search topic is the query itself shouldContinue: true }; researchSessions.set(sessionId, researchState); return researchState; } /** * Get research state for a session * * @param sessionId Session identifier * @returns Research state or null if not found */ export function getResearchState(sessionId: string): ResearchState | null { return researchSessions.get(sessionId) || null; } /** * Execute the next step in the research process * * @param sessionId Session identifier * @returns Updated research state */ export async function executeResearchStep(sessionId: string): Promise<ResearchState> { const researchState = researchSessions.get(sessionId); if (!researchState) { throw new Error(`No research session found with ID: ${sessionId}`); } if (researchState.currentDepth >= researchState.depth) { // Max depth reached - research is complete return researchState; } try { // Determine search topic for this step const currentSearchTopic = researchState.nextSearchTopic || researchState.query; // Add current topic to the list of searched topics researchState.topics.push(currentSearchTopic); console.error(`[Research] Searching for: "${currentSearchTopic}"`); // Search for information on the current topic let searchResult; try { searchResult = await searchWeb(currentSearchTopic); } catch (searchError) { console.error(`[Research] Search error: ${searchError instanceof Error ? searchError.message : String(searchError)}`); // Create a fallback search result to indicate the error searchResult = { query: currentSearchTopic, results: [{ title: 'Search Error', url: 'https://error.example.com', content: `An error occurred while searching: ${searchError instanceof Error ? searchError.message : String(searchError)}. The search will continue with the next topic.`, score: 0 }] }; } // Format search results into a detailed finding const finding = formatSearchResults(searchResult); researchState.findings.push(finding); // Always analyze findings to determine next steps, regardless of current shouldContinue value const analysis = await analyzeResearch( researchState.query, researchState.findings, researchState.topics ); // Update research state with next topic and continue flag researchState.nextSearchTopic = analysis.nextSearchTopic; researchState.shouldContinue = analysis.shouldContinue; // Always increment the depth counter researchState.currentDepth++; // Update the session researchSessions.set(sessionId, researchState); return researchState; } catch (error) { console.error('Error executing research step:', error); // Update the research state to continue despite errors if (researchState) { // Increment depth to make progress even with errors researchState.currentDepth++; // If we don't have a next search topic, set shouldContinue to false to end the process if (!researchState.nextSearchTopic) { researchState.shouldContinue = false; } // Update the session researchSessions.set(sessionId, researchState); } throw new Error(`Failed to execute research step: ${error instanceof Error ? error.message : String(error)}`); } } /** * Generate a final research report * * @param sessionId Session identifier * @returns Research report */ export async function generateResearchReport(sessionId: string): Promise<ResearchReport> { const researchState = researchSessions.get(sessionId); if (!researchState) { throw new Error(`No research session found with ID: ${sessionId}`); } const report = await generateReport(researchState.query, researchState.findings); return { query: researchState.query, findings: researchState.findings, topics: researchState.topics, report }; } /** * Format search results into a readable finding * * @param searchResult Search results * @returns Formatted finding text */ function formatSearchResults(searchResult: SearchResult): string { let formattedResult = `# Search Results for: ${searchResult.query}\n\n`; searchResult.results.forEach((result, index) => { const resultNumber = index + 1; formattedResult += `## Source [${resultNumber}]: ${result.title}\n`; formattedResult += `URL: ${result.url}\n`; formattedResult += `Citation: [${resultNumber}] ${result.url}\n\n`; formattedResult += `### Content from Source [${resultNumber}]:\n${result.content}\n\n`; }); // Add a clear and standardized source section for easy citation formattedResult += `# Source URLs for Citation\n\n`; searchResult.results.forEach((result, index) => { formattedResult += `[${index + 1}] ${result.url} - ${result.title}\n`; }); return formattedResult; }

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/ameeralns/DeepResearchMCP'

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