Gauntlet-Incept MCP

  • src
/** * Gauntlet Incept MCP Server * * This server implements the Model Context Protocol (MCP) to allow Claude Desktop * to interact with the Gauntlet Incept educational content generation system. */ const { createServer } = require('@modelcontextprotocol/server'); const { Pool } = require('pg'); const dotenv = require('dotenv'); const questionService = require('./services/questionService'); const articleService = require('./services/articleService'); // Load environment variables dotenv.config(); // Initialize database connection to RDS PostgreSQL const pool = new Pool({ host: process.env.DB_HOST || 'alphacommoncrawl-core-reboot.cluster-caeuiwckzo1a.us-east-1.rds.amazonaws.com', port: process.env.DB_PORT || 5432, database: process.env.DB_NAME || 'core', user: process.env.DB_USER || 'postgres', password: process.env.DB_PASSWORD, // SSH tunnel configuration would need to be handled separately // This connection assumes direct access or SSH tunnel is already established }); // Create MCP server const server = createServer({ name: 'gauntlet-incept', description: 'Educational content generation system for K-8 students', version: '0.1.0', tools: [ // Question Tools { name: 'tagQuestion', description: 'Tag a question with subject, grade, standard, lesson, and difficulty', parameters: { type: 'object', properties: { question: { type: 'string', description: 'The question content to tag' } }, required: ['question'] }, handler: async ({ question }) => { try { const result = await questionService.tagQuestion(question); return { success: true, data: result }; } catch (error) { return { success: false, error: error.message }; } } }, { name: 'gradeQuestion', description: 'Grade a tagged question against quality standards', parameters: { type: 'object', properties: { question: { type: 'string', description: 'The question content to grade' }, tags: { type: 'object', description: 'The tags for the question', properties: { subject: { type: 'string' }, grade: { type: 'string' }, standard: { type: 'string' }, lesson: { type: 'string' }, difficulty: { type: 'integer' } }, required: ['subject', 'grade', 'standard', 'lesson', 'difficulty'] } }, required: ['question', 'tags'] }, handler: async ({ question, tags }) => { try { const result = await questionService.gradeQuestion(question, tags); return { success: true, data: result }; } catch (error) { return { success: false, error: error.message }; } } }, { name: 'generateQuestion', description: 'Generate a question based on tags or an example question', parameters: { type: 'object', properties: { tags: { type: 'object', description: 'The tags for the question to generate', properties: { subject: { type: 'string' }, grade: { type: 'string' }, standard: { type: 'string' }, lesson: { type: 'string' }, difficulty: { type: 'integer' } } }, exampleQuestion: { type: 'string', description: 'An example question to generate a variant of' } }, oneOf: [ { required: ['tags'] }, { required: ['exampleQuestion'] } ] }, handler: async ({ tags, exampleQuestion }) => { try { const result = await questionService.generateQuestion(tags, exampleQuestion); return { success: true, data: result }; } catch (error) { return { success: false, error: error.message }; } } }, // Article Tools { name: 'tagArticle', description: 'Tag an article with subject, grade, standard, and lesson', parameters: { type: 'object', properties: { article: { type: 'string', description: 'The article content to tag' } }, required: ['article'] }, handler: async ({ article }) => { try { const result = await articleService.tagArticle(article); return { success: true, data: result }; } catch (error) { return { success: false, error: error.message }; } } }, { name: 'gradeArticle', description: 'Grade a tagged article against quality standards', parameters: { type: 'object', properties: { article: { type: 'string', description: 'The article content to grade' }, tags: { type: 'object', description: 'The tags for the article', properties: { subject: { type: 'string' }, grade: { type: 'string' }, standard: { type: 'string' }, lesson: { type: 'string' } }, required: ['subject', 'grade', 'standard', 'lesson'] } }, required: ['article', 'tags'] }, handler: async ({ article, tags }) => { try { const result = await articleService.gradeArticle(article, tags); return { success: true, data: result }; } catch (error) { return { success: false, error: error.message }; } } }, { name: 'generateArticle', description: 'Generate an article based on tags or an example article', parameters: { type: 'object', properties: { tags: { type: 'object', description: 'The tags for the article to generate', properties: { subject: { type: 'string' }, grade: { type: 'string' }, standard: { type: 'string' }, lesson: { type: 'string' } } }, exampleArticle: { type: 'string', description: 'An example article to generate a variant of' } }, oneOf: [ { required: ['tags'] }, { required: ['exampleArticle'] } ] }, handler: async ({ tags, exampleArticle }) => { try { const result = await articleService.generateArticle(tags, exampleArticle); return { success: true, data: result }; } catch (error) { return { success: false, error: error.message }; } } }, // Database Tools { name: 'searchExamples', description: 'Search for example content in the CCC database', parameters: { type: 'object', properties: { contentType: { type: 'string', description: 'The type of content to search for (question or article)', enum: ['question', 'article'] }, subject: { type: 'string', description: 'The subject to search for' }, grade: { type: 'string', description: 'The grade level to search for' }, standard: { type: 'string', description: 'The educational standard to search for' }, lesson: { type: 'string', description: 'The lesson to search for' }, difficulty: { type: 'integer', description: 'The difficulty level to search for (for questions only)' }, limit: { type: 'integer', description: 'The maximum number of results to return', default: 10 } }, required: ['contentType'] }, handler: async ({ contentType, subject, grade, standard, lesson, difficulty, limit = 10 }) => { try { // Build query based on content type let query; let params = []; let paramIndex = 1; if (contentType === 'question') { query = 'SELECT * FROM ccc.questions WHERE 1=1'; if (subject) { query += ` AND subject = $${paramIndex++}`; params.push(subject); } if (grade) { query += ` AND grade = $${paramIndex++}`; params.push(grade); } if (standard) { query += ` AND standard = $${paramIndex++}`; params.push(standard); } if (lesson) { query += ` AND lesson = $${paramIndex++}`; params.push(lesson); } if (difficulty) { query += ` AND difficulty = $${paramIndex++}`; params.push(difficulty); } query += ` LIMIT $${paramIndex}`; params.push(limit); } else if (contentType === 'article') { query = 'SELECT * FROM ccc.articles WHERE 1=1'; if (subject) { query += ` AND subject = $${paramIndex++}`; params.push(subject); } if (grade) { query += ` AND grade = $${paramIndex++}`; params.push(grade); } if (standard) { query += ` AND standard = $${paramIndex++}`; params.push(standard); } if (lesson) { query += ` AND lesson = $${paramIndex++}`; params.push(lesson); } query += ` LIMIT $${paramIndex}`; params.push(limit); } else { throw new Error('Invalid content type'); } // Execute query const result = await pool.query(query, params); return { success: true, data: result.rows }; } catch (error) { return { success: false, error: error.message }; } } }, { name: 'getCourseStructure', description: 'Get the structure of a course', parameters: { type: 'object', properties: { subject: { type: 'string', description: 'The subject of the course' }, grade: { type: 'string', description: 'The grade level of the course' }, courseId: { type: 'integer', description: 'The ID of the course' } }, oneOf: [ { required: ['courseId'] }, { required: ['subject', 'grade'] } ] }, handler: async ({ subject, grade, courseId }) => { try { let query; let params = []; if (courseId) { query = ` SELECT c.*, json_agg(json_build_object( 'id', l.id, 'title', l.title, 'description', l.description, 'standard', l.standard, 'sequence_number', l.sequence_number ) ORDER BY l.sequence_number) AS lessons FROM ccc.courses c LEFT JOIN ccc.lessons l ON c.id = l.course_id WHERE c.id = $1 GROUP BY c.id `; params.push(courseId); } else { query = ` SELECT c.*, json_agg(json_build_object( 'id', l.id, 'title', l.title, 'description', l.description, 'standard', l.standard, 'sequence_number', l.sequence_number ) ORDER BY l.sequence_number) AS lessons FROM ccc.courses c LEFT JOIN ccc.lessons l ON c.id = l.course_id WHERE c.subject = $1 AND c.grade = $2 GROUP BY c.id `; params.push(subject, grade); } const result = await pool.query(query, params); if (result.rows.length === 0) { throw new Error('Course not found'); } return { success: true, data: result.rows[0] }; } catch (error) { return { success: false, error: error.message }; } } } ], resources: [ { name: 'question', description: 'An educational question', methods: { get: async ({ id }) => { try { const result = await pool.query('SELECT * FROM ccc.questions WHERE id = $1', [id]); if (result.rows.length === 0) { throw new Error('Question not found'); } return { success: true, data: result.rows[0] }; } catch (error) { return { success: false, error: error.message }; } }, list: async ({ subject, grade, standard, lesson, difficulty, limit = 10 }) => { try { let query = 'SELECT * FROM ccc.questions WHERE 1=1'; let params = []; let paramIndex = 1; if (subject) { query += ` AND subject = $${paramIndex++}`; params.push(subject); } if (grade) { query += ` AND grade = $${paramIndex++}`; params.push(grade); } if (standard) { query += ` AND standard = $${paramIndex++}`; params.push(standard); } if (lesson) { query += ` AND lesson = $${paramIndex++}`; params.push(lesson); } if (difficulty) { query += ` AND difficulty = $${paramIndex++}`; params.push(difficulty); } query += ` LIMIT $${paramIndex}`; params.push(limit); const result = await pool.query(query, params); return { success: true, data: result.rows }; } catch (error) { return { success: false, error: error.message }; } } } }, { name: 'article', description: 'An educational article', methods: { get: async ({ id }) => { try { const result = await pool.query('SELECT * FROM ccc.articles WHERE id = $1', [id]); if (result.rows.length === 0) { throw new Error('Article not found'); } return { success: true, data: result.rows[0] }; } catch (error) { return { success: false, error: error.message }; } }, list: async ({ subject, grade, standard, lesson, limit = 10 }) => { try { let query = 'SELECT * FROM ccc.articles WHERE 1=1'; let params = []; let paramIndex = 1; if (subject) { query += ` AND subject = $${paramIndex++}`; params.push(subject); } if (grade) { query += ` AND grade = $${paramIndex++}`; params.push(grade); } if (standard) { query += ` AND standard = $${paramIndex++}`; params.push(standard); } if (lesson) { query += ` AND lesson = $${paramIndex++}`; params.push(lesson); } query += ` LIMIT $${paramIndex}`; params.push(limit); const result = await pool.query(query, params); return { success: true, data: result.rows }; } catch (error) { return { success: false, error: error.message }; } } } }, { name: 'course', description: 'An educational course', methods: { get: async ({ id }) => { try { const query = ` SELECT c.*, json_agg(json_build_object( 'id', l.id, 'title', l.title, 'description', l.description, 'standard', l.standard, 'sequence_number', l.sequence_number ) ORDER BY l.sequence_number) AS lessons FROM ccc.courses c LEFT JOIN ccc.lessons l ON c.id = l.course_id WHERE c.id = $1 GROUP BY c.id `; const result = await pool.query(query, [id]); if (result.rows.length === 0) { throw new Error('Course not found'); } return { success: true, data: result.rows[0] }; } catch (error) { return { success: false, error: error.message }; } }, list: async ({ subject, grade, limit = 10 }) => { try { let query = ` SELECT c.*, json_agg(json_build_object( 'id', l.id, 'title', l.title, 'description', l.description, 'standard', l.standard, 'sequence_number', l.sequence_number ) ORDER BY l.sequence_number) AS lessons FROM ccc.courses c LEFT JOIN ccc.lessons l ON c.id = l.course_id WHERE 1=1 `; let params = []; let paramIndex = 1; if (subject) { query += ` AND c.subject = $${paramIndex++}`; params.push(subject); } if (grade) { query += ` AND c.grade = $${paramIndex++}`; params.push(grade); } query += ` GROUP BY c.id LIMIT $${paramIndex}`; params.push(limit); const result = await pool.query(query, params); return { success: true, data: result.rows }; } catch (error) { return { success: false, error: error.message }; } } } } ] }); // Start the server server.listen(process.env.PORT || 3000, () => { console.log(`Gauntlet Incept MCP Server running on port ${process.env.PORT || 3000}`); });