execute-research-step
Execute targeted research steps on DeepResearch MCP to perform web searches, data analysis, and generate detailed reports on specified topics, supporting iterative and intelligent research workflows.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| sessionId | Yes |
Input Schema (JSON Schema)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"additionalProperties": false,
"properties": {
"sessionId": {
"type": "string"
}
},
"required": [
"sessionId"
],
"type": "object"
}
Implementation Reference
- src/services/research.ts:52-131 (handler)Core implementation of the executeResearchStep function that performs web search, formats results, analyzes findings with OpenAI, and updates the 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)}`); } }
- src/index.ts:97-158 (registration)MCP server.tool registration for 'execute-research-step', including input schema { sessionId: z.string() } and wrapper handler function that calls the core executeResearchStep and formats the response.server.tool( 'execute-research-step', { sessionId: z.string(), }, async ({ sessionId }) => { try { const updatedState = await executeResearchStep(sessionId); return { content: [{ type: 'text', text: JSON.stringify({ message: 'Research step executed', currentDepth: updatedState.currentDepth, maxDepth: updatedState.depth, lastTopic: updatedState.topics[updatedState.topics.length - 1], nextTopic: updatedState.nextSearchTopic, shouldContinue: updatedState.shouldContinue, state: updatedState }, null, 2) }] }; } catch (error) { console.error('Error executing research step:', error); // Get the current state, even if there was an error const currentState = getResearchState(sessionId); // If we have a valid state, return a properly formatted JSON response with the error if (currentState) { return { content: [{ type: 'text', text: JSON.stringify({ message: `Error: ${error instanceof Error ? error.message : String(error)}`, currentDepth: currentState.currentDepth, maxDepth: currentState.depth, lastTopic: currentState.topics.length > 0 ? currentState.topics[currentState.topics.length - 1] : currentState.query, nextTopic: currentState.nextSearchTopic, shouldContinue: false, // Stop research on error state: currentState, error: true }, null, 2) }] }; } // Fallback if we can't get the current state return { content: [{ type: 'text', text: JSON.stringify({ message: `Error executing research step: ${error instanceof Error ? error.message : String(error)}`, error: true }, null, 2) }], isError: true }; } } );
- src/index.ts:100-101 (schema)Zod input schema for the execute-research-step tool: requires a sessionId string.sessionId: z.string(), },
- src/services/research.ts:161-179 (helper)Helper function formatSearchResults used within executeResearchStep to format web search results into a markdown finding for analysis.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; }