connectry-architect-mcp 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -183,6 +183,33 @@ function loadOrCreateUserConfig() {
183
183
  // src/tools/submit-answer.ts
184
184
  import { z } from "zod";
185
185
 
186
+ // src/tools/elicit.ts
187
+ async function elicitSingleSelect(mcpServer, message, fieldName, options) {
188
+ try {
189
+ const result = await mcpServer.server.elicitInput({
190
+ mode: "form",
191
+ message,
192
+ requestedSchema: {
193
+ type: "object",
194
+ properties: {
195
+ [fieldName]: {
196
+ type: "string",
197
+ title: fieldName,
198
+ oneOf: options.map((o) => ({ const: o.value, title: o.title }))
199
+ }
200
+ },
201
+ required: [fieldName]
202
+ }
203
+ });
204
+ if (result.action === "accept" && result.content) {
205
+ return result.content[fieldName];
206
+ }
207
+ return null;
208
+ } catch {
209
+ return null;
210
+ }
211
+ }
212
+
186
213
  // src/engine/grading.ts
187
214
  function gradeAnswer(question, userAnswer) {
188
215
  const normalizedAnswer = userAnswer.toUpperCase();
@@ -396,6 +423,17 @@ function registerSubmitAnswer(server2, db2, userConfig2) {
396
423
  { key: "handout", label: "Show me the handout" },
397
424
  { key: "project", label: "Show me in the reference project" }
398
425
  ];
426
+ const elicitOptions = followUpOptions.map((opt) => ({
427
+ value: opt.key,
428
+ title: opt.label
429
+ }));
430
+ const elicitMessage = result.isCorrect ? "Nice work! What would you like to do next?" : "What would you like to do next?";
431
+ const selectedFollowUp = await elicitSingleSelect(
432
+ server2,
433
+ elicitMessage,
434
+ "followUp",
435
+ elicitOptions
436
+ );
399
437
  const response = {
400
438
  questionId: result.questionId,
401
439
  isCorrect: result.isCorrect,
@@ -403,7 +441,8 @@ function registerSubmitAnswer(server2, db2, userConfig2) {
403
441
  explanation: result.explanation,
404
442
  whyYourAnswerWasWrong: result.whyUserWasWrong,
405
443
  references: result.references,
406
- followUpOptions
444
+ followUpOptions,
445
+ ...selectedFollowUp != null ? { selectedFollowUp } : {}
407
446
  };
408
447
  return {
409
448
  content: [{ type: "text", text: JSON.stringify(response, null, 2) }]
@@ -561,6 +600,23 @@ function findUnansweredForTaskStatement(questions, taskStatement, answeredIds) {
561
600
  }
562
601
 
563
602
  // src/tools/get-practice-question.ts
603
+ var OPTION_KEYS = ["A", "B", "C", "D"];
604
+ function formatQuestionText(question) {
605
+ const lines = [
606
+ `**Question ${question.id}** (Domain ${question.domainId} | ${question.difficulty})`,
607
+ "",
608
+ `**Task:** ${question.taskStatement}`,
609
+ "",
610
+ "---",
611
+ "",
612
+ question.scenario,
613
+ "",
614
+ `**${question.text}**`,
615
+ "",
616
+ ...OPTION_KEYS.map((key) => ` **${key}.** ${question.options[key]}`)
617
+ ];
618
+ return lines.join("\n");
619
+ }
564
620
  function registerGetPracticeQuestion(server2, db2, userConfig2) {
565
621
  server2.tool(
566
622
  "get_practice_question",
@@ -585,16 +641,39 @@ function registerGetPracticeQuestion(server2, db2, userConfig2) {
585
641
  content: [{ type: "text", text: "No more questions available for the selected criteria. Try a different domain or difficulty." }]
586
642
  };
587
643
  }
588
- const response = {
589
- questionId: question.id,
590
- taskStatement: question.taskStatement,
591
- domainId: question.domainId,
592
- difficulty: question.difficulty,
593
- scenario: question.scenario,
594
- text: question.text,
595
- options: question.options
596
- };
597
- return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }] };
644
+ const questionText = formatQuestionText(question);
645
+ const elicitOptions = OPTION_KEYS.map((key) => ({
646
+ value: key,
647
+ title: `${key}. ${question.options[key]}`
648
+ }));
649
+ const selectedAnswer = await elicitSingleSelect(
650
+ server2,
651
+ questionText,
652
+ "answer",
653
+ elicitOptions
654
+ );
655
+ if (selectedAnswer) {
656
+ const responseText = [
657
+ questionText,
658
+ "",
659
+ "---",
660
+ "",
661
+ `**Selected answer: ${selectedAnswer}**`,
662
+ "",
663
+ `Use submit_answer with questionId "${question.id}" and answer "${selectedAnswer}" to grade this response.`
664
+ ].join("\n");
665
+ return { content: [{ type: "text", text: responseText }] };
666
+ }
667
+ const fallbackText = [
668
+ questionText,
669
+ "",
670
+ "---",
671
+ "",
672
+ `Question ID: ${question.id}`,
673
+ "",
674
+ "Use submit_answer with the question ID and your chosen answer (A, B, C, or D) to check your response."
675
+ ].join("\n");
676
+ return { content: [{ type: "text", text: fallbackText }] };
598
677
  }
599
678
  );
