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.

116 lines (104 loc) • 4.13 kB
/** * @moduleName: Quiz API Service - Frontend HTTP Client * @version: 2.0.0 * @since: 2025-07-21 * @lastUpdated: 2025-07-27 * @projectSummary: Enhanced MCP Quiz Server - Frontend service for REST API communication * @techStack: TypeScript, Fetch API, Singleton Pattern, REST Client * @dependency: Quiz, QuizResult interfaces from types/index.js, backend REST API * @interModuleDependency: Backend API endpoints (/quiz/*, /results/*), error handling * @requirementsTraceability: * {@link Requirements.REQ_API_001} (HTTP Endpoints) * {@link Requirements.REQ_INTEGRATION_001} (Frontend-Backend Communication) * @briefDescription: Singleton service managing HTTP communication with backend quiz API endpoints * @methods: getAllQuizzes, getQuiz, submitResult, createQuiz * @contributors: GitHub Copilot, Backend Integration Team * @examples: * - const quizzes = await QuizService.getInstance().getAllQuizzes() * - await quizService.submitResult(quizId, answers) * - const quiz = await quizService.getQuiz('quiz-123') * @vulnerabilitiesAssessment: HTTP-only client, CSRF protection via SameSite cookies, input validation on server */ import { Quiz, QuizResult } from '../types/index'; export class QuizService { private static instance: QuizService; static getInstance(): QuizService { if (!QuizService.instance) { QuizService.instance = new QuizService(); } return QuizService.instance; } async getAllQuizzes(): Promise<Quiz[]> { try { const response = await fetch('/quiz/all'); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); // Handle both legacy (array) and new (object with quizzes property) response formats if (Array.isArray(data)) { return data; } else if (data.quizzes && Array.isArray(data.quizzes)) { return data.quizzes; } else { console.error('Unexpected API response format:', data); throw new Error('Invalid response format from server'); } } catch (error) { console.error('Failed to fetch quizzes:', error); throw new Error('Failed to load quizzes'); } } async getQuizById(id: string): Promise<Quiz> { try { const response = await fetch(`/quiz/${id}`); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); // Handle both legacy (direct quiz object) and new (object with quiz property) response formats if (data.quiz) { return data.quiz; } else if (data.id) { // Direct quiz object return data; } else { console.error('Unexpected API response format for quiz:', data); throw new Error('Invalid response format from server'); } } catch (error) { console.error(`Failed to fetch quiz ${id}:`, error); throw new Error('Failed to load quiz'); } } async submitQuiz(quizId: string, answers: Record<string, string>): Promise<QuizResult> { try { const response = await fetch('/quiz/submit', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ quizId, answers }), }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return await response.json(); } catch (error) { console.error('Failed to submit quiz:', error); throw new Error('Failed to submit quiz'); } } async searchQuizzes(term: string): Promise<Quiz[]> { try { const response = await fetch(`/quiz/search/${encodeURIComponent(term)}`); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return await response.json(); } catch (error) { console.error('Failed to search quizzes:', error); throw new Error('Failed to search quizzes'); } } }