/**
* Choose Solution Tool - Select a solution and return its questions
*/
import { z } from 'zod';
import { ErrorHandler, ErrorCategory, ErrorSeverity } from '../core/error-handling';
import { DotAI } from '../core/index';
import { Logger } from '../core/error-handling';
import { GenericSessionManager } from '../core/generic-session-manager';
import type { SolutionData } from './recommend';
// Tool metadata for direct MCP registration
export const CHOOSESOLUTION_TOOL_NAME = 'chooseSolution';
export const CHOOSESOLUTION_TOOL_DESCRIPTION = 'Select a solution by ID and return its questions for configuration';
// Zod schema for MCP registration
export const CHOOSESOLUTION_TOOL_INPUT_SCHEMA = {
solutionId: z.string().regex(/^sol-\d+-[a-f0-9]{8}$/).describe('The solution ID to choose (e.g., sol-1762983784617-9ddae2b8)')
};
// Session management now handled by GenericSessionManager
/**
* Direct MCP tool handler for chooseSolution functionality
*/
export async function handleChooseSolutionTool(
args: { solutionId: string },
dotAI: DotAI,
logger: Logger,
requestId: string
): Promise<{ content: { type: 'text'; text: string }[] }> {
return await ErrorHandler.withErrorHandling(
async () => {
logger.debug('Handling chooseSolution request', { requestId, solutionId: args?.solutionId });
// Input validation is handled automatically by MCP SDK with Zod schema
// args are already validated and typed when we reach this point
// Initialize session manager
const sessionManager = new GenericSessionManager<SolutionData>('sol');
logger.debug('Session manager initialized', { requestId });
// Load solution session
const session = sessionManager.getSession(args.solutionId);
if (!session) {
throw ErrorHandler.createError(
ErrorCategory.VALIDATION,
ErrorSeverity.HIGH,
`Solution not found: ${args.solutionId}`,
{
operation: 'solution_loading',
component: 'ChooseSolutionTool',
requestId,
input: { solutionId: args.solutionId },
suggestedActions: [
'Verify the solution ID is correct',
'Ensure the solution was created by the recommend tool',
'Check that the session has not expired'
]
}
);
}
const solution = session.data;
// For Helm solutions, generate questions if not already present
if (solution.type === 'helm' && solution.chart) {
const hasQuestions = (solution.questions?.required?.length ?? 0) > 0 ||
(solution.questions?.basic?.length ?? 0) > 0 ||
(solution.questions?.advanced?.length ?? 0) > 0;
if (!hasQuestions) {
logger.info('Generating questions for Helm solution', {
requestId,
solutionId: args.solutionId,
chart: solution.chart.chartName
});
const questions = await dotAI.schema.generateQuestionsForHelmChart(
solution.intent,
solution.chart,
solution.description,
`choose_solution_${args.solutionId}`
);
solution.questions = questions;
// Update the session with generated questions
sessionManager.updateSession(args.solutionId, solution);
logger.info('Helm questions generated and saved', {
requestId,
solutionId: args.solutionId,
questionCounts: {
required: questions.required?.length || 0,
basic: questions.basic?.length || 0,
advanced: questions.advanced?.length || 0
}
});
}
}
logger.debug('Solution file loaded successfully', {
solutionId: args.solutionId,
type: solution.type,
hasQuestions: !!solution.questions,
questionCategories: {
required: solution.questions?.required?.length || 0,
basic: solution.questions?.basic?.length || 0,
advanced: solution.questions?.advanced?.length || 0,
hasOpen: !!solution.questions?.open
}
});
// Prepare response with solution details and questions
const response = {
status: 'stage_questions',
solutionId: args.solutionId,
currentStage: 'required',
questions: solution.questions.required || [],
nextStage: 'basic',
message: 'Please provide the required configuration for your application.',
nextAction: 'Call recommend tool with stage: answerQuestion:required',
guidance: 'Present ALL required questions to the user. All must be answered before proceeding.',
agentInstructions: `MANDATORY: Present ALL questions from this stage to the user - do not skip any.
For each question, show the default value if one exists and ask user to confirm or change it.
Wait for explicit user response on EVERY question before calling answerQuestion.
NEVER auto-fill answers without user confirmation, even if you can deduce values.
NEVER assume what the user wants - always ask.`,
timestamp: new Date().toISOString()
};
logger.info('Choose solution completed successfully', {
solutionId: args.solutionId,
questionCategories: {
required: solution.questions.required?.length || 0,
basic: solution.questions.basic?.length || 0,
advanced: solution.questions.advanced?.length || 0,
hasOpen: !!solution.questions.open
},
totalQuestions: (solution.questions.required?.length || 0) +
(solution.questions.basic?.length || 0) +
(solution.questions.advanced?.length || 0) +
(solution.questions.open ? 1 : 0)
});
return {
content: [{
type: 'text' as const,
text: JSON.stringify(response, null, 2)
}]
};
},
{
operation: 'choose_solution',
component: 'ChooseSolutionTool',
requestId,
input: args
}
);
}