Skip to main content
Glama

research

Streamline AI-powered research by integrating project context, task IDs, and file paths. Save results to tasks or files, and customize detail levels within the Task Master MCP server.

Instructions

Perform AI-powered research queries with project context

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
customContextNoAdditional custom context text to include in the research
detailLevelNoDetail level for the research response (default: medium)
filePathsNoComma-separated list of file paths for context (e.g., "src/api.js,docs/readme.md")
includeProjectTreeNoInclude project file tree structure in context (default: false)
projectRootYesThe directory of the project. Must be an absolute path.
queryYesResearch query/prompt (required)
saveToNoAutomatically save research results to specified task/subtask ID (e.g., "15" or "15.2")
saveToFileNoSave research results to .taskmaster/docs/research/ directory (default: false)
tagNoTag context to operate on
taskIdsNoComma-separated list of task/subtask IDs for context (e.g., "15,16.2,17")

Implementation Reference

  • Core handler function implementing the research tool logic: validates args, gathers project context (tasks/files/tree), calls performResearch AI function, handles auto-save to tasks/subtasks/files, returns detailed results with token usage and telemetry.
    export async function researchDirect(args, log, context = {}) { // Destructure expected args const { query, taskIds, filePaths, customContext, includeProjectTree = false, detailLevel = 'medium', saveTo, saveToFile = false, projectRoot, tag } = args; const { session } = context; // Destructure session from context // Enable silent mode to prevent console logs from interfering with JSON response enableSilentMode(); // Create logger wrapper using the utility const mcpLog = createLogWrapper(log); try { // Check required parameters if (!query || typeof query !== 'string' || query.trim().length === 0) { log.error('Missing or invalid required parameter: query'); disableSilentMode(); return { success: false, error: { code: 'MISSING_PARAMETER', message: 'The query parameter is required and must be a non-empty string' } }; } // Parse comma-separated task IDs if provided const parsedTaskIds = taskIds ? taskIds .split(',') .map((id) => id.trim()) .filter((id) => id.length > 0) : []; // Parse comma-separated file paths if provided const parsedFilePaths = filePaths ? filePaths .split(',') .map((path) => path.trim()) .filter((path) => path.length > 0) : []; // Validate detail level const validDetailLevels = ['low', 'medium', 'high']; if (!validDetailLevels.includes(detailLevel)) { log.error(`Invalid detail level: ${detailLevel}`); disableSilentMode(); return { success: false, error: { code: 'INVALID_PARAMETER', message: `Detail level must be one of: ${validDetailLevels.join(', ')}` } }; } log.info( `Performing research query: "${query.substring(0, 100)}${query.length > 100 ? '...' : ''}", ` + `taskIds: [${parsedTaskIds.join(', ')}], ` + `filePaths: [${parsedFilePaths.join(', ')}], ` + `detailLevel: ${detailLevel}, ` + `includeProjectTree: ${includeProjectTree}, ` + `projectRoot: ${projectRoot}` ); // Prepare options for the research function const researchOptions = { taskIds: parsedTaskIds, filePaths: parsedFilePaths, customContext: customContext || '', includeProjectTree, detailLevel, projectRoot, tag, saveToFile }; // Prepare context for the research function const researchContext = { session, mcpLog, commandName: 'research', outputType: 'mcp' }; // Call the performResearch function const result = await performResearch( query.trim(), researchOptions, researchContext, 'json', // outputFormat - use 'json' to suppress CLI UI false // allowFollowUp - disable for MCP calls ); // Auto-save to task/subtask if requested if (saveTo) { try { const isSubtask = saveTo.includes('.'); // Format research content for saving const researchContent = `## Research Query: ${query.trim()} **Detail Level:** ${result.detailLevel} **Context Size:** ${result.contextSize} characters **Timestamp:** ${new Date().toLocaleDateString()} ${new Date().toLocaleTimeString()} ### Results ${result.result}`; if (isSubtask) { // Save to subtask const { updateSubtaskById } = await import( '../../../../scripts/modules/task-manager/update-subtask-by-id.js' ); const tasksPath = path.join( projectRoot, '.taskmaster', 'tasks', 'tasks.json' ); await updateSubtaskById( tasksPath, saveTo, researchContent, false, // useResearch = false for simple append { session, mcpLog, commandName: 'research-save', outputType: 'mcp', projectRoot, tag }, 'json' ); log.info(`Research saved to subtask ${saveTo}`); } else { // Save to task const updateTaskById = ( await import( '../../../../scripts/modules/task-manager/update-task-by-id.js' ) ).default; const taskIdNum = parseInt(saveTo, 10); const tasksPath = path.join( projectRoot, '.taskmaster', 'tasks', 'tasks.json' ); await updateTaskById( tasksPath, taskIdNum, researchContent, false, // useResearch = false for simple append { session, mcpLog, commandName: 'research-save', outputType: 'mcp', projectRoot, tag }, 'json', true // appendMode = true ); log.info(`Research saved to task ${saveTo}`); } } catch (saveError) { log.warn(`Error saving research to task/subtask: ${saveError.message}`); } } // Restore normal logging disableSilentMode(); return { success: true, data: { query: result.query, result: result.result, contextSize: result.contextSize, contextTokens: result.contextTokens, tokenBreakdown: result.tokenBreakdown, systemPromptTokens: result.systemPromptTokens, userPromptTokens: result.userPromptTokens, totalInputTokens: result.totalInputTokens, detailLevel: result.detailLevel, telemetryData: result.telemetryData, tagInfo: result.tagInfo, savedFilePath: result.savedFilePath } }; } catch (error) { // Make sure to restore normal logging even if there's an error disableSilentMode(); log.error(`Error in researchDirect: ${error.message}`); return { success: false, error: { code: error.code || 'RESEARCH_ERROR', message: error.message } }; } }
  • Registers the 'research' MCP tool with the server: defines name, description, full Zod input schema, and execute handler that normalizes project root, resolves tag, delegates to researchDirect core function, and handles API results/errors.
    export function registerResearchTool(server) { server.addTool({ name: 'research', description: 'Perform AI-powered research queries with project context', parameters: z.object({ query: z.string().describe('Research query/prompt (required)'), taskIds: z .string() .optional() .describe( 'Comma-separated list of task/subtask IDs for context (e.g., "15,16.2,17")' ), filePaths: z .string() .optional() .describe( 'Comma-separated list of file paths for context (e.g., "src/api.js,docs/readme.md")' ), customContext: z .string() .optional() .describe('Additional custom context text to include in the research'), includeProjectTree: z .boolean() .optional() .describe( 'Include project file tree structure in context (default: false)' ), detailLevel: z .enum(['low', 'medium', 'high']) .optional() .describe('Detail level for the research response (default: medium)'), saveTo: z .string() .optional() .describe( 'Automatically save research results to specified task/subtask ID (e.g., "15" or "15.2")' ), saveToFile: z .boolean() .optional() .describe( 'Save research results to .taskmaster/docs/research/ directory (default: false)' ), projectRoot: z .string() .describe('The directory of the project. Must be an absolute path.'), tag: z.string().optional().describe('Tag context to operate on') }), execute: withNormalizedProjectRoot(async (args, { log, session }) => { try { const resolvedTag = resolveTag({ projectRoot: args.projectRoot, tag: args.tag }); log.info( `Starting research with query: "${args.query.substring(0, 100)}${args.query.length > 100 ? '...' : ''}"` ); // Call the direct function const result = await researchDirect( { query: args.query, taskIds: args.taskIds, filePaths: args.filePaths, customContext: args.customContext, includeProjectTree: args.includeProjectTree || false, detailLevel: args.detailLevel || 'medium', saveTo: args.saveTo, saveToFile: args.saveToFile || false, projectRoot: args.projectRoot, tag: resolvedTag }, log, { session } ); return handleApiResult({ result, log: log, errorPrefix: 'Error performing research', projectRoot: args.projectRoot }); } catch (error) { log.error(`Error in research tool: ${error.message}`); return createErrorResponse(error.message); } }) }); }
  • Zod schema defining all input parameters for the research tool, including query, context selectors (tasks/files/custom/tree), output options (detail/save), project/tag info.
    parameters: z.object({ query: z.string().describe('Research query/prompt (required)'), taskIds: z .string() .optional() .describe( 'Comma-separated list of task/subtask IDs for context (e.g., "15,16.2,17")' ), filePaths: z .string() .optional() .describe( 'Comma-separated list of file paths for context (e.g., "src/api.js,docs/readme.md")' ), customContext: z .string() .optional() .describe('Additional custom context text to include in the research'), includeProjectTree: z .boolean() .optional() .describe( 'Include project file tree structure in context (default: false)' ), detailLevel: z .enum(['low', 'medium', 'high']) .optional() .describe('Detail level for the research response (default: medium)'), saveTo: z .string() .optional() .describe( 'Automatically save research results to specified task/subtask ID (e.g., "15" or "15.2")' ), saveToFile: z .boolean() .optional() .describe( 'Save research results to .taskmaster/docs/research/ directory (default: false)' ), projectRoot: z .string() .describe('The directory of the project. Must be an absolute path.'), tag: z.string().optional().describe('Tag context to operate on') }),
  • Central tool registry maps the 'research' tool name to its registration function registerResearchTool, enabling dynamic tool registration by MCP server init code.
    research: registerResearchTool,
  • Central re-export module that imports and re-exports researchDirect from its direct-functions implementation, making it available to tool execute handlers.
    import { researchDirect } from './direct-functions/research.js'; import { scopeDownDirect } from './direct-functions/scope-down.js'; import { scopeUpDirect } from './direct-functions/scope-up.js'; import { setTaskStatusDirect } from './direct-functions/set-task-status.js'; import { updateSubtaskByIdDirect } from './direct-functions/update-subtask-by-id.js'; import { updateTaskByIdDirect } from './direct-functions/update-task-by-id.js'; import { updateTasksDirect } from './direct-functions/update-tasks.js'; import { useTagDirect } from './direct-functions/use-tag.js'; import { validateDependenciesDirect } from './direct-functions/validate-dependencies.js'; // Re-export utility functions export { findTasksPath } from './utils/path-utils.js'; // Use Map for potential future enhancements like introspection or dynamic dispatch export const directFunctions = new Map([ ['getCacheStatsDirect', getCacheStatsDirect], ['parsePRDDirect', parsePRDDirect], ['updateTasksDirect', updateTasksDirect], ['updateTaskByIdDirect', updateTaskByIdDirect], ['updateSubtaskByIdDirect', updateSubtaskByIdDirect], ['setTaskStatusDirect', setTaskStatusDirect], ['nextTaskDirect', nextTaskDirect], ['expandTaskDirect', expandTaskDirect], ['addTaskDirect', addTaskDirect], ['addSubtaskDirect', addSubtaskDirect], ['removeSubtaskDirect', removeSubtaskDirect], ['analyzeTaskComplexityDirect', analyzeTaskComplexityDirect], ['clearSubtasksDirect', clearSubtasksDirect], ['expandAllTasksDirect', expandAllTasksDirect], ['removeDependencyDirect', removeDependencyDirect], ['validateDependenciesDirect', validateDependenciesDirect], ['fixDependenciesDirect', fixDependenciesDirect], ['complexityReportDirect', complexityReportDirect], ['addDependencyDirect', addDependencyDirect], ['removeTaskDirect', removeTaskDirect], ['initializeProjectDirect', initializeProjectDirect], ['modelsDirect', modelsDirect], ['moveTaskDirect', moveTaskDirect], ['moveTaskCrossTagDirect', moveTaskCrossTagDirect], ['researchDirect', researchDirect], ['addTagDirect', addTagDirect], ['deleteTagDirect', deleteTagDirect], ['listTagsDirect', listTagsDirect], ['useTagDirect', useTagDirect], ['renameTagDirect', renameTagDirect], ['copyTagDirect', copyTagDirect], ['scopeUpDirect', scopeUpDirect], ['scopeDownDirect', scopeDownDirect] ]); // Re-export all direct function implementations export { getCacheStatsDirect, parsePRDDirect, updateTasksDirect, updateTaskByIdDirect, updateSubtaskByIdDirect, setTaskStatusDirect, nextTaskDirect, expandTaskDirect, addTaskDirect, addSubtaskDirect, removeSubtaskDirect, analyzeTaskComplexityDirect, clearSubtasksDirect, expandAllTasksDirect, removeDependencyDirect, validateDependenciesDirect, fixDependenciesDirect, complexityReportDirect, addDependencyDirect, removeTaskDirect, initializeProjectDirect, modelsDirect, moveTaskDirect, moveTaskCrossTagDirect, researchDirect,

Latest Blog Posts

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/eyaltoledano/claude-task-master'

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