run_agent
Delegate complex multi-step tasks to specialized autonomous agents for independent execution with dedicated context, maintaining conversation continuity across sessions.
Instructions
Delegate complex, multi-step, or specialized tasks to an autonomous agent for independent execution with dedicated context (e.g., refactoring across multiple files, fixing all test failures, systematic codebase analysis, batch operations). Returns session_id in response metadata - reuse it in subsequent calls to maintain conversation context continuity across multiple agent executions.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| agent | Yes | Agent name exactly as listed in list_agents resource. | |
| prompt | Yes | User's direct request content. Agent context is separately provided via agent parameter. | |
| cwd | Yes | Working directory path for agent execution context. Must be an absolute path to a valid directory. | |
| extra_args | No | Additional configuration parameters for agent execution (optional) | |
| session_id | No | Session ID for continuing previous conversation context (optional). If omitted, a new session will be auto-generated and returned in response metadata. Reuse the returned session_id in subsequent calls to maintain context continuity. |
Implementation Reference
- src/tools/RunAgentTool.ts:166-369 (handler)The `execute` method is the main handler that performs parameter validation, agent lookup, execution via AgentExecutor, session management, and formats the MCP-compliant response. It handles errors gracefully and provides structured logging.async execute(params: unknown): Promise<McpToolResponse> { const startTime = Date.now() const requestId = this.generateRequestId() this.logger.info('Run agent tool execution started', { requestId, timestamp: new Date().toISOString(), }) // Best-effort cleanup: old session files (ADR-0002) // Non-blocking execution - does not affect main processing flow if (this.sessionManager) { Promise.resolve() .then(() => this.sessionManager!.cleanupOldSessions()) .catch((error) => { this.logger.warn('Session cleanup failed (best-effort)', { requestId, error: error instanceof Error ? error.message : String(error), }) }) } try { // Validate parameters with enhanced validation const validatedParams = this.validateParams(params) // Auto-generate session_id if not provided and SessionManager is available const sessionId = validatedParams.session_id || (this.sessionManager ? randomUUID() : undefined) this.logger.debug('Parameters validated successfully', { requestId, agent: validatedParams.agent, promptLength: validatedParams.prompt.length, cwd: validatedParams.cwd, extraArgsCount: validatedParams.extra_args?.length || 0, sessionId: sessionId, sessionIdGenerated: !validatedParams.session_id && !!sessionId, }) // Check if agent exists and load agent definition once let agentDefinition: AgentDefinition | undefined if (this.agentManager) { agentDefinition = await this.agentManager.getAgent(validatedParams.agent) if (!agentDefinition) { this.logger.warn('Agent not found', { requestId, requestedAgent: validatedParams.agent, }) return this.createErrorResponse( `Agent '${validatedParams.agent}' not found`, await this.getAvailableAgentsList() ) } this.logger.debug('Agent found and validated', { requestId, agentName: agentDefinition.name, agentDescription: agentDefinition.description, }) } // Execute agent if executor is available if (this.agentExecutor) { // Report progress: Starting agent execution // Use agent definition content if available (already loaded above) const agentContext = agentDefinition?.content ?? validatedParams.agent // Load session history if session_id is provided and SessionManager is available let promptWithHistory = validatedParams.prompt if (sessionId && this.sessionManager) { try { // CRITICAL: Pass agent_type to enforce sub-agent isolation const sessionData = await this.sessionManager.loadSession( sessionId, validatedParams.agent ) if (sessionData && sessionData.history.length > 0) { // Convert session history to Markdown format for token efficiency and LLM comprehension const historyMarkdown = formatSessionHistory(sessionData) promptWithHistory = `Previous conversation history:\n\n${historyMarkdown}\n\n---\n\nCurrent request:\n${validatedParams.prompt}` this.logger.info('Session history loaded and merged', { requestId, sessionId: sessionId, historyEntries: sessionData.history.length, }) } else { this.logger.debug('No session history found', { requestId, sessionId: sessionId, }) } } catch (error) { // Log error but continue - session loading failure should not break main flow this.logger.warn('Failed to load session history', { requestId, sessionId: sessionId, error: error instanceof Error ? error.message : String(error), }) } } const executionParams: ExecutionParams = { agent: agentContext, prompt: promptWithHistory, ...(validatedParams.cwd !== undefined && { cwd: validatedParams.cwd }), ...(validatedParams.extra_args !== undefined && { extra_args: validatedParams.extra_args, }), } // Report progress: Executing agent // Execute agent (this has its own timeout: MCP -> AI) const result = await this.agentExecutor.executeAgent(executionParams) // Report progress: Execution completed // Update execution statistics this.updateExecutionStats(validatedParams.agent, result.executionTime) this.logger.info('Agent execution completed successfully', { requestId, agent: validatedParams.agent, exitCode: result.exitCode, executionTime: result.executionTime, totalTime: Date.now() - startTime, }) // Save session if session_id is available and SessionManager is available if (sessionId && this.sessionManager) { try { // Build request object with only defined properties const sessionRequest: { agent: string prompt: string cwd?: string extra_args?: string[] } = { agent: validatedParams.agent, prompt: validatedParams.prompt, } if (validatedParams.cwd !== undefined) { sessionRequest.cwd = validatedParams.cwd } if (validatedParams.extra_args !== undefined) { sessionRequest.extra_args = validatedParams.extra_args } await this.sessionManager.saveSession(sessionId, sessionRequest, { stdout: result.stdout, stderr: result.stderr, exitCode: result.exitCode, executionTime: result.executionTime, }) this.logger.info('Session saved successfully', { requestId, sessionId: sessionId, }) } catch (error) { // Log error but continue - session save failure should not break main flow this.logger.warn('Failed to save session', { requestId, sessionId: sessionId, error: error instanceof Error ? error.message : String(error), }) } } // Mark MCP request as completed return this.formatExecutionResponse(result, validatedParams.agent, requestId, sessionId) } // Fallback response if executor is not available this.logger.warn('Agent executor not available', { requestId }) return { content: [ { type: 'text', text: `Agent execution request received for '${validatedParams.agent}' with prompt: "${validatedParams.prompt}"\n\nNote: Agent executor not initialized.`, }, ], } } catch (error) { const totalTime = Date.now() - startTime this.logger.error('Agent execution failed', error instanceof Error ? error : undefined, { requestId, totalTime, errorType: error instanceof Error ? error.constructor.name : 'Unknown', }) return this.createErrorResponse( `Agent execution failed: ${error instanceof Error ? error.message : 'Unknown error'}`, null ) } }
- src/tools/RunAgentTool.ts:118-147 (schema)Defines the input schema for the run_agent tool, specifying required parameters (agent, prompt, cwd) and optional ones (extra_args, session_id) with descriptions.public readonly inputSchema: RunAgentInputSchema = { type: 'object', properties: { agent: { type: 'string', description: 'Agent name exactly as listed in list_agents resource.', }, prompt: { type: 'string', description: "User's direct request content. Agent context is separately provided via agent parameter.", }, cwd: { type: 'string', description: 'Working directory path for agent execution context. Must be an absolute path to a valid directory.', }, extra_args: { type: 'array', items: { type: 'string' }, description: 'Additional configuration parameters for agent execution (optional)', }, session_id: { type: 'string', description: 'Session ID for continuing previous conversation context (optional). If omitted, a new session will be auto-generated and returned in response metadata. Reuse the returned session_id in subsequent calls to maintain context continuity.', }, }, required: ['agent', 'prompt', 'cwd'], }
- src/server/McpServer.ts:197-202 (registration)Registers the run_agent tool in the MCP server's list_tools handler by including its name, description, and inputSchema in the returned tools list.tools: [ { name: this.runAgentTool.name, description: this.runAgentTool.description, inputSchema: this.runAgentTool.inputSchema, },
- src/server/McpServer.ts:230-240 (registration)Dispatches calls to the run_agent tool in the MCP server's call_tool handler by checking the tool name and invoking the RunAgentTool's execute method.if (params.name === 'run_agent') { const result = await this.runAgentTool.execute(params.arguments) this.log('info', 'Tool execution completed', { tool: params.name, responseTime: Date.now() - startTime, success: true, }) return result as CallToolResult }
- src/server/McpServer.ts:105-105 (registration)Instantiates the RunAgentTool instance in the McpServer constructor, injecting required dependencies for agent execution and management.this.runAgentTool = new RunAgentTool(this.agentExecutor, this.agentManager, this.sessionManager)