Skip to main content
Glama
jotraynor

Soulseek MCP Server

by jotraynor

search

Find songs and music files on the Soulseek peer-to-peer network by searching with artist names, song titles, or album queries.

Instructions

Search for songs/files on the Soulseek network. Returns a list of available files matching the query.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
queryYesSearch query (artist name, song title, album, etc.)
limitNoMaximum number of results to return (default: 50)

Implementation Reference

  • MCP tool handler for 'search' tool: parses input with searchSchema, calls soulseekClient.search, formats results, and returns formatted text response.
    case 'search': {
      const parsed = searchSchema.parse(args);
      const results = await soulseekClient.search(parsed.query, parsed.limit);
    
      if (results.length === 0) {
        return {
          content: [
            {
              type: 'text',
              text: `No results found for "${parsed.query}".`,
            },
          ],
        };
      }
    
      const formatted = results.map((r, i) => formatSearchResult(r, i)).join('\n\n');
    
      // Also return structured data for programmatic use
      const jsonData = JSON.stringify(results, null, 2);
    
      return {
        content: [
          {
            type: 'text',
            text: `Found ${results.length} result(s) for "${parsed.query}":\n\n${formatted}\n\n---\nTo download a file, use the download tool with the username and full filename path.`,
          },
        ],
      };
    }
  • Zod schema for validating 'search' tool input parameters: query (required string), limit (optional number, default 50).
    const searchSchema = z.object({
      query: z.string().describe('Search query for songs/files'),
      limit: z.number().optional().default(50).describe('Maximum number of results to return'),
    });
  • src/index.ts:74-91 (registration)
    Registration of 'search' tool in ListToolsRequestHandler: defines name, description, and inputSchema matching searchSchema.
    {
      name: 'search',
      description: 'Search for songs/files on the Soulseek network. Returns a list of available files matching the query.',
      inputSchema: {
        type: 'object',
        properties: {
          query: {
            type: 'string',
            description: 'Search query (artist name, song title, album, etc.)',
          },
          limit: {
            type: 'number',
            description: 'Maximum number of results to return (default: 50)',
          },
        },
        required: ['query'],
      },
    },
  • Core search implementation in SoulseekClientWrapper: performs Soulseek network search, processes results into SearchResult[], sorts by availability and speed, limits results.
    async search(query: string, limit: number = 50): Promise<SearchResult[]> {
      await this.ensureConnected();
    
      if (!this.client) {
        throw new Error('Client not connected');
      }
    
      const results: SearchResult[] = [];
    
      try {
        const searchResults = await this.client.search(query, {
          timeout: 10000,
          onResult: (result) => {
            for (const file of result.files) {
              // Extract attributes
              const bitrate = file.attrs.get(0) ?? null; // FileAttribute.Bitrate = 0
              const duration = file.attrs.get(1) ?? null; // FileAttribute.Duration = 1
    
              results.push({
                username: result.username,
                filename: file.filename,
                size: Number(file.size),
                bitrate,
                duration,
                slotsFree: result.slotsFree,
                speed: result.avgSpeed,
                queueLength: result.queueLength,
              });
            }
          },
        });
    
        // Sort by: slots free first, then by speed descending
        results.sort((a, b) => {
          if (a.slotsFree !== b.slotsFree) {
            return a.slotsFree ? -1 : 1;
          }
          return b.speed - a.speed;
        });
    
        return results.slice(0, limit);
      } catch (error) {
        throw new Error(`Search failed: ${error instanceof Error ? error.message : String(error)}`);
      }
    }
  • TypeScript interface defining the structure of search results returned by the search tool.
    export interface SearchResult {
      username: string;
      filename: string;
      size: number;
      bitrate: number | null;
      duration: number | null;
      slotsFree: boolean;
      speed: number;
      queueLength: number;
    }
Install Server

Other Tools

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/jotraynor/SoulseekMCP'

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