follow_up
Use after submitting an answer to request follow-up actions including code examples, concept explanations, handouts, or reference projects.
Instructions
Handle post-answer follow-up actions. Use after submit_answer to explore concepts, code examples, handouts, or reference projects.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| questionId | Yes | The question ID from the previous answer | |
| action | Yes | The follow-up action to take |
Implementation Reference
- src/tools/follow-up.ts:34-182 (handler)Main handler: registers the 'follow_up' MCP tool. Accepts questionId and action (next, code_example, concept, handout, project, why_wrong). Dispatches to the appropriate logic: 'next' calls get_practice_question, 'code_example'/'concept'/'handout' extract sections from handouts, 'project' maps domain to a scaffold_project, 'why_wrong' returns incorrect answer explanations.
export function registerFollowUp(server: McpServer, _db: Database.Database, _userConfig: UserConfig): void { server.tool( 'follow_up', 'Handle post-answer follow-up actions. Use after submit_answer to explore concepts, code examples, handouts, or reference projects.', { questionId: z.string().describe('The question ID from the previous answer'), action: z.enum(FOLLOW_UP_ACTIONS).describe('The follow-up action to take'), }, async ({ questionId, action }) => { const question = findQuestion(questionId); if (!question) { return { content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Question not found', questionId }) }], isError: true, }; } switch (action) { case 'next': { return { content: [{ type: 'text' as const, text: JSON.stringify({ instruction: 'Call get_practice_question to get the next question.', taskStatement: question.taskStatement, domainId: question.domainId, }, null, 2), }], }; } case 'code_example': { const handout = loadHandout(question.taskStatement); if (!handout) { return { content: [{ type: 'text' as const, text: JSON.stringify({ error: 'No handout found for this task statement', taskStatement: question.taskStatement }) }], isError: true, }; } const codeExample = extractSection(handout, 'Code Example'); if (!codeExample) { return { content: [{ type: 'text' as const, text: JSON.stringify({ error: 'No Code Example section found in handout', taskStatement: question.taskStatement }) }], isError: true, }; } return { content: [{ type: 'text' as const, text: JSON.stringify({ taskStatement: question.taskStatement, codeExample, }, null, 2), }], }; } case 'concept': { const handout = loadHandout(question.taskStatement); if (!handout) { return { content: [{ type: 'text' as const, text: JSON.stringify({ error: 'No handout found for this task statement', taskStatement: question.taskStatement }) }], isError: true, }; } const concept = extractSection(handout, 'Concept'); if (!concept) { return { content: [{ type: 'text' as const, text: JSON.stringify({ error: 'No Concept section found in handout', taskStatement: question.taskStatement }) }], isError: true, }; } return { content: [{ type: 'text' as const, text: JSON.stringify({ taskStatement: question.taskStatement, concept, }, null, 2), }], }; } case 'handout': { const handout = loadHandout(question.taskStatement); if (!handout) { return { content: [{ type: 'text' as const, text: JSON.stringify({ error: 'No handout found for this task statement', taskStatement: question.taskStatement }) }], isError: true, }; } return { content: [{ type: 'text' as const, text: JSON.stringify({ taskStatement: question.taskStatement, handout, }, null, 2), }], }; } case 'project': { const projectId = DOMAIN_PROJECT_MAP[question.domainId] ?? null; if (!projectId) { return { content: [{ type: 'text' as const, text: JSON.stringify({ error: 'No reference project mapped for this domain', domainId: question.domainId }) }], isError: true, }; } return { content: [{ type: 'text' as const, text: JSON.stringify({ instruction: 'Call scaffold_project to explore the reference project for this domain.', projectId, domainId: question.domainId, }, null, 2), }], }; } case 'why_wrong': { const incorrectOptions = Object.entries(question.whyWrongMap) .filter(([key]) => key !== question.correctAnswer) .reduce<Record<string, string>>((acc, [key, value]) => { if (value) { return { ...acc, [key]: value }; } return acc; }, {}); return { content: [{ type: 'text' as const, text: JSON.stringify({ questionId: question.id, correctAnswer: question.correctAnswer, explanation: question.explanation, whyOthersAreWrong: incorrectOptions, }, null, 2), }], }; } } } ); } - src/tools/follow-up.ts:7-8 (schema)Defines the allowed follow-up actions as a const array: next, code_example, concept, handout, project, why_wrong. Used in the zod enum schema for the 'action' parameter.
const FOLLOW_UP_ACTIONS = ['next', 'code_example', 'concept', 'handout', 'project', 'why_wrong'] as const; - src/tools/follow-up.ts:9-15 (schema)Maps domain IDs (1-5) to reference project directory names used by the 'project' action.
const DOMAIN_PROJECT_MAP: Readonly<Record<number, string>> = { 1: 'd1-agentic', 2: 'd2-tools', 3: 'd3-config', 4: 'd4-prompts', 5: 'd5-context', } as const; - src/tools/index.ts:17-42 (registration)Imports and registers registerFollowUp in the central registerTools function, called at line 37.
import { registerFollowUp } from './follow-up.js'; import { registerStartCapstoneBuild } from './start-capstone-build.js'; import { registerCapstoneBuildStep } from './capstone-build-step.js'; import { registerCapstoneBuildStatus } from './capstone-build-status.js'; import { registerDashboard } from './dashboard.js'; export function registerTools(server: McpServer, db: Database.Database, userConfig: UserConfig): void { registerSubmitAnswer(server, db, userConfig); registerGetProgress(server, db, userConfig); registerGetCurriculum(server, db, userConfig); registerGetSectionDetails(server, db, userConfig); registerGetPracticeQuestion(server, db, userConfig); registerStartAssessment(server, db, userConfig); registerGetWeakAreas(server, db, userConfig); registerGetStudyPlan(server, db, userConfig); registerScaffoldProject(server, db, userConfig); registerResetProgress(server, db, userConfig); registerStartPracticeExam(server, db, userConfig); registerSubmitExamAnswer(server, db, userConfig); registerGetExamHistory(server, db, userConfig); registerFollowUp(server, db, userConfig); registerStartCapstoneBuild(server, db, userConfig); registerCapstoneBuildStep(server, db, userConfig); registerCapstoneBuildStatus(server, db, userConfig); registerDashboard(server, db, userConfig); } - src/types.ts:171-174 (helper)Defines the FollowUpOption interface with key and label fields, used by submit-answer.ts to construct follow-up options.
export interface FollowUpOption { readonly key: string; readonly label: string; }