start_assessment
Begin the Connectry Architect certification assessment to test knowledge through scenario-based questions with immediate feedback and progress tracking.
Instructions
Start the initial assessment. Returns ONE question at a time (15 total, 3 per domain).
IMPORTANT — follow this flow for EVERY question:
Check if "isNewDomain" is true. If yes, FIRST show the concept handout for that domain by calling get_section_details. Tell the user: "Let's learn about [domain] before testing your knowledge." After showing the handout, proceed to step 2.
Present the question to the user using AskUserQuestion:
header: "Q[number]"
question: Include the FULL scenario text AND question text from the response
options: Use the 4 answer options (A/B/C/D) with label as the letter and description as the option text
If the scenario contains code, add a "preview" field on each option showing the relevant code snippet so the user can reference it while choosing
After user selects, call submit_answer with questionId and their answer.
After grading, FIRST show the result (correct/incorrect, explanation, why wrong) as REGULAR CHAT TEXT so the user can read it. THEN present follow-up options using AskUserQuestion. The explanation must NOT be hidden behind the card.
Call start_assessment again for the next question.
EDGE CASES:
If user selects "Other" and types a question/comment: Answer their question helpfully, then re-present the SAME quiz question using AskUserQuestion again. Never lose the current question.
If user clicks "Skip": Treat it as moving to the next question. Call start_assessment again immediately. The skipped question remains unanswered and will appear again later.
NEVER let Other or Skip break the assessment flow. Always continue to the next question or re-ask the current one.
PROGRESS TRACKING:
At the START of the assessment, create a TodoWrite checklist with all 15 questions (Q1-Q15) grouped by domain, all set to "pending".
After each answer, update the corresponding todo item to "completed" (with correct/incorrect note).
This gives the user a visual progress tracker throughout the assessment.
When assessment is complete, present next steps using AskUserQuestion with header "Next step".
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- src/tools/start-assessment.ts:64-156 (handler)The actual handler implementation for the 'start_assessment' tool. It checks for the next unanswered question from the assessment, determines if it's the start of a new domain, and returns the question data.
async () => { const userId = userConfig.userId; ensureUser(db, userId); const questions = buildAssessmentQuestions(); if (questions.length === 0) { return { content: [{ type: 'text' as const, text: JSON.stringify({ error: 'No assessment questions available.' }) }], }; } const answeredIds = db.prepare( `SELECT questionId FROM answers WHERE userId = ? AND questionId IN (${questions.map(() => '?').join(',')})`, ).all(userId, ...questions.map(q => q.id)) as readonly { questionId: string }[]; const answeredSet = new Set(answeredIds.map(r => r.questionId)); const nextQuestion = questions.find(q => !answeredSet.has(q.id)); // All done — compute results if (!nextQuestion) { const results = db.prepare( `SELECT domainId, COUNT(*) as total, SUM(CASE WHEN isCorrect THEN 1 ELSE 0 END) as correct FROM answers WHERE userId = ? AND questionId IN (${questions.map(() => '?').join(',')}) GROUP BY domainId`, ).all(userId, ...questions.map(q => q.id)) as readonly { domainId: number; total: number; correct: number }[]; const totalCorrect = results.reduce((sum, r) => sum + r.correct, 0); const totalQuestions = results.reduce((sum, r) => sum + r.total, 0); const overallAccuracy = totalQuestions > 0 ? Math.round((totalCorrect / totalQuestions) * 100) : 0; const path = overallAccuracy >= 60 ? 'exam-weighted' : 'beginner-friendly'; db.prepare('UPDATE users SET assessmentCompleted = TRUE, learningPath = ? WHERE id = ?').run(path, userId); const response = { status: 'complete', overall: { correct: totalCorrect, total: totalQuestions, accuracy: overallAccuracy }, learningPath: path === 'exam-weighted' ? 'Exam-Weighted' : 'Beginner-Friendly', domainResults: results.map(r => ({ domain: r.domainId, name: DOMAIN_NAMES[r.domainId] ?? '', correct: r.correct, total: r.total, accuracy: Math.round((r.correct / r.total) * 100), })), nextStepOptions: [ { label: 'Study Plan', description: 'Get personalized study recommendations based on your results' }, { label: 'Practice Questions', description: 'Start adaptive practice targeting your weak areas' }, { label: 'Capstone Build', description: 'Build your own project while learning all 30 task statements' }, { label: 'Reference Projects', description: 'Explore runnable code examples for each domain' }, ], instruction: 'Present nextStepOptions using AskUserQuestion with header "Next step".', }; return { content: [{ type: 'text' as const, text: JSON.stringify(response, null, 2) }], }; } // Determine if this is the first question for a new domain const questionIndex = answeredSet.size; const previousDomainIds = questions .filter(q => answeredSet.has(q.id)) .map(q => q.domainId); const isNewDomain = !previousDomainIds.includes(nextQuestion.domainId); const response = { status: 'question', questionNumber: questionIndex + 1, totalQuestions: questions.length, questionId: nextQuestion.id, domainId: nextQuestion.domainId, domainName: DOMAIN_NAMES[nextQuestion.domainId] ?? 'Unknown', difficulty: nextQuestion.difficulty, taskStatement: nextQuestion.taskStatement, isNewDomain, scenario: nextQuestion.scenario, questionText: nextQuestion.text, options: { A: nextQuestion.options.A, B: nextQuestion.options.B, C: nextQuestion.options.C, D: nextQuestion.options.D, }, instruction: isNewDomain ? `This is a NEW domain (${DOMAIN_NAMES[nextQuestion.domainId]}). Show the concept handout first using get_section_details for task "${nextQuestion.taskStatement}", then present this question using AskUserQuestion. Put the scenario + question in the "question" field. Use options with label "A"/"B"/"C"/"D" and description as the option text.` : `Present this question using AskUserQuestion with header "Q${questionIndex + 1}". Put the scenario + question text in the "question" field. Use options with label "A"/"B"/"C"/"D" and description as the option text.`, }; return { content: [{ type: 'text' as const, text: JSON.stringify(response, null, 2) }], }; } ); - src/tools/start-assessment.ts:31-157 (registration)The function registering the 'start_assessment' tool within the McpServer.
export function registerStartAssessment(server: McpServer, db: Database.Database, userConfig: UserConfig): void { server.tool( 'start_assessment', `Start the initial assessment. Returns ONE question at a time (15 total, 3 per domain). IMPORTANT — follow this flow for EVERY question: 1. Check if "isNewDomain" is true. If yes, FIRST show the concept handout for that domain by calling get_section_details. Tell the user: "Let's learn about [domain] before testing your knowledge." After showing the handout, proceed to step 2. 2. Present the question to the user using AskUserQuestion: - header: "Q[number]" - question: Include the FULL scenario text AND question text from the response - options: Use the 4 answer options (A/B/C/D) with label as the letter and description as the option text - If the scenario contains code, add a "preview" field on each option showing the relevant code snippet so the user can reference it while choosing 3. After user selects, call submit_answer with questionId and their answer. 4. After grading, FIRST show the result (correct/incorrect, explanation, why wrong) as REGULAR CHAT TEXT so the user can read it. THEN present follow-up options using AskUserQuestion. The explanation must NOT be hidden behind the card. 5. Call start_assessment again for the next question. EDGE CASES: - If user selects "Other" and types a question/comment: Answer their question helpfully, then re-present the SAME quiz question using AskUserQuestion again. Never lose the current question. - If user clicks "Skip": Treat it as moving to the next question. Call start_assessment again immediately. The skipped question remains unanswered and will appear again later. - NEVER let Other or Skip break the assessment flow. Always continue to the next question or re-ask the current one. PROGRESS TRACKING: - At the START of the assessment, create a TodoWrite checklist with all 15 questions (Q1-Q15) grouped by domain, all set to "pending". - After each answer, update the corresponding todo item to "completed" (with correct/incorrect note). - This gives the user a visual progress tracker throughout the assessment. When assessment is complete, present next steps using AskUserQuestion with header "Next step".`, {}, async () => { const userId = userConfig.userId; ensureUser(db, userId); const questions = buildAssessmentQuestions(); if (questions.length === 0) { return { content: [{ type: 'text' as const, text: JSON.stringify({ error: 'No assessment questions available.' }) }], }; } const answeredIds = db.prepare( `SELECT questionId FROM answers WHERE userId = ? AND questionId IN (${questions.map(() => '?').join(',')})`, ).all(userId, ...questions.map(q => q.id)) as readonly { questionId: string }[]; const answeredSet = new Set(answeredIds.map(r => r.questionId)); const nextQuestion = questions.find(q => !answeredSet.has(q.id)); // All done — compute results if (!nextQuestion) { const results = db.prepare( `SELECT domainId, COUNT(*) as total, SUM(CASE WHEN isCorrect THEN 1 ELSE 0 END) as correct FROM answers WHERE userId = ? AND questionId IN (${questions.map(() => '?').join(',')}) GROUP BY domainId`, ).all(userId, ...questions.map(q => q.id)) as readonly { domainId: number; total: number; correct: number }[]; const totalCorrect = results.reduce((sum, r) => sum + r.correct, 0); const totalQuestions = results.reduce((sum, r) => sum + r.total, 0); const overallAccuracy = totalQuestions > 0 ? Math.round((totalCorrect / totalQuestions) * 100) : 0; const path = overallAccuracy >= 60 ? 'exam-weighted' : 'beginner-friendly'; db.prepare('UPDATE users SET assessmentCompleted = TRUE, learningPath = ? WHERE id = ?').run(path, userId); const response = { status: 'complete', overall: { correct: totalCorrect, total: totalQuestions, accuracy: overallAccuracy }, learningPath: path === 'exam-weighted' ? 'Exam-Weighted' : 'Beginner-Friendly', domainResults: results.map(r => ({ domain: r.domainId, name: DOMAIN_NAMES[r.domainId] ?? '', correct: r.correct, total: r.total, accuracy: Math.round((r.correct / r.total) * 100), })), nextStepOptions: [ { label: 'Study Plan', description: 'Get personalized study recommendations based on your results' }, { label: 'Practice Questions', description: 'Start adaptive practice targeting your weak areas' }, { label: 'Capstone Build', description: 'Build your own project while learning all 30 task statements' }, { label: 'Reference Projects', description: 'Explore runnable code examples for each domain' }, ], instruction: 'Present nextStepOptions using AskUserQuestion with header "Next step".', }; return { content: [{ type: 'text' as const, text: JSON.stringify(response, null, 2) }], }; } // Determine if this is the first question for a new domain const questionIndex = answeredSet.size; const previousDomainIds = questions .filter(q => answeredSet.has(q.id)) .map(q => q.domainId); const isNewDomain = !previousDomainIds.includes(nextQuestion.domainId); const response = { status: 'question', questionNumber: questionIndex + 1, totalQuestions: questions.length, questionId: nextQuestion.id, domainId: nextQuestion.domainId, domainName: DOMAIN_NAMES[nextQuestion.domainId] ?? 'Unknown', difficulty: nextQuestion.difficulty, taskStatement: nextQuestion.taskStatement, isNewDomain, scenario: nextQuestion.scenario, questionText: nextQuestion.text, options: { A: nextQuestion.options.A, B: nextQuestion.options.B, C: nextQuestion.options.C, D: nextQuestion.options.D, }, instruction: isNewDomain ? `This is a NEW domain (${DOMAIN_NAMES[nextQuestion.domainId]}). Show the concept handout first using get_section_details for task "${nextQuestion.taskStatement}", then present this question using AskUserQuestion. Put the scenario + question in the "question" field. Use options with label "A"/"B"/"C"/"D" and description as the option text.` : `Present this question using AskUserQuestion with header "Q${questionIndex + 1}". Put the scenario + question text in the "question" field. Use options with label "A"/"B"/"C"/"D" and description as the option text.`, }; return { content: [{ type: 'text' as const, text: JSON.stringify(response, null, 2) }], }; } ); }