600
679
  }
@@ -1044,38 +1123,48 @@ function registerStartPracticeExam(server2, db2, userConfig2) {
1044
1123
  return ` D${domainId}: ${title} \u2014 ${count} questions (${domainScores[`d${domainId}`].weight}%)`;
1045
1124
  });
1046
1125
  const firstQuestion = examQuestions[0];
1126
+ const text = [
1127
+ "\u2550\u2550\u2550 PRACTICE EXAM STARTED \u2550\u2550\u2550",
1128
+ "",
1129
+ "Simulating the Claude Certified Architect \u2014 Foundations exam.",
1130
+ "",
1131
+ `Exam ID: ${examId}`,
1132
+ "Total Questions: 60",
1133
+ "Passing Score: 720/1000",
1134
+ "",
1135
+ "Question Distribution:",
1136
+ ...distribution,
1137
+ "",
1138
+ "\u2500\u2500\u2500 Question 1 of 60 \u2500\u2500\u2500",
1139
+ "",
1140
+ `Domain: D${firstQuestion.domainId}`,
1141
+ `Task: ${firstQuestion.taskStatement}`,
1142
+ `Difficulty: ${firstQuestion.difficulty}`,
1143
+ "",
1144
+ `Scenario: ${firstQuestion.scenario}`,
1145
+ "",
1146
+ firstQuestion.text,
1147
+ "",
1148
+ `A) ${firstQuestion.options.A}`,
1149
+ `B) ${firstQuestion.options.B}`,
1150
+ `C) ${firstQuestion.options.C}`,
1151
+ `D) ${firstQuestion.options.D}`,
1152
+ "",
1153
+ `[Submit your answer using submit_exam_answer with examId: ${examId} and questionId: "${firstQuestion.id}"]`
1154
+ ].join("\n");
1155
+ const selected = await elicitSingleSelect(server2, "Select your answer:", "answer", [
1156
+ { value: "A", title: `A) ${firstQuestion.options.A}` },
1157
+ { value: "B", title: `B) ${firstQuestion.options.B}` },
1158
+ { value: "C", title: `C) ${firstQuestion.options.C}` },
1159
+ { value: "D", title: `D) ${firstQuestion.options.D}` }
1160
+ ]);
1161
+ const responseText = selected !== null ? `${text}
1162
+
1163
+ User selected: ${selected}` : text;
1047
1164
  return {
1048
1165
  content: [{
1049
1166
  type: "text",
1050
- text: [
1051
- "\u2550\u2550\u2550 PRACTICE EXAM STARTED \u2550\u2550\u2550",
1052
- "",
1053
- "Simulating the Claude Certified Architect \u2014 Foundations exam.",
1054
- "",
1055
- `Exam ID: ${examId}`,
1056
- "Total Questions: 60",
1057
- "Passing Score: 720/1000",
1058
- "",
1059
- "Question Distribution:",
1060
- ...distribution,
1061
- "",
1062
- "\u2500\u2500\u2500 Question 1 of 60 \u2500\u2500\u2500",
1063
- "",
1064
- `Domain: D${firstQuestion.domainId}`,
1065
- `Task: ${firstQuestion.taskStatement}`,
1066
- `Difficulty: ${firstQuestion.difficulty}`,
1067
- "",
1068
- `Scenario: ${firstQuestion.scenario}`,
1069
- "",
1070
- firstQuestion.text,
1071
- "",
1072
- `A) ${firstQuestion.options.A}`,
1073
- `B) ${firstQuestion.options.B}`,
1074
- `C) ${firstQuestion.options.C}`,
1075
- `D) ${firstQuestion.options.D}`,
1076
- "",
1077
- `[Submit your answer using submit_exam_answer with examId: ${examId} and questionId: "${firstQuestion.id}"]`
1078
- ].join("\n")
1167
+ text: responseText
1079
1168
  }]
1080
1169
  };
1081
1170
  }
@@ -1193,6 +1282,15 @@ function registerSubmitExamAnswer(server2, db2, userConfig2) {
1193
1282
  lines.push(`B) ${nextQuestion.options.B}`);
1194
1283
  lines.push(`C) ${nextQuestion.options.C}`);
1195
1284
  lines.push(`D) ${nextQuestion.options.D}`);
1285
+ const selected = await elicitSingleSelect(server2, "Select your answer:", "answer", [
1286
+ { value: "A", title: `A) ${nextQuestion.options.A}` },
1287
+ { value: "B", title: `B) ${nextQuestion.options.B}` },
1288
+ { value: "C", title: `C) ${nextQuestion.options.C}` },
1289
+ { value: "D", title: `D) ${nextQuestion.options.D}` }
1290
+ ]);
1291
+ if (selected !== null) {
1292
+ lines.push("", `User selected: ${selected}`);
1293
+ }
1196
1294
  }
1197
1295
  }
1198
1296
  }
@@ -2214,6 +2312,16 @@ function handleAbandon(db2, userId) {
2214
2312
  buildId: build.id
2215
2313
  });
2216
2314
  }
2315
+ function appendSelectedAction(response, selected) {
2316
+ if (!selected) return response;
2317
+ const existingText = response.content[0].text;
2318
+ return {
2319
+ ...response,
2320
+ content: [{ type: "text", text: `${existingText}
2321
+
2322
+ User selected next action: ${selected}` }]
2323
+ };
2324
+ }
2217
2325
  function registerCapstoneBuildStep(server2, db2, userConfig2) {
2218
2326
  server2.tool(
2219
2327
  "capstone_build_step",
@@ -2225,8 +2333,17 @@ function registerCapstoneBuildStep(server2, db2, userConfig2) {
2225
2333
  const userId = userConfig2.userId;
2226
2334
  ensureUser(db2, userId);
2227
2335
  switch (action) {
2228
- case "confirm":
2229
- return handleConfirm(db2, userId);
2336
+ case "confirm": {
2337
+ const result = handleConfirm(db2, userId);
2338
+ if ("isError" in result) return result;
2339
+ const selected = await elicitSingleSelect(
2340
+ server2,
2341
+ "Build confirmed! What would you like to do next?",
2342
+ "nextAction",
2343
+ [{ value: "quiz", title: "Start the first quiz" }]
2344
+ );
2345
+ return appendSelectedAction(result, selected);
2346
+ }
2230
2347
  case "quiz":
2231
2348
  return handleQuiz(db2, userId);
2232
2349
  case "build": {
@@ -2237,10 +2354,33 @@ function registerCapstoneBuildStep(server2, db2, userConfig2) {
2237
2354
  if (build.status !== "building") {
2238
2355
  return errorResponse(`Build must be in "building" status. Current status: "${build.status}".`);
2239
2356
  }
2240
- return handleBuild(db2, userId, build);
2357
+ const result = handleBuild(db2, userId, build);
2358
+ if ("isError" in result) return result;
2359
+ const selected = await elicitSingleSelect(
2360
+ server2,
2361
+ "Build instructions generated. What would you like to do next?",
2362
+ "nextAction",
2363
+ [
2364
+ { value: "next", title: "Advance to the next step" },
2365
+ { value: "status", title: "Check build progress" }
2366
+ ]
2367
+ );
2368
+ return appendSelectedAction(result, selected);
2369
+ }
2370
+ case "next": {
2371
+ const result = handleNext(db2, userId);
2372
+ if ("isError" in result) return result;
2373
+ const selected = await elicitSingleSelect(
2374
+ server2,
2375
+ "Step advanced. What would you like to do next?",
2376
+ "nextAction",
2377
+ [
2378
+ { value: "quiz", title: "Start this step's quiz" },
2379
+ { value: "status", title: "Check build progress" }
2380
+ ]
2381
+ );
2382
+ return appendSelectedAction(result, selected);
2241
2383
  }
2242
- case "next":
2243
- return handleNext(db2, userId);
2244
2384
  case "status":
2245
2385
  return handleStatus(db2, userId);
2246
2386
  case "abandon":