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.

218 lines (217 loc) 9.28 kB
"use strict"; /** * @moduleName: Create Quiz Tool - MCP Tool Implementation * @version: 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, UUID * @dependency: typeorm, uuid * @interModuleDependency: ../../types/mcp-types, ../../security/input-validator, ../../utils/database-manager * @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: Extensively tested in test-all-tools.js, test-debug-create.js, test-complete-workflow.js * @testType: integration, e2e * @testFramework: custom MCP protocol testing * @briefDescription: Focused MCP tool for creating new quizzes with database persistence and validation * @methods: execute, validateInput, formatResponse * @contributors: GitHub Copilot * @examples: * - const tool = new CreateQuizTool(); await tool.execute(args, context); * - MCP call: {"method": "tools/call", "params": {"name": "create_quiz", "arguments": {...}}} * @vulnerabilitiesAssessment: Input validation, SQL injection prevention, UUID security */ Object.defineProperty(exports, "__esModule", { value: true }); exports.CreateQuizTool = void 0; const uuid_1 = require("uuid"); // Import database entities and configuration const config_1 = require("../../../database/config"); const Question_1 = require("../../../database/entities/Question"); const Quiz_1 = require("../../../database/entities/Quiz"); class CreateQuizTool { constructor() { this.definition = { name: 'create_quiz', description: 'Create a new quiz with database persistence and unique ID generation', inputSchema: { type: 'object', properties: { title: { type: 'string', description: 'The title of the quiz', minLength: 1, maxLength: 200, }, description: { type: 'string', description: 'Optional quiz description', maxLength: 1000, }, questions: { type: 'array', description: 'Array of quiz questions', minItems: 1, maxItems: 100, items: { type: 'object', properties: { question: { type: 'string', minLength: 1, maxLength: 500 }, options: { type: 'array', items: { type: 'string', maxLength: 200 }, minItems: 2, maxItems: 10, }, answer: { type: 'string', minLength: 1, maxLength: 200 }, explanation: { type: 'string', maxLength: 1000 }, }, required: ['question', 'options', 'answer'], }, }, metadata: { type: 'object', properties: { category: { type: 'string', maxLength: 100 }, difficulty: { type: 'string', enum: ['easy', 'medium', 'hard'] }, tags: { type: 'array', items: { type: 'string', maxLength: 50 }, maxItems: 10, }, }, }, }, required: ['title', 'questions'], }, }; } async execute(args, context) { // Validate input arguments first - let validation errors bubble up this.validateInput(args); console.log('🔨 Creating new quiz:', args.title); try { // Generate unique quiz ID const quizId = (0, uuid_1.v4)(); const now = new Date(); // Create quiz entity (simplified for now - will use proper entities later) const quizData = { id: quizId, title: args.title, description: args.description || '', questions: args.questions.map((q, index) => ({ id: (0, uuid_1.v4)(), questionNumber: index + 1, question: q.question, options: q.options, correctAnswer: q.answer, explanation: q.explanation || '', })), metadata: args.metadata || {}, createdAt: now, updatedAt: now, }; // Save to database using TypeORM let dataSource = (0, config_1.getSafeDataSource)(); if (!dataSource) { // Initialize database if not available dataSource = await (0, config_1.initializeDatabase)(); } const quizRepository = dataSource.getRepository(Quiz_1.Quiz); const questionRepository = dataSource.getRepository(Question_1.Question); // Create quiz entity const quiz = quizRepository.create({ id: quizId, title: args.title, description: args.description || '', category: args.category || 'general', difficulty: args.difficulty || 'medium', timeLimit: args.timeLimit || 0, metadata: args.metadata || {}, createdAt: now, updatedAt: now, }); // Save quiz first const savedQuiz = await quizRepository.save(quiz); // Create and save questions const questionEntities = args.questions.map((q, index) => { return questionRepository.create({ quizId: savedQuiz.id, question: q.question, options: q.options, answer: q.answer, explanation: q.explanation || '', order: index + 1, }); }); await questionRepository.save(questionEntities); console.log('✅ Quiz created successfully:', quizId); return this.formatResponse({ quizId, questionsCount: args.questions.length, createdAt: now.toISOString(), title: args.title, }); } catch (error) { console.error('❌ Quiz creation failed:', error); return { content: [ { type: 'text', text: `❌ Quiz creation failed: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], isError: true, }; } } validateInput(args) { var _a; if (!args) { throw new Error('Arguments are required'); } if (!((_a = args.title) === null || _a === void 0 ? void 0 : _a.trim())) { throw new Error('Quiz title is required and cannot be empty'); } if (!Array.isArray(args.questions) || args.questions.length === 0) { throw new Error('At least one question is required'); } // Validate each question args.questions.forEach((question, index) => { var _a, _b; if (!((_a = question.question) === null || _a === void 0 ? void 0 : _a.trim())) { throw new Error(`Question ${index + 1}: Question text is required`); } if (!Array.isArray(question.options) || question.options.length < 2) { throw new Error(`Question ${index + 1}: At least 2 options are required`); } if (!((_b = question.answer) === null || _b === void 0 ? void 0 : _b.trim())) { throw new Error(`Question ${index + 1}: Answer is required`); } if (!question.options.includes(question.answer)) { throw new Error(`Question ${index + 1}: Answer must be one of the provided options`); } }); } formatResponse(result) { return { content: [ { type: 'text', text: JSON.stringify({ success: true, message: '🎉 Quiz created successfully!', data: result, }, null, 2), }, ], _meta: { toolName: 'create_quiz', executionTime: Date.now(), }, }; } } exports.CreateQuizTool = CreateQuizTool;