Gauntlet-Incept MCP
by Birdsmith
- 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}`);
});