Skip to main content
Glama
pixxelboy
by pixxelboy
mcp-server.test.ts8.17 kB
/** * Integration tests for MCP Server * * These tests verify that the MCP server correctly handles * tool listing and tool execution. */ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { Server } from '@modelcontextprotocol/sdk/server/index.js'; // Mock the tool handlers vi.mock('../../src/tools/analyze-music.js', () => ({ analyzeMusicTool: { name: 'analyze_music', description: 'Test description', inputSchema: { type: 'object', properties: {}, required: [] }, }, handleAnalyzeMusic: vi.fn(), })); vi.mock('../../src/tools/separate-stems.js', () => ({ separateStemsTool: { name: 'separate_stems', description: 'Test description', inputSchema: { type: 'object', properties: {}, required: [] }, }, handleSeparateStems: vi.fn(), })); vi.mock('../../src/tools/detect-ai-music.js', () => ({ detectAiMusicTool: { name: 'detect_ai_music', description: 'Test description', inputSchema: { type: 'object', properties: {}, required: [] }, }, handleDetectAiMusic: vi.fn(), })); vi.mock('../../src/tools/analyze-loudness.js', () => ({ analyzeLoudnessTool: { name: 'analyze_loudness', description: 'Test description', inputSchema: { type: 'object', properties: {}, required: [] }, }, handleAnalyzeLoudness: vi.fn(), })); vi.mock('../../src/tools/check-job-status.js', () => ({ checkJobStatusTool: { name: 'check_job_status', description: 'Test description', inputSchema: { type: 'object', properties: {}, required: [] }, }, handleCheckJobStatus: vi.fn(), })); // Mock auth vi.mock('../../src/utils/auth.js', () => ({ getApiKey: vi.fn(() => 'test-api-key'), hasApiKey: vi.fn(() => true), createAuthHeaders: vi.fn(() => ({})), API_KEY_ENV_VAR: 'IRCAM_AMPLIFY_API_KEY', })); import { handleAnalyzeMusic } from '../../src/tools/analyze-music.js'; import { handleSeparateStems } from '../../src/tools/separate-stems.js'; import { handleDetectAiMusic } from '../../src/tools/detect-ai-music.js'; import { handleAnalyzeLoudness } from '../../src/tools/analyze-loudness.js'; import { handleCheckJobStatus } from '../../src/tools/check-job-status.js'; import { hasApiKey } from '../../src/utils/auth.js'; describe('MCP Server Integration', () => { const originalEnv = process.env; beforeEach(() => { vi.clearAllMocks(); process.env = { ...originalEnv }; process.env.IRCAM_AMPLIFY_API_KEY = 'test-api-key'; }); afterEach(() => { process.env = originalEnv; vi.resetAllMocks(); }); describe('Tool Registration', () => { it('should export all 5 tools', async () => { // Import the tools const { analyzeMusicTool } = await import('../../src/tools/analyze-music.js'); const { separateStemsTool } = await import('../../src/tools/separate-stems.js'); const { detectAiMusicTool } = await import('../../src/tools/detect-ai-music.js'); const { analyzeLoudnessTool } = await import('../../src/tools/analyze-loudness.js'); const { checkJobStatusTool } = await import('../../src/tools/check-job-status.js'); const tools = [ analyzeMusicTool, separateStemsTool, detectAiMusicTool, analyzeLoudnessTool, checkJobStatusTool, ]; expect(tools).toHaveLength(5); tools.forEach((tool) => { expect(tool).toHaveProperty('name'); expect(tool).toHaveProperty('description'); expect(tool).toHaveProperty('inputSchema'); }); }); it('should have unique tool names', async () => { const { analyzeMusicTool } = await import('../../src/tools/analyze-music.js'); const { separateStemsTool } = await import('../../src/tools/separate-stems.js'); const { detectAiMusicTool } = await import('../../src/tools/detect-ai-music.js'); const { analyzeLoudnessTool } = await import('../../src/tools/analyze-loudness.js'); const { checkJobStatusTool } = await import('../../src/tools/check-job-status.js'); const names = [ analyzeMusicTool.name, separateStemsTool.name, detectAiMusicTool.name, analyzeLoudnessTool.name, checkJobStatusTool.name, ]; const uniqueNames = new Set(names); expect(uniqueNames.size).toBe(5); }); }); describe('Tool Execution Flow', () => { it('should execute analyze_music and return result', async () => { const mockResult = { genre: ['electronic'], mood: ['energetic'], tempo: 128, key: 'A minor', instruments: ['synth'], }; vi.mocked(handleAnalyzeMusic).mockResolvedValue(mockResult); const result = await handleAnalyzeMusic({ audio_url: 'https://example.com/song.mp3', }); expect(result).toEqual(mockResult); }); it('should execute separate_stems and return job_id for async', async () => { const mockResult = { job_id: 'job-123' }; vi.mocked(handleSeparateStems).mockResolvedValue(mockResult); const result = await handleSeparateStems({ audio_url: 'https://example.com/song.mp3', }); expect(result).toEqual({ job_id: 'job-123' }); }); it('should execute detect_ai_music and return classification', async () => { const mockResult = { confidence: 85, classification: 'ai_generated' as const, }; vi.mocked(handleDetectAiMusic).mockResolvedValue(mockResult); const result = await handleDetectAiMusic({ audio_url: 'https://example.com/song.mp3', }); expect(result.classification).toBe('ai_generated'); expect(result.confidence).toBe(85); }); it('should execute analyze_loudness and return metrics', async () => { const mockResult = { integrated_lufs: -14.0, true_peak_db: -1.0, loudness_range: 6.0, }; vi.mocked(handleAnalyzeLoudness).mockResolvedValue(mockResult); const result = await handleAnalyzeLoudness({ audio_url: 'https://example.com/song.mp3', }); expect(result.integrated_lufs).toBe(-14.0); expect(result.true_peak_db).toBe(-1.0); }); it('should execute check_job_status and return job state', async () => { const mockResult = { status: 'completed' as const, progress: 100, result: { vocals_url: 'https://cdn.example.com/vocals.wav', drums_url: 'https://cdn.example.com/drums.wav', bass_url: 'https://cdn.example.com/bass.wav', other_url: 'https://cdn.example.com/other.wav', }, }; vi.mocked(handleCheckJobStatus).mockResolvedValue(mockResult); const result = await handleCheckJobStatus({ job_id: 'job-123', }); expect(result.status).toBe('completed'); expect(result.result).toBeDefined(); }); }); describe('API Key Validation', () => { it('should check API key before tool execution', () => { vi.mocked(hasApiKey).mockReturnValue(true); expect(hasApiKey()).toBe(true); }); it('should return false when API key is not set', () => { vi.mocked(hasApiKey).mockReturnValue(false); expect(hasApiKey()).toBe(false); }); }); describe('Error Handling', () => { it('should propagate errors from tool handlers', async () => { const mockError = { code: 'INVALID_URL', message: 'Invalid URL', suggestion: 'Use a valid URL', }; vi.mocked(handleAnalyzeMusic).mockRejectedValue(mockError); await expect( handleAnalyzeMusic({ audio_url: 'invalid' }) ).rejects.toMatchObject({ code: 'INVALID_URL', }); }); it('should handle rate limiting errors', async () => { const mockError = { code: 'RATE_LIMITED', message: 'Rate limited', suggestion: 'Wait and retry', retry_after: 30, }; vi.mocked(handleAnalyzeMusic).mockRejectedValue(mockError); await expect( handleAnalyzeMusic({ audio_url: 'https://example.com/song.mp3' }) ).rejects.toMatchObject({ code: 'RATE_LIMITED', retry_after: 30, }); }); }); });

Latest Blog Posts

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/pixxelboy/amplify-mcp'

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