# Task 10 — Wire 4 tutor tools into `QuantumBridgeServer` > **Index:** [README](README.md). **Spec:** [design](../../specs/2026-04-29-quantum-tutor-design.md). ## Goal Register `get_lesson`, `check_exercise`, `explain_result`, `get_progress` as MCP tools. The wrapper: 1. Loads `UserProgress` via `ProgressStore` (sandboxed by env var). 2. Routes through `self.backend` (set up in Task 0). 3. Maps `protocol_error` to `McpError::invalid_params`; all other errors stay in the structured payload. 4. Persists `mark_solved` on `passed: true`. ## Prerequisites - Task 9 merged (all 4 response functions exist in `tutor_tools.rs`). ## Files - Modify: `src/tools/mod.rs` ## Steps - [ ] **Step 1: Add imports + parameter structs at the top of `src/tools/mod.rs`** ```rust use crate::progress::ProgressStore; use crate::tutor::CurriculumLoader; use crate::tools::tutor_tools; #[derive(Debug, Deserialize, JsonSchema)] pub struct GetLessonParams { /// Module number (1–7). pub module_id: u32, /// Lesson number within the module (optional, defaults to first lesson with pending exercises). pub lesson_id: Option, } #[derive(Debug, Deserialize, JsonSchema)] pub struct CheckExerciseParams { /// Exercise identifier, e.g. "1-1-a". pub exercise_id: String, /// OpenQASM 3.0 source of the circuit to verify. pub circuit: String, } #[derive(Debug, Deserialize, JsonSchema)] pub struct ExplainResultParams { /// OpenQASM 3.0 source of the circuit that was executed. pub circuit: String, /// Measurement counts from run_circuit (bitstring → count). pub counts: serde_json::Value, /// Optional statevector from run_circuit with return_statevector=true. pub statevector: Option, } ``` - [ ] **Step 2: Add helpers on `QuantumBridgeServer` (outside the `#[tool_router]` block)** ```rust impl QuantumBridgeServer { fn loader(&self) -> CurriculumLoader { CurriculumLoader::default() } fn store(&self) -> Result { let path = ProgressStore::default_path() .map_err(|e| McpError::internal_error(e.to_string(), None))?; Ok(ProgressStore::new(path)) } } ``` - [ ] **Step 3: Add the 4 tool methods inside `#[tool_router(server_handler)] impl QuantumBridgeServer`** ```rust #[tool(description = "Get a quantum-computing lesson with concept, example circuit, and the next pending exercise.")] async fn get_lesson( &self, Parameters(GetLessonParams { module_id, lesson_id }): Parameters, ) -> Result { let store = self.store()?; let progress = store.load(); let json = tutor_tools::get_lesson_response(&self.loader(), &progress, module_id, lesson_id); if let Some(err) = json.get("error").and_then(|v| v.as_str()).map(str::to_owned) { return Err(McpError::invalid_params(err, None)); } Ok(CallToolResult::success(vec![Content::text(json.to_string())])) } #[tool(description = "Verify a submitted OpenQASM 3.0 circuit against a curriculum exercise. Returns pass/fail with feedback.")] async fn check_exercise( &self, Parameters(CheckExerciseParams { exercise_id, circuit }): Parameters, ) -> Result { let store = self.store()?; let progress = store.load(); let mut json = tutor_tools::check_exercise_response( &self.loader(), self.backend.as_ref(), &progress, &exercise_id, &circuit, ); if let Some(err) = json.get("protocol_error").and_then(|v| v.as_str()).map(str::to_owned) { return Err(McpError::invalid_params(err, None)); } if json["passed"].as_bool().unwrap_or(false) { store .mark_solved(&exercise_id) .map_err(|e| McpError::internal_error(e.to_string(), None))?; json["progress_updated"] = serde_json::json!(true); } Ok(CallToolResult::success(vec![Content::text(json.to_string())])) } #[tool(description = "Analyze a circuit and its measurement results to produce structured pedagogical data (gate breakdown, key concept, outcome stats).")] async fn explain_result( &self, Parameters(ExplainResultParams { circuit, counts, statevector }): Parameters, ) -> Result { let json = tutor_tools::explain_result_response(&self.loader(), &circuit, &counts, statevector.as_ref()); if let Some(err) = json.get("error").and_then(|v| v.as_str()).map(str::to_owned) { return Err(McpError::invalid_params(err, None)); } Ok(CallToolResult::success(vec![Content::text(json.to_string())])) } #[tool(description = "Get the learner's progress through the curriculum (modules status, current lesson, percent complete).")] async fn get_progress(&self) -> Result { let store = self.store()?; let progress = store.load(); let json = tutor_tools::get_progress_response(&self.loader(), &progress); Ok(CallToolResult::success(vec![Content::text(json.to_string())])) } ``` - [ ] **Step 4: Build and run all unit tests** ```bash cargo build && cargo test --lib 2>&1 | grep -E "test result|FAILED" ``` Expected: every existing suite green; the 4 new methods compile and the existing tests are untouched. - [ ] **Step 5: Verify the MCP `tools/list` exposes 7 tools** ```bash echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"0.0.1"}}} {"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' | timeout 3 cargo run 2>/dev/null | tail -1 | python3 -c "import sys,json; d=json.load(sys.stdin); print(len(d['result']['tools']), 'tools')" ``` Expected: `7 tools`. - [ ] **Step 6: Commit** ```bash git add src/tools/mod.rs git commit -m "feat: register 4 tutor tools on QuantumBridgeServer" ```