UNPKG

mcp-quiz-server

Version:

🧠 AI-Powered Quiz Management via Model Context Protocol (MCP) - Create, manage, and take quizzes directly from VS Code, Claude, and other AI agents.

268 lines (267 loc) 11.7 kB
"use strict"; /** * @moduleName: Get Quiz Tool - MCP Tool Implementation * @ve // Get quiz from database with questions const { getDataSource } = await import('../../../database/config'); const { Quiz } = await import('../../../database/entities/Quiz'); const Question = await import('../../../database/entities/Question');n: 2.0.0 * @since: 2025-07-25 * @lastUpdated: 2025-07-25 * @projectSummary: Enhanced MCP Quiz Server - Modular Architecture * @techStack: TypeScript, JSON-RPC 2.0, MCP Protocol, TypeORM * @dependency: typeorm * @interModuleDependency: ../../types/mcp-types, ./create-quiz * @requirementsTraceability: * {@link Requirements.REQ_API_001} (Quiz CRUD Operations) * {@link Requirements.REQ_MCP_002} (Dynamic Tool Registry) * {@link Requirements.REQ_MCP_004} (Quiz Management Tools) * @testCoverage: Tested in test-all-tools.js, test-complete-workflow.js, enhanced-mcp.test.js * @testType: integration, e2e * @testFramework: custom MCP protocol testing * @briefDescription: Focused MCP tool for retrieving specific quiz data with optional question inclusion * @methods: execute, validateInput, formatResponse, findQuizById * @contributors: GitHub Copilot * @examples: * - const tool = new GetQuizTool(); await tool.execute({quizId: "123"}, context); * - MCP call: {"method": "tools/call", "params": {"name": "get_quiz", "arguments": {"quizId": "123"}}} * @vulnerabilitiesAssessment: UUID validation, data sanitization, access control ready */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.GetQuizTool = void 0; class GetQuizTool { constructor() { this.definition = { name: 'get_quiz', description: 'Retrieve a specific quiz by ID with optional question details', inputSchema: { type: 'object', properties: { quizId: { type: 'string', description: 'Unique identifier of the quiz to retrieve', pattern: '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$', }, includeQuestions: { type: 'boolean', description: 'Whether to include full question details', default: true, }, includeMetadata: { type: 'boolean', description: 'Whether to include quiz metadata and analytics', default: true, }, }, required: ['quizId'], }, }; } async execute(args, context) { try { // Validate input first this.validateInput(args); console.log('🔍 Retrieving quiz:', args.quizId); // Get quiz from database with questions const { getSafeDataSource, initializeDatabase } = await Promise.resolve().then(() => __importStar(require('../../../database/config'))); const { Quiz } = await Promise.resolve().then(() => __importStar(require('../../../database/entities/Quiz'))); const Question = await Promise.resolve().then(() => __importStar(require('../../../database/entities/Question'))); let dataSource = getSafeDataSource(); if (!dataSource) { // Initialize database if not available dataSource = await initializeDatabase(); } const quizRepository = dataSource.getRepository(Quiz); const quiz = await quizRepository.findOne({ where: { id: args.quizId }, relations: ['questions'], }); if (!quiz) { return { content: [ { type: 'text', text: JSON.stringify({ success: false, error: 'Quiz not found', message: `❌ Quiz with ID ${args.quizId} does not exist`, }, null, 2), }, ], isError: true, }; } console.log('✅ Quiz retrieved successfully:', quiz.title); return this.formatResponse(quiz); } catch (error) { console.error('❌ Quiz retrieval failed:', error); return { content: [ { type: 'text', text: `❌ Quiz retrieval failed: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], isError: true, }; } } validateInput(args) { var _a; if (!args || !((_a = args.quizId) === null || _a === void 0 ? void 0 : _a.trim())) { throw new Error('Quiz ID is required'); } // Validate UUID format const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; if (!uuidRegex.test(args.quizId)) { throw new Error('Invalid quiz ID format. Must be a valid UUID.'); } } async findQuizById(quizId, options) { // TODO: Replace with actual database query // const quiz = await context.dataSource // .getRepository(Quiz) // .findOne({ // where: { id: quizId }, // relations: options.includeQuestions ? ['questions'] : [], // }); // Mock data for now const mockQuizzes = { 'quiz-1': { id: 'quiz-1', title: 'JavaScript Fundamentals', description: 'Test your JavaScript knowledge with these fundamental questions', questionsCount: 3, createdAt: '2025-07-20T10:00:00Z', updatedAt: '2025-07-20T10:00:00Z', metadata: { category: 'programming', difficulty: 'easy', tags: ['javascript', 'basics', 'programming'], estimatedTime: 15, }, questions: options.includeQuestions ? [ { id: 'q1', questionNumber: 1, question: 'What is the correct way to declare a variable in JavaScript?', options: ['var x = 5;', 'variable x = 5;', 'v x = 5;', 'declare x = 5;'], correctAnswer: 'var x = 5;', explanation: 'In JavaScript, variables are declared using var, let, or const keywords.', }, { id: 'q2', questionNumber: 2, question: 'Which method is used to add an element to the end of an array?', options: ['push()', 'pop()', 'shift()', 'unshift()'], correctAnswer: 'push()', explanation: 'The push() method adds one or more elements to the end of an array.', }, { id: 'q3', questionNumber: 3, question: 'What does === operator do in JavaScript?', options: ['Assignment', 'Strict equality', 'Loose equality', 'Not equal'], correctAnswer: 'Strict equality', explanation: 'The === operator checks for strict equality without type coercion.', }, ] : undefined, }, 'quiz-2': { id: 'quiz-2', title: 'Advanced TypeScript', description: 'Advanced TypeScript concepts and patterns', questionsCount: 5, createdAt: '2025-07-22T14:30:00Z', updatedAt: '2025-07-22T14:30:00Z', metadata: { category: 'programming', difficulty: 'hard', tags: ['typescript', 'advanced', 'types'], estimatedTime: 30, }, questions: options.includeQuestions ? [ { id: 'q1-ts', questionNumber: 1, question: 'What is a generic in TypeScript?', options: [ 'A type that can work with any data type', 'A specific data type', 'A function parameter', 'A class method', ], correctAnswer: 'A type that can work with any data type', explanation: 'Generics provide a way to make components work with any data type.', }, ] : undefined, }, }; return mockQuizzes[quizId] || null; } formatResponse(quiz) { const responseData = { success: true, message: `🎯 Quiz "${quiz.title}" retrieved successfully`, data: { quiz: { ...quiz, // Remove questions from summary if they weren't requested ...(quiz.questions ? {} : { questions: undefined }), }, }, }; return { content: [ { type: 'text', text: JSON.stringify(responseData, null, 2), }, ], _meta: { toolName: 'get_quiz', executionTime: Date.now(), quizId: quiz.id, includeQuestions: !!quiz.questions, }, }; } } exports.GetQuizTool = GetQuizTool;