Skip to main content
Glama

resolve_music

Find where to listen to, buy, or stream songs and albums across platforms like Spotify, Apple Music, and Amazon with direct links and pricing.

Instructions

Find where to listen to, buy, or stream a song or album. Returns ranked results from trusted music platforms (Spotify, Apple Music, Amazon, etc.) with prices and direct purchase/streaming links. Use this when a user asks about music, songs, albums, or artists.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
slugYesThe BeatsVine page slug for the track or album. Format: artist-name-song-title (lowercase, hyphenated). Example: 'ed-sheeran-galway-girl'

Implementation Reference

  • Main handler function resolveMusic that executes the tool logic. Fetches data from BeatsVine /[slug]/json endpoint, validates the response, and returns structured results with error handling.
    export async function resolveMusic(input: ResolveMusicInput): Promise<ResolveMusicResult> {
        const { slug } = input;
        const url = `${BEATSVINE_BASE}/${encodeURIComponent(slug)}/json`;
    
        try {
            const res = await fetch(url, {
                headers: {
                    "User-Agent": "rootvine-mcp/1.0.0",
                    "Accept": "application/json",
                },
                signal: AbortSignal.timeout(5000), // 5s timeout
            });
    
            if (!res.ok && res.status !== 404) {
                return {
                    success: false,
                    error: `BeatsVine returned HTTP ${res.status}`,
                };
            }
    
            const data = await res.json();
    
            // Validate against v1 schema
            const validation = validateResponse(data);
            if (!validation.success) {
                return {
                    success: false,
                    error: `Response validation failed: ${validation.error.message}`,
                };
            }
    
            return {
                success: true,
                response: validation.data as RootVineResponseV1,
            };
        } catch (err) {
            const message = err instanceof Error ? err.message : "Unknown error";
            return {
                success: false,
                error: `Failed to reach BeatsVine: ${message}`,
            };
        }
    }
  • Input/output TypeScript type definitions for resolveMusic: ResolveMusicInput (slug parameter) and ResolveMusicResult (success status with optional response or error).
    export interface ResolveMusicInput {
        slug: string;
    }
    
    export interface ResolveMusicResult {
        success: boolean;
        response?: RootVineResponseV1;
        error?: string;
    }
  • src/index.ts:47-82 (registration)
    MCP tool registration using server.registerTool for 'resolve_music'. Defines the tool description, input schema using Zod, and the handler that wraps resolveMusic with response formatting.
    // Tool: resolve_music
    // ============================================
    server.registerTool(
        "resolve_music",
        {
            description: "Find where to listen to, buy, or stream a song or album. Returns ranked results from trusted music platforms (Spotify, Apple Music, Amazon, etc.) with prices and direct purchase/streaming links. Use this when a user asks about music, songs, albums, or artists.",
            inputSchema: {
                slug: z
                    .string()
                    .describe("The BeatsVine page slug for the track or album. Format: artist-name-song-title (lowercase, hyphenated). Example: 'ed-sheeran-galway-girl'"),
            },
        },
        async ({ slug }) => {
            const result = await resolveMusic({ slug });
    
            if (!result.success || !result.response) {
                return {
                    content: [
                        {
                            type: "text" as const,
                            text: `Could not resolve music: ${result.error || "Unknown error"}`,
                        },
                    ],
                };
            }
    
            return {
                content: [
                    {
                        type: "text" as const,
                        text: formatMusicResponse(result.response),
                    },
                ],
            };
        },
    );
  • Zod schema definition for RootVineResponseV1 used to validate BeatsVine API responses before returning them to agents.
    export const RootVineResponseV1Schema = z.object({
        rootvine: z.object({
            version: z.literal("1.0"),
            resolved_at: z.string(),
            ttl_seconds: z.number().int().min(0),
            resolver: z.string(),
            category: z.enum(["music", "games"]),
            schema_url: z.string(),
        }),
        response_id: z.string().startsWith("rv_resp_"),
        status: z.enum(["success", "partial", "no_results", "error"]),
        query: QueryZ,
        results: z.array(ResultZ),
        warnings: z.array(z.string()),
        partial_sources: z.array(z.string()),
        error: ErrorZ.nullable(),
        cover_art: z.string().optional(),
        source_url: z.string().optional(),
        dlc_count: z.number().optional(),
        mcp: z.object({
            package: z.string(),
            tool_hint: z.string(),
        }),
    });
  • Helper function formatMusicResponse that formats the validated RootVine response for display to agents/users, including error handling, result ranking, and status display.
    export function formatMusicResponse(response: RootVineResponseV1): string {
        const lines: string[] = [];
    
        // Header
        if (response.query.artist && response.query.title) {
            lines.push(`🎵 ${response.query.artist} — ${response.query.title}`);
        } else {
            lines.push(`🎵 ${response.query.raw}`);
        }
    
        if (response.cover_art) {
            lines.push(`Cover: ${response.cover_art}`);
        }
        lines.push("");
    
        // Status handling
        if (response.status === "error" && response.error) {
            lines.push(`❌ Error: ${response.error.message}`);
            if (response.error.retryable) {
                lines.push("(This error is retryable)");
            }
            return lines.join("\n");
        }
    
        if (response.status === "no_results") {
            lines.push("No results found for this query.");
            if (response.source_url) {
                lines.push(`Source: ${response.source_url}`);
            }
            return lines.join("\n");
        }
    
        // Results
        for (const result of response.results) {
            const priceStr = result.price
                ? `${result.price.currency} ${result.price.amount.toFixed(2)}`
                : result.type === "stream" ? "Free" : "Price unknown";
    
            const link = result.click_url || result.url; // Always prefer click_url
    
            lines.push(
                `${result.rank}. **${result.merchant}** (${result.trust_tier})`,
                `   ${result.type === "stream" ? "▶️ Stream" : "🛒 Buy"} — ${priceStr}`,
                `   ${link}`,
                "",
            );
        }
    
        // Warnings
        if (response.warnings.length > 0) {
            lines.push(`⚠️ Warnings: ${response.warnings.join(", ")}`);
        }
    
        // Source
        if (response.source_url) {
            lines.push(`Source: ${response.source_url}`);
        }
    
        return lines.join("\n");
    }
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden. It discloses key behavioral traits like returning ranked results from specific platforms with prices and links, but lacks details on error handling, rate limits, or authentication needs.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with purpose and usage, uses two efficient sentences with zero wasted words, and every part earns its place by adding value.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's moderate complexity (1 parameter, no output schema, no annotations), the description is mostly complete. It explains what the tool does and when to use it, but could benefit from more behavioral details like response format or limitations.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3. The description adds no additional parameter semantics beyond what the schema provides, which already fully documents the 'slug' parameter.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose with specific verbs ('find where to listen to, buy, or stream') and resources ('song or album'), and distinguishes it from sibling tools by focusing on music rather than products or games.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly states when to use this tool ('when a user asks about music, songs, albums, or artists'), providing clear context for selection versus alternatives like find_product or resolve_game.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

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/RagingOrangutan/rootvine-mcp'

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