Skip to main content
Glama

Discogs MCP Server

by cswkim
database.test.ts51.3 kB
import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js'; import { FastMCP } from 'fastmcp'; import { describe, expect, it, vi } from 'vitest'; import { formatDiscogsError } from '../../src/errors.js'; import { ArtistService } from '../../src/services/artist'; import { DatabaseService } from '../../src/services/database'; import { LabelService } from '../../src/services/label'; import { MasterReleaseService } from '../../src/services/master'; import { ReleaseService } from '../../src/services/release'; import { deleteReleaseRatingTool, editReleaseRatingTool, getArtistReleasesTool, getArtistTool, getLabelReleasesTool, getLabelTool, getMasterReleaseTool, getMasterReleaseVersionsTool, getReleaseCommunityRatingTool, getReleaseRatingTool, getReleaseTool, searchTool, } from '../../src/tools/database'; import { CurrencyCodeSchema } from '../../src/types/common'; import { runWithTestServer } from '../utils/testServer'; describe('Database Tools', () => { describe('get_release', () => { it('adds get_release tool', async () => { await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); server.addTool(getReleaseTool); return server; }, run: async ({ client }) => { expect(await client.listTools()).toEqual({ tools: [ { name: 'get_release', description: 'Get a release', inputSchema: { additionalProperties: false, $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', properties: { release_id: { type: 'number', minimum: 1 }, curr_abbr: { type: 'string', enum: CurrencyCodeSchema.options, }, }, required: ['release_id'], }, }, ], }); }, }); }); it('calls get_release tool', async () => { const mockRelease = { id: 123, title: 'Test Release', artists: [ { id: 456, name: 'Test Artist', join: ',', role: 'Main', anv: '', resource_url: 'https://api.discogs.com/artists/456', tracks: '', }, ], labels: [ { id: 789, name: 'Test Label', resource_url: 'https://api.discogs.com/labels/789', catno: 'TEST001', }, ], year: 2024, resource_url: 'https://api.discogs.com/releases/123', uri: 'https://www.discogs.com/release/123', }; await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); vi.spyOn(ReleaseService.prototype, 'get').mockResolvedValue(mockRelease); server.addTool(getReleaseTool); return server; }, run: async ({ client }) => { expect( await client.callTool({ name: 'get_release', arguments: { release_id: 123, }, }), ).toEqual({ content: [{ type: 'text', text: JSON.stringify(mockRelease) }], }); }, }); }); it('handles get_release DiscogsResourceNotFoundError', async () => { await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); // Mock the service to throw a UserError vi.spyOn(ReleaseService.prototype, 'get').mockRejectedValue( formatDiscogsError('Resource not found'), ); server.addTool(getReleaseTool); return server; }, run: async ({ client }) => { expect( await client.callTool({ name: 'get_release', arguments: { release_id: 123, }, }), ).toEqual({ content: [{ type: 'text', text: 'Resource not found' }], isError: true, }); }, }); }); it('handles get_release invalid parameters', async () => { await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); server.addTool(getReleaseTool); return server; }, run: async ({ client }) => { try { await client.callTool({ name: 'get_release', arguments: {}, }); } catch (error) { expect(error).toBeInstanceOf(McpError); expect(error.code).toBe(ErrorCode.InvalidParams); } }, }); }); }); describe('get_release_rating_by_user', () => { it('adds get_release_rating_by_user tool', async () => { await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); server.addTool(getReleaseRatingTool); return server; }, run: async ({ client }) => { expect(await client.listTools()).toEqual({ tools: [ { name: 'get_release_rating_by_user', description: `Retrieves the release's rating for a given user`, inputSchema: { additionalProperties: false, $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', properties: { release_id: { type: 'number', minimum: 1 }, username: { type: 'string', minLength: 1 }, }, required: ['username', 'release_id'], }, }, ], }); }, }); }); it('calls get_release_rating_by_user tool', async () => { const mockRating = { release_id: 123, username: 'testuser', rating: 5, }; await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); vi.spyOn(ReleaseService.prototype, 'getRatingByUser').mockResolvedValue(mockRating); server.addTool(getReleaseRatingTool); return server; }, run: async ({ client }) => { expect( await client.callTool({ name: 'get_release_rating_by_user', arguments: { release_id: 123, username: 'testuser', }, }), ).toEqual({ content: [{ type: 'text', text: JSON.stringify(mockRating) }], }); }, }); }); it('handles get_release_rating_by_user DiscogsResourceNotFoundError', async () => { await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); vi.spyOn(ReleaseService.prototype, 'getRatingByUser').mockRejectedValue( formatDiscogsError('Resource not found'), ); server.addTool(getReleaseRatingTool); return server; }, run: async ({ client }) => { expect( await client.callTool({ name: 'get_release_rating_by_user', arguments: { release_id: 123, username: 'testuser', }, }), ).toEqual({ content: [{ type: 'text', text: 'Resource not found' }], isError: true, }); }, }); }); it('handles get_release_rating_by_user invalid parameters', async () => { await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); server.addTool(getReleaseRatingTool); return server; }, run: async ({ client }) => { try { await client.callTool({ name: 'get_release_rating_by_user', arguments: {}, }); } catch (error) { expect(error).toBeInstanceOf(McpError); expect(error.code).toBe(ErrorCode.InvalidParams); } }, }); }); }); describe('edit_release_rating', () => { it('adds edit_release_rating tool', async () => { await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); server.addTool(editReleaseRatingTool); return server; }, run: async ({ client }) => { expect(await client.listTools()).toEqual({ tools: [ { name: 'edit_release_rating', description: `Updates the release's rating for a given user`, inputSchema: { additionalProperties: false, $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', properties: { release_id: { type: 'number', minimum: 1 }, username: { type: 'string', minLength: 1 }, rating: { type: 'integer', minimum: 1, maximum: 5 }, }, required: ['username', 'release_id', 'rating'], }, }, ], }); }, }); }); it('calls edit_release_rating tool', async () => { const mockRating = { release_id: 123, username: 'testuser', rating: 5, }; await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); vi.spyOn(ReleaseService.prototype, 'editRatingByUser').mockResolvedValue(mockRating); server.addTool(editReleaseRatingTool); return server; }, run: async ({ client }) => { expect( await client.callTool({ name: 'edit_release_rating', arguments: { release_id: 123, username: 'testuser', rating: 5, }, }), ).toEqual({ content: [{ type: 'text', text: JSON.stringify(mockRating) }], }); }, }); }); it('handles edit_release_rating DiscogsResourceNotFoundError', async () => { await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); vi.spyOn(ReleaseService.prototype, 'editRatingByUser').mockRejectedValue( formatDiscogsError('Resource not found'), ); server.addTool(editReleaseRatingTool); return server; }, run: async ({ client }) => { expect( await client.callTool({ name: 'edit_release_rating', arguments: { release_id: 123, username: 'testuser', rating: 5, }, }), ).toEqual({ content: [{ type: 'text', text: 'Resource not found' }], isError: true, }); }, }); }); it('handles edit_release_rating invalid parameters', async () => { await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); server.addTool(editReleaseRatingTool); return server; }, run: async ({ client }) => { try { await client.callTool({ name: 'edit_release_rating', arguments: {}, }); } catch (error) { expect(error).toBeInstanceOf(McpError); expect(error.code).toBe(ErrorCode.InvalidParams); } try { await client.callTool({ name: 'edit_release_rating', arguments: { release_id: 123, username: 'testuser', rating: 6, }, }); } catch (error) { expect(error).toBeInstanceOf(McpError); expect(error.code).toBe(ErrorCode.InvalidParams); } }, }); }); }); describe('delete_release_rating', () => { it('adds delete_release_rating tool', async () => { await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); server.addTool(deleteReleaseRatingTool); return server; }, run: async ({ client }) => { expect(await client.listTools()).toEqual({ tools: [ { name: 'delete_release_rating', description: `Deletes the release's rating for a given user`, inputSchema: { additionalProperties: false, $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', properties: { release_id: { type: 'number', minimum: 1 }, username: { type: 'string', minLength: 1 }, }, required: ['username', 'release_id'], }, }, ], }); }, }); }); it('calls delete_release_rating tool', async () => { await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); vi.spyOn(ReleaseService.prototype, 'deleteRatingByUser').mockResolvedValue(undefined); server.addTool(deleteReleaseRatingTool); return server; }, run: async ({ client }) => { expect( await client.callTool({ name: 'delete_release_rating', arguments: { release_id: 123, username: 'testuser', }, }), ).toEqual({ content: [{ type: 'text', text: 'Release rating deleted successfully' }], }); }, }); }); it('handles delete_release_rating DiscogsResourceNotFoundError', async () => { await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); vi.spyOn(ReleaseService.prototype, 'deleteRatingByUser').mockRejectedValue( formatDiscogsError('Resource not found'), ); server.addTool(deleteReleaseRatingTool); return server; }, run: async ({ client }) => { expect( await client.callTool({ name: 'delete_release_rating', arguments: { release_id: 123, username: 'testuser', }, }), ).toEqual({ content: [{ type: 'text', text: 'Resource not found' }], isError: true, }); }, }); }); it('handles delete_release_rating invalid parameters', async () => { await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); server.addTool(deleteReleaseRatingTool); return server; }, run: async ({ client }) => { try { await client.callTool({ name: 'delete_release_rating', arguments: {}, }); } catch (error) { expect(error).toBeInstanceOf(McpError); expect(error.code).toBe(ErrorCode.InvalidParams); } }, }); }); }); describe('get_release_community_rating', () => { it('adds get_release_community_rating tool', async () => { await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); server.addTool(getReleaseCommunityRatingTool); return server; }, run: async ({ client }) => { expect(await client.listTools()).toEqual({ tools: [ { name: 'get_release_community_rating', description: 'Retrieves the release community rating average and count', inputSchema: { additionalProperties: false, $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', properties: { release_id: { type: 'number', minimum: 1 }, }, required: ['release_id'], }, }, ], }); }, }); }); it('calls get_release_community_rating tool', async () => { const mockRating = { release_id: 123, rating: { average: 4.5, count: 100, }, }; await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); vi.spyOn(ReleaseService.prototype, 'getCommunityRating').mockResolvedValue(mockRating); server.addTool(getReleaseCommunityRatingTool); return server; }, run: async ({ client }) => { expect( await client.callTool({ name: 'get_release_community_rating', arguments: { release_id: 123, }, }), ).toEqual({ content: [{ type: 'text', text: JSON.stringify(mockRating) }], }); }, }); }); it('handles get_release_community_rating DiscogsResourceNotFoundError', async () => { await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); vi.spyOn(ReleaseService.prototype, 'getCommunityRating').mockRejectedValue( formatDiscogsError('Resource not found'), ); server.addTool(getReleaseCommunityRatingTool); return server; }, run: async ({ client }) => { expect( await client.callTool({ name: 'get_release_community_rating', arguments: { release_id: 123, }, }), ).toEqual({ content: [{ type: 'text', text: 'Resource not found' }], isError: true, }); }, }); }); it('handles get_release_community_rating invalid parameters', async () => { await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); server.addTool(getReleaseCommunityRatingTool); return server; }, run: async ({ client }) => { try { await client.callTool({ name: 'get_release_community_rating', arguments: {}, }); } catch (error) { expect(error).toBeInstanceOf(McpError); expect(error.code).toBe(ErrorCode.InvalidParams); } }, }); }); }); describe('get_master_release', () => { it('adds get_master_release tool', async () => { await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); server.addTool(getMasterReleaseTool); return server; }, run: async ({ client }) => { expect(await client.listTools()).toEqual({ tools: [ { name: 'get_master_release', description: 'Get a master release', inputSchema: { additionalProperties: false, $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', properties: { master_id: { type: 'integer', minimum: -9007199254740991, maximum: 9007199254740991, }, }, required: ['master_id'], }, }, ], }); }, }); }); it('calls get_master_release tool', async () => { const mockMasterRelease = { id: 123, title: 'Test Master Release', artists: [ { id: 456, name: 'Test Artist', join: ',', role: 'Main', anv: '', resource_url: 'https://api.discogs.com/artists/456', tracks: '', thumbnail_url: 'https://api.discogs.com/artists/456/thumb.jpg', }, ], year: 2024, resource_url: 'https://api.discogs.com/masters/123', uri: 'https://www.discogs.com/master/123', main_release: 123, most_recent_release: 123, versions_url: 'https://api.discogs.com/masters/123/versions', main_release_url: 'https://api.discogs.com/releases/123', most_recent_release_url: 'https://api.discogs.com/releases/123', }; await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); vi.spyOn(MasterReleaseService.prototype, 'get').mockResolvedValue(mockMasterRelease); server.addTool(getMasterReleaseTool); return server; }, run: async ({ client }) => { expect( await client.callTool({ name: 'get_master_release', arguments: { master_id: 123, }, }), ).toEqual({ content: [{ type: 'text', text: JSON.stringify(mockMasterRelease) }], }); }, }); }); it('handles get_master_release DiscogsResourceNotFoundError', async () => { await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); vi.spyOn(MasterReleaseService.prototype, 'get').mockRejectedValue( formatDiscogsError('Resource not found'), ); server.addTool(getMasterReleaseTool); return server; }, run: async ({ client }) => { expect( await client.callTool({ name: 'get_master_release', arguments: { master_id: 123, }, }), ).toEqual({ content: [{ type: 'text', text: 'Resource not found' }], isError: true, }); }, }); }); it('handles get_master_release invalid parameters', async () => { await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); server.addTool(getMasterReleaseTool); return server; }, run: async ({ client }) => { try { await client.callTool({ name: 'get_master_release', arguments: {}, }); } catch (error) { expect(error).toBeInstanceOf(McpError); expect(error.code).toBe(ErrorCode.InvalidParams); } }, }); }); }); describe('get_artist', () => { it('adds get_artist tool', async () => { await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); server.addTool(getArtistTool); return server; }, run: async ({ client }) => { expect(await client.listTools()).toEqual({ tools: [ { name: 'get_artist', description: 'Get an artist', inputSchema: { additionalProperties: false, $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', properties: { artist_id: { type: 'number' }, }, required: ['artist_id'], }, }, ], }); }, }); }); it('calls get_artist tool', async () => { const mockArtist = { id: 123, name: 'Test Artist', profile: 'Test profile', resource_url: 'https://api.discogs.com/artists/123', uri: 'https://www.discogs.com/artist/123', }; await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); vi.spyOn(ArtistService.prototype, 'get').mockResolvedValue(mockArtist); server.addTool(getArtistTool); return server; }, run: async ({ client }) => { expect( await client.callTool({ name: 'get_artist', arguments: { artist_id: 123, }, }), ).toEqual({ content: [{ type: 'text', text: JSON.stringify(mockArtist) }], }); }, }); }); it('handles get_artist DiscogsResourceNotFoundError', async () => { await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); vi.spyOn(ArtistService.prototype, 'get').mockRejectedValue( formatDiscogsError('Resource not found'), ); server.addTool(getArtistTool); return server; }, run: async ({ client }) => { expect( await client.callTool({ name: 'get_artist', arguments: { artist_id: 123, }, }), ).toEqual({ content: [{ type: 'text', text: 'Resource not found' }], isError: true, }); }, }); }); it('handles get_artist invalid parameters', async () => { await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); server.addTool(getArtistTool); return server; }, run: async ({ client }) => { try { await client.callTool({ name: 'get_artist', arguments: {}, }); } catch (error) { expect(error).toBeInstanceOf(McpError); expect(error.code).toBe(ErrorCode.InvalidParams); } }, }); }); }); describe('get_artist_releases', () => { it('adds get_artist_releases tool', async () => { await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); server.addTool(getArtistReleasesTool); return server; }, run: async ({ client }) => { expect(await client.listTools()).toEqual({ tools: [ { name: 'get_artist_releases', description: `Get an artist's releases`, inputSchema: { additionalProperties: false, $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', properties: { artist_id: { type: 'number' }, page: { type: 'integer', minimum: 1, maximum: 9007199254740991 }, per_page: { type: 'integer', minimum: 1, maximum: 100 }, sort: { type: 'string', enum: ['year', 'title', 'format'], }, sort_order: { type: 'string', enum: ['asc', 'desc'], }, }, required: ['artist_id'], }, }, ], }); }, }); }); it('calls get_artist_releases tool', async () => { const mockReleases = { pagination: { page: 1, pages: 1, per_page: 50, items: 1, urls: { first: 'https://api.discogs.com/artists/123/releases?page=1', prev: undefined, next: undefined, last: 'https://api.discogs.com/artists/123/releases?page=1', }, }, releases: [ { id: 123, title: 'Test Release', year: 2024, resource_url: 'https://api.discogs.com/releases/123', uri: 'https://www.discogs.com/release/123', artist: 'Test Artist', type: 'release', status: 'Accepted', role: 'Main', catno: 'TEST001', thumb: 'https://api.discogs.com/releases/123/thumb.jpg', trackinfo: 'A1 Test Track', }, ], }; await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); vi.spyOn(ArtistService.prototype, 'getReleases').mockResolvedValue(mockReleases); server.addTool(getArtistReleasesTool); return server; }, run: async ({ client }) => { expect( await client.callTool({ name: 'get_artist_releases', arguments: { artist_id: 123, }, }), ).toEqual({ content: [{ type: 'text', text: JSON.stringify(mockReleases) }], }); }, }); }); it('handles get_artist_releases DiscogsResourceNotFoundError', async () => { await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); vi.spyOn(ArtistService.prototype, 'getReleases').mockRejectedValue( formatDiscogsError('Resource not found'), ); server.addTool(getArtistReleasesTool); return server; }, run: async ({ client }) => { expect( await client.callTool({ name: 'get_artist_releases', arguments: { artist_id: 123, }, }), ).toEqual({ content: [{ type: 'text', text: 'Resource not found' }], isError: true, }); }, }); }); it('handles get_artist_releases invalid parameters', async () => { await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); server.addTool(getArtistReleasesTool); return server; }, run: async ({ client }) => { try { await client.callTool({ name: 'get_artist_releases', arguments: {}, }); } catch (error) { expect(error).toBeInstanceOf(McpError); expect(error.code).toBe(ErrorCode.InvalidParams); } }, }); }); }); describe('get_label', () => { it('adds get_label tool', async () => { await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); server.addTool(getLabelTool); return server; }, run: async ({ client }) => { expect(await client.listTools()).toEqual({ tools: [ { name: 'get_label', description: 'Get a label', inputSchema: { additionalProperties: false, $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', properties: { label_id: { type: 'number' }, }, required: ['label_id'], }, }, ], }); }, }); }); it('calls get_label tool', async () => { const mockLabel = { id: 123, name: 'Test Label', profile: 'Test profile', resource_url: 'https://api.discogs.com/labels/123', uri: 'https://www.discogs.com/label/123', }; await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); vi.spyOn(LabelService.prototype, 'get').mockResolvedValue(mockLabel); server.addTool(getLabelTool); return server; }, run: async ({ client }) => { expect( await client.callTool({ name: 'get_label', arguments: { label_id: 123, }, }), ).toEqual({ content: [{ type: 'text', text: JSON.stringify(mockLabel) }], }); }, }); }); it('handles get_label DiscogsResourceNotFoundError', async () => { await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); vi.spyOn(LabelService.prototype, 'get').mockRejectedValue( formatDiscogsError('Resource not found'), ); server.addTool(getLabelTool); return server; }, run: async ({ client }) => { expect( await client.callTool({ name: 'get_label', arguments: { label_id: 123, }, }), ).toEqual({ content: [{ type: 'text', text: 'Resource not found' }], isError: true, }); }, }); }); it('handles get_label invalid parameters', async () => { await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); server.addTool(getLabelTool); return server; }, run: async ({ client }) => { try { await client.callTool({ name: 'get_label', arguments: {}, }); } catch (error) { expect(error).toBeInstanceOf(McpError); expect(error.code).toBe(ErrorCode.InvalidParams); } }, }); }); }); describe('get_label_releases', () => { it('adds get_label_releases tool', async () => { await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); server.addTool(getLabelReleasesTool); return server; }, run: async ({ client }) => { expect(await client.listTools()).toEqual({ tools: [ { name: 'get_label_releases', description: 'Returns a list of Releases associated with the label', inputSchema: { additionalProperties: false, $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', properties: { label_id: { type: 'number' }, page: { type: 'integer', minimum: 1, maximum: 9007199254740991 }, per_page: { type: 'integer', minimum: 1, maximum: 100 }, sort: { type: 'string', enum: [], }, sort_order: { type: 'string', enum: ['asc', 'desc'], }, }, required: ['label_id'], }, }, ], }); }, }); }); it('calls get_label_releases tool', async () => { const mockReleases = { pagination: { page: 1, pages: 1, per_page: 50, items: 1, urls: { first: 'https://api.discogs.com/labels/123/releases?page=1', prev: undefined, next: undefined, last: 'https://api.discogs.com/labels/123/releases?page=1', }, }, releases: [ { id: 123, title: 'Test Release', year: 2024, resource_url: 'https://api.discogs.com/releases/123', uri: 'https://www.discogs.com/release/123', artist: 'Test Artist', type: 'release', status: 'Accepted', role: 'Main', catno: 'TEST001', thumb: 'https://api.discogs.com/releases/123/thumb.jpg', trackinfo: 'A1 Test Track', }, ], }; await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); vi.spyOn(LabelService.prototype, 'getReleases').mockResolvedValue(mockReleases); server.addTool(getLabelReleasesTool); return server; }, run: async ({ client }) => { expect( await client.callTool({ name: 'get_label_releases', arguments: { label_id: 123, }, }), ).toEqual({ content: [{ type: 'text', text: JSON.stringify(mockReleases) }], }); }, }); }); it('handles get_label_releases DiscogsResourceNotFoundError', async () => { await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); vi.spyOn(LabelService.prototype, 'getReleases').mockRejectedValue( formatDiscogsError('Resource not found'), ); server.addTool(getLabelReleasesTool); return server; }, run: async ({ client }) => { expect( await client.callTool({ name: 'get_label_releases', arguments: { label_id: 123, }, }), ).toEqual({ content: [{ type: 'text', text: 'Resource not found' }], isError: true, }); }, }); }); it('handles get_label_releases invalid parameters', async () => { await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); server.addTool(getLabelReleasesTool); return server; }, run: async ({ client }) => { try { await client.callTool({ name: 'get_label_releases', arguments: {}, }); } catch (error) { expect(error).toBeInstanceOf(McpError); expect(error.code).toBe(ErrorCode.InvalidParams); } }, }); }); }); describe('search', () => { it('adds search tool', async () => { await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); server.addTool(searchTool); return server; }, run: async ({ client }) => { expect(await client.listTools()).toEqual({ tools: [ { name: 'search', description: 'Issue a search query to the Discogs database', inputSchema: { additionalProperties: false, $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', properties: { q: { type: 'string' }, type: { type: 'string', enum: ['artist', 'label', 'master', 'release'], }, title: { type: 'string' }, release_title: { type: 'string' }, credit: { type: 'string' }, artist: { type: 'string' }, anv: { type: 'string' }, label: { type: 'string' }, genre: { type: 'string' }, style: { type: 'string' }, country: { type: 'string' }, year: { type: 'string' }, format: { type: 'string' }, catno: { type: 'string' }, barcode: { type: 'string' }, track: { type: 'string' }, submitter: { type: 'string' }, contributor: { type: 'string' }, page: { type: 'integer', minimum: 1, maximum: 9007199254740991 }, per_page: { type: 'integer', minimum: 1, maximum: 100 }, sort: { type: 'string', enum: ['title', 'artist', 'year'], }, sort_order: { type: 'string', enum: ['asc', 'desc'], }, }, }, }, ], }); }, }); }); it('calls search tool', async () => { const mockResults = { pagination: { page: 1, pages: 1, per_page: 50, items: 1, urls: { first: 'https://api.discogs.com/database/search?q=test&page=1', prev: undefined, next: undefined, last: 'https://api.discogs.com/database/search?q=test&page=1', }, }, results: [ { id: 123, type: 'release' as const, title: 'Test Release', year: '2024', resource_url: 'https://api.discogs.com/releases/123', uri: 'https://www.discogs.com/release/123', catno: 'TEST001', community: { have: 100, want: 50, }, }, ], }; await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); vi.spyOn(DatabaseService.prototype, 'search').mockResolvedValue(mockResults); server.addTool(searchTool); return server; }, run: async ({ client }) => { expect( await client.callTool({ name: 'search', arguments: { q: 'test', }, }), ).toEqual({ content: [{ type: 'text', text: JSON.stringify(mockResults) }], }); }, }); }); }); describe('get_master_release_versions', () => { it('adds get_master_release_versions tool', async () => { await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); server.addTool(getMasterReleaseVersionsTool); return server; }, run: async ({ client }) => { expect(await client.listTools()).toEqual({ tools: [ { name: 'get_master_release_versions', description: 'Retrieves a list of all Releases that are versions of this master', inputSchema: { additionalProperties: false, $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', properties: { master_id: { type: 'integer', minimum: -9007199254740991, maximum: 9007199254740991, }, page: { type: 'integer', minimum: 1, maximum: 9007199254740991 }, per_page: { type: 'integer', minimum: 1, maximum: 100 }, sort: { type: 'string', enum: ['released', 'title', 'format', 'label', 'catno', 'country'], }, sort_order: { type: 'string', enum: ['asc', 'desc'], }, format: { type: 'string' }, label: { type: 'string' }, released: { type: 'string' }, country: { type: 'string' }, }, required: ['master_id'], }, }, ], }); }, }); }); it('calls get_master_release_versions tool', async () => { const mockVersions = { pagination: { page: 1, per_page: 50, pages: 2, items: 75, urls: { first: 'https://api.discogs.com/masters/123/versions?page=1', next: 'https://api.discogs.com/masters/123/versions?page=2', last: 'https://api.discogs.com/masters/123/versions?page=2', }, }, versions: [ { id: 456, label: 'Test Label', country: 'US', title: 'Test Release', major_formats: ['Vinyl'], format: 'LP', catno: 'TEST-001', released: '2024', status: 'Accepted', resource_url: 'https://api.discogs.com/releases/456', thumb: 'https://example.com/thumb.jpg', stats: { community: { in_wantlist: 10, in_collection: 20, }, user: { in_wantlist: 1, in_collection: 0, }, }, }, ], filters: { applied: {}, available: { format: { LP: 50, CD: 25 }, country: { US: 40, UK: 35 }, }, }, filter_facets: [ { title: 'Format', id: 'format', values: [ { title: 'LP', value: 'LP', count: 50 }, { title: 'CD', value: 'CD', count: 25 }, ], allows_multiple_values: true, }, { title: 'Country', id: 'country', values: [ { title: 'US', value: 'US', count: 40 }, { title: 'UK', value: 'UK', count: 35 }, ], allows_multiple_values: true, }, ], }; await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); vi.spyOn(MasterReleaseService.prototype, 'getVersions').mockResolvedValue(mockVersions); server.addTool(getMasterReleaseVersionsTool); return server; }, run: async ({ client }) => { expect( await client.callTool({ name: 'get_master_release_versions', arguments: { master_id: 123, }, }), ).toEqual({ content: [{ type: 'text', text: JSON.stringify(mockVersions) }], }); }, }); }); it('handles get_master_release_versions DiscogsResourceNotFoundError', async () => { await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); vi.spyOn(MasterReleaseService.prototype, 'getVersions').mockRejectedValue( formatDiscogsError('Resource not found'), ); server.addTool(getMasterReleaseVersionsTool); return server; }, run: async ({ client }) => { expect( await client.callTool({ name: 'get_master_release_versions', arguments: { master_id: 123, }, }), ).toEqual({ content: [{ type: 'text', text: 'Resource not found' }], isError: true, }); }, }); }); it('handles get_master_release_versions invalid parameters', async () => { await runWithTestServer({ server: async () => { const server = new FastMCP({ name: 'Test', version: '1.0.0', }); server.addTool(getMasterReleaseVersionsTool); return server; }, run: async ({ client }) => { try { await client.callTool({ name: 'get_master_release_versions', arguments: {}, }); } catch (error) { expect(error).toBeInstanceOf(McpError); expect(error.code).toBe(ErrorCode.InvalidParams); } try { await client.callTool({ name: 'get_master_release_versions', arguments: { master_id: 123, per_page: 200, }, }); } catch (error) { expect(error).toBeInstanceOf(McpError); expect(error.code).toBe(ErrorCode.InvalidParams); } }, }); }); }); });

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/cswkim/discogs-mcp-server'

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