Skip to main content
Glama

Promptopia MCP

by lumile
MIT License
18
2
mcp-integration.test.ts15.1 kB
import { describe, it, expect, beforeAll, afterAll, beforeEach, vi } from 'vitest' import { PromptsService } from '../src/services/prompts.service.js' import { PromptsHandler } from '../src/handlers/prompts.handler.js' import { ToolsHandler } from '../src/handlers/tools.handler.js' import { fileURLToPath } from 'url' import path from 'path' import fs from 'fs/promises' const __filename = fileURLToPath(import.meta.url) const __dirname = path.dirname(__filename) const TEST_PROMPTS_DIR = path.join(__dirname, '..', 'test-prompts-integration') describe('MCP Prompts Integration', () => { let promptsService: PromptsService let promptsHandler: PromptsHandler let toolsHandler: ToolsHandler let mockServer: any beforeAll(async () => { // Create test prompts directory await fs.mkdir(TEST_PROMPTS_DIR, { recursive: true }) // Mock server with minimal implementation needed for testing mockServer = { notification: vi.fn(), setRequestHandler: vi.fn(), onerror: vi.fn(), connect: vi.fn(), close: vi.fn() } // Initialize services with test directory process.env.PROMPTS_DIR = TEST_PROMPTS_DIR promptsService = new PromptsService() promptsHandler = new PromptsHandler(mockServer, promptsService) toolsHandler = new ToolsHandler() }) beforeEach(async () => { // Clean up any existing test files before each test try { const files = await fs.readdir(TEST_PROMPTS_DIR) for (const file of files) { if (file.endsWith('.json')) { await fs.unlink(path.join(TEST_PROMPTS_DIR, file)) } } } catch (error) { // Directory might be empty, that's fine } }) afterAll(async () => { // Clean up test prompts directory await fs.rm(TEST_PROMPTS_DIR, { recursive: true, force: true }) }) describe('MCP Protocol Compliance', () => { it('should list prompts via MCP protocol', async () => { // Create test prompts using tools await toolsHandler.callTool('add_prompt', { name: 'Simple Greeting', content: 'Hello {{name}}!', description: 'A simple greeting' }) await toolsHandler.callTool('add_multi_message_prompt', { name: 'Interview Assistant', description: 'Conducts interviews', messages: [ { role: 'assistant', content: { type: 'text', text: 'You are an interviewer for {{position}} roles.' } }, { role: 'user', content: { type: 'text', text: 'Begin interview for {{candidate}}.' } } ] }) // Test MCP list prompts const result = await promptsHandler.listPrompts() expect(result.prompts).toHaveLength(2) // Check MCP format compliance result.prompts.forEach(prompt => { expect(prompt).toHaveProperty('name') expect(prompt).toHaveProperty('description') expect(prompt).toHaveProperty('arguments') expect(Array.isArray(prompt.arguments)).toBe(true) if (prompt.arguments) { prompt.arguments.forEach(arg => { expect(arg).toHaveProperty('name') expect(arg).toHaveProperty('required') expect(typeof arg.required).toBe('boolean') }) } }) // Verify specific prompts const simpleGreeting = result.prompts.find(p => p.name === 'Simple Greeting') expect(simpleGreeting).toBeDefined() expect(simpleGreeting!.arguments).toHaveLength(1) expect(simpleGreeting!.arguments![0].name).toBe('name') const interviewAssistant = result.prompts.find(p => p.name === 'Interview Assistant') expect(interviewAssistant).toBeDefined() expect(interviewAssistant!.arguments).toHaveLength(2) expect(interviewAssistant!.arguments!.map(a => a.name)).toEqual(['position', 'candidate']) }) it('should execute single content prompt via MCP protocol', async () => { // Create test prompt await toolsHandler.callTool('add_prompt', { name: 'Weather Query', content: 'What is the weather like in {{city}} today?', description: 'Weather query prompt' }) // Test MCP get prompt without arguments const resultNoArgs = await promptsHandler.getPrompt('Weather Query') expect(resultNoArgs.messages).toHaveLength(1) expect(resultNoArgs.messages[0].role).toBe('user') expect(resultNoArgs.messages[0].content.type).toBe('text') expect(resultNoArgs.messages[0].content.text).toBe('What is the weather like in {{city}} today?') // Test MCP get prompt with arguments const resultWithArgs = await promptsHandler.getPrompt('Weather Query', { city: 'London' }) expect(resultWithArgs.messages).toHaveLength(1) expect(resultWithArgs.messages[0].role).toBe('user') expect(resultWithArgs.messages[0].content.text).toBe('What is the weather like in London today?') }) it('should execute multi-message prompt via MCP protocol', async () => { // Create test multi-message prompt await toolsHandler.callTool('add_multi_message_prompt', { name: 'Code Review Session', description: 'Comprehensive code review', messages: [ { role: 'assistant', content: { type: 'text', text: 'You are an expert {{language}} code reviewer. Focus on {{criteria}}.' } }, { role: 'user', content: { type: 'text', text: 'Please review this {{language}} code:\n\n{{code}}' } }, { role: 'assistant', content: { type: 'text', text: 'I will analyze the code for {{criteria}} and provide feedback.' } } ] }) // Test MCP get prompt without arguments const resultNoArgs = await promptsHandler.getPrompt('Code Review Session') expect(resultNoArgs.messages).toHaveLength(3) expect(resultNoArgs.messages[0].role).toBe('assistant') expect(resultNoArgs.messages[1].role).toBe('user') expect(resultNoArgs.messages[2].role).toBe('assistant') // Check template variables are preserved expect(resultNoArgs.messages[0].content.text).toContain('{{language}}') expect(resultNoArgs.messages[0].content.text).toContain('{{criteria}}') // Test MCP get prompt with arguments const resultWithArgs = await promptsHandler.getPrompt('Code Review Session', { language: 'TypeScript', code: 'function add(a: number, b: number) { return a + b; }', criteria: 'performance and type safety' }) expect(resultWithArgs.messages).toHaveLength(3) // Check variables are applied expect(resultWithArgs.messages[0].content.text).toBe('You are an expert TypeScript code reviewer. Focus on performance and type safety.') expect(resultWithArgs.messages[1].content.text).toContain('function add(a: number, b: number)') expect(resultWithArgs.messages[2].content.text).toBe('I will analyze the code for performance and type safety and provide feedback.') }) }) describe('End-to-End Workflow', () => { it('should support complete prompt lifecycle via tools and MCP', async () => { // 1. Create prompt via tools const createResult = await toolsHandler.callTool('add_multi_message_prompt', { name: 'Tutorial Creator', description: 'Creates programming tutorials', messages: [ { role: 'assistant', content: { type: 'text', text: 'You are a programming tutorial creator specializing in {{topic}}.' } }, { role: 'user', content: { type: 'text', text: 'Create a {{difficulty}} tutorial about {{topic}} for {{audience}}.' } } ] }) const promptData = JSON.parse(createResult.content[0].text) expect(promptData.name).toBe('Tutorial Creator') // 2. Verify prompt appears in MCP list const listResult = await promptsHandler.listPrompts() const tutorialPrompt = listResult.prompts.find(p => p.name === 'Tutorial Creator') expect(tutorialPrompt).toBeDefined() expect(tutorialPrompt!.arguments).toHaveLength(3) // 3. Use prompt via MCP protocol const mcpResult = await promptsHandler.getPrompt('Tutorial Creator', { topic: 'React Hooks', difficulty: 'intermediate', audience: 'web developers' }) expect(mcpResult.messages).toHaveLength(2) expect(mcpResult.messages[0].content.text).toBe('You are a programming tutorial creator specializing in React Hooks.') expect(mcpResult.messages[1].content.text).toBe('Create a intermediate tutorial about React Hooks for web developers.') // 4. Update prompt via tools const updateResult = await toolsHandler.callTool('update_prompt', { id: promptData.id, name: 'Enhanced Tutorial Creator', description: 'Enhanced tutorial creator with examples', messages: [ { role: 'assistant', content: { type: 'text', text: 'You are an expert tutorial creator for {{topic}}. Include {{examples}} examples.' } }, { role: 'user', content: { type: 'text', text: 'Create a {{difficulty}} tutorial about {{topic}}.' } }, { role: 'assistant', content: { type: 'text', text: 'I will create a comprehensive tutorial with {{examples}} practical examples.' } } ] }) const updatedData = JSON.parse(updateResult.content[0].text) expect(updatedData.success).toBe(true) // 5. Verify updated prompt via MCP const updatedMcpResult = await promptsHandler.getPrompt('Enhanced Tutorial Creator', { topic: 'Vue.js', difficulty: 'beginner', examples: 'three' }) expect(updatedMcpResult.messages).toHaveLength(3) expect(updatedMcpResult.messages[0].content.text).toBe('You are an expert tutorial creator for Vue.js. Include three examples.') expect(updatedMcpResult.messages[2].content.text).toBe('I will create a comprehensive tutorial with three practical examples.') }) it('should handle conversion from single to multi-message format', async () => { // 1. Create single content prompt const createResult = await toolsHandler.callTool('add_prompt', { name: 'Simple Question', content: 'What is {{concept}} in {{subject}}?', description: 'Simple question template' }) const promptData = JSON.parse(createResult.content[0].text) // 2. Verify single format via MCP const singleResult = await promptsHandler.getPrompt('Simple Question', { concept: 'inheritance', subject: 'JavaScript' }) expect(singleResult.messages).toHaveLength(1) expect(singleResult.messages[0].role).toBe('user') expect(singleResult.messages[0].content.text).toBe('What is inheritance in JavaScript?') // 3. Convert to multi-message format via tools await toolsHandler.callTool('update_prompt', { id: promptData.id, name: 'Enhanced Question', messages: [ { role: 'assistant', content: { type: 'text', text: 'You are a knowledgeable teacher in {{subject}}.' } }, { role: 'user', content: { type: 'text', text: 'Explain {{concept}} in {{subject}} with examples.' } } ] }) // 4. Verify multi-message format via MCP const multiResult = await promptsHandler.getPrompt('Enhanced Question', { concept: 'closures', subject: 'JavaScript' }) expect(multiResult.messages).toHaveLength(2) expect(multiResult.messages[0].role).toBe('assistant') expect(multiResult.messages[0].content.text).toBe('You are a knowledgeable teacher in JavaScript.') expect(multiResult.messages[1].role).toBe('user') expect(multiResult.messages[1].content.text).toBe('Explain closures in JavaScript with examples.') }) }) describe('Error Handling Integration', () => { it('should handle MCP errors for non-existent prompts', async () => { await expect(promptsHandler.getPrompt('Non Existent Prompt')).rejects.toThrow('Prompt not found: Non Existent Prompt') }) it('should handle tool errors for invalid prompt data', async () => { // Test invalid multi-message prompt (empty messages) await expect(toolsHandler.callTool('add_multi_message_prompt', { name: 'Invalid Prompt', messages: [] })).rejects.toThrow() // Test invalid variable application await toolsHandler.callTool('add_prompt', { name: 'Variable Test', content: 'Need {{required}} variable' }) await expect(toolsHandler.callTool('apply_prompt', { id: 'prompt-123', // Non-existent ID variables: {} })).rejects.toThrow() }) }) describe('Mixed Format Compatibility', () => { it('should handle both formats seamlessly in MCP listing', async () => { // Create both formats await toolsHandler.callTool('add_prompt', { name: 'Single Format', content: 'Single {{variable}}', description: 'Single content' }) await toolsHandler.callTool('add_multi_message_prompt', { name: 'Multi Format', description: 'Multi-message content', messages: [ { role: 'user', content: { type: 'text', text: 'Multi {{variable}}' } } ] }) // List via MCP const result = await promptsHandler.listPrompts() expect(result.prompts).toHaveLength(2) // Both should have same MCP structure result.prompts.forEach(prompt => { expect(prompt).toHaveProperty('name') expect(prompt).toHaveProperty('description') expect(prompt).toHaveProperty('arguments') expect(prompt.arguments).toHaveLength(1) expect(prompt.arguments![0].name).toBe('variable') }) // Test execution of both formats const singleResult = await promptsHandler.getPrompt('Single Format', { variable: 'test' }) const multiResult = await promptsHandler.getPrompt('Multi Format', { variable: 'test' }) expect(singleResult.messages).toHaveLength(1) expect(multiResult.messages).toHaveLength(1) expect(singleResult.messages[0].content.text).toBe('Single test') expect(multiResult.messages[0].content.text).toBe('Multi test') }) }) })

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/lumile/promptopia-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server