MCP server
A stdio Model Context Protocol server that gives AI agents the Tuneping API. It is packaged as the .NET global tool Tuneping.Mcp (command tuneping-mcp) and proxies https://api.tuneping.com/v1; every tool call is an ordinary API request under your key's quota.
What it is
The server runs as a child process of your MCP client (Claude Desktop, Claude Code, Cursor or any other client that speaks stdio) and talks JSON-RPC on stdin and stdout. It gives the agent what internet radio stations announce as playing right now, a play history, the track and artist catalogue, and wishlists. Results are the API's JSON, unchanged.
Metadata detections, not fingerprinted airplay; about 73 % of directory stations expose a title, about half are songs.
It needs the .NET 10 runtime or later to run, and the .NET 10 SDK to install, because dotnet tool is an SDK command.
Tools and resources
| Tool | What it returns | Plan |
|---|---|---|
search_now_playing(q, country?, limit?) | Stations playing a title matching q right now | Free |
get_station(station_id) | One station by Radio Browser UUID | Free |
search_stations(q, country?, tag?) | Stations by name, country, tag | Free |
get_station_now_playing(station_id) | The title one station announces now | Free |
get_now_playing_batch(station_ids[]) | Current titles for up to 100 stations (20 without a key) | Free |
get_station_history(station_id, from?, to?) | One station's past titles | Developer (10 days), Pro (30 days) |
search_tracks(q) | Catalogue tracks, trk_… ids | Free |
search_artists(q) | Catalogue artists, art_… ids | Free |
get_track_plays(track_id, from?, to?, country?) | Where and when a track was detected | Developer (10 days), Pro (30 days) |
get_artist_airplay(artist_id, group_by, from?, to?) | Plays per station, country or day | Pro |
create_wishlist(kind, value, station_filter?) | Records detections of an artist, track or query (poll delivery) | Developer |
list_wishlist_matches(wishlist_id) | Matches of one wishlist | Developer |
get_coverage_stats() | Coverage figures of the index | Free (no key needed) |
| Resource | What it returns | Plan |
|---|---|---|
tuneping://stats | Coverage stats, the same body as get_coverage_stats | Free (no key needed) |
tuneping://station/{id} | One station; now-playing stays a tool | Free (no key needed) |
The Free tools and both resources work without a key, limited per IP address. get_station_history and get_track_plays return at most 200 rows per call, newest first; a non-null next_cursor means older rows exist, so narrow from/to. get_artist_airplay returns up to 1,000 groups. create_wishlist is the only tool that writes, and it always uses poll delivery: an agent cannot create webhook delivery.
Ids: station ids are Radio Browser UUIDs; tracks trk_…, artists art_…, wishlists wl_…. Dates are RFC 3339 UTC (2026-11-01 or 2026-11-01T00:00:00Z), from inclusive, to exclusive.
Results of the Free tools and the resources carry an attribution member (Data by Tuneping, https://tuneping.com): show "Data by Tuneping" linking to https://tuneping.com when you display the data publicly. See attribution and fair use.
Configuration
The server reads three environment variables and nothing else.
| Variable | Default | Effect |
|---|---|---|
TUNEPING_API_KEY | unset | Optional. Sent as Authorization: Bearer. Without it the Free tools and both resources work; the Developer and Pro tools answer 401 unauthenticated without sending a request, naming the plan that unlocks them. Keys: sign-up page. |
TUNEPING_BASE_URL | https://api.tuneping.com/v1 | Absolute https URL including /v1. Plain http only for a loopback host (127.0.0.1, localhost), so the key never crosses a network unencrypted. |
TUNEPING_MCP_LOG | Warning | Log level on stderr (Warning, Information, Debug). Stdout carries JSON-RPC only; the key is never logged. |
An invalid TUNEPING_BASE_URL, or a TUNEPING_API_KEY with a space or a non-ASCII character (for example the placeholder tp_live_… pasted unchanged), stops the server at start with exit code 2 and the reason on stderr. tuneping-mcp --version prints the version; it is also sent as User-Agent: tuneping-mcp/<version>.
Client setup
Claude Desktop (claude_desktop_config.json):
{ "mcpServers": { "tuneping": { "command": "tuneping-mcp", "env": { "TUNEPING_API_KEY": "tp_live_…" } } } }
macOS GUI apps do not inherit the shell PATH: use the full path /Users/<you>/.dotnet/tools/tuneping-mcp; on Windows %USERPROFILE%\.dotnet\tools\tuneping-mcp.exe. If .NET is installed outside the default location, add "DOTNET_ROOT": "/Users/<you>/.dotnet" to the env block next to the key.
Claude Code:
claude mcp add tuneping --env TUNEPING_API_KEY=tp_live_… -- tuneping-mcp
or in a project's .mcp.json. Cursor: ~/.cursor/mcp.json or .cursor/mcp.json in the project:
{ "mcpServers": { "tuneping": { "command": "tuneping-mcp", "env": { "TUNEPING_API_KEY": "tp_live_…" } } } }
Without a key, leave out env (or --env) entirely: claude mcp add tuneping -- tuneping-mcp.
Errors
API problems come back as tool errors (isError) with two lines: a summary with the API's status and code and a one-line hint, then the raw problem JSON:
403 forbidden_tier: Your tier keeps 10 days of history; `from` may not be older than 2026-10-24T09:00:00Z. Hint: Unlock with the Pro plan (30 days of history and artist airplay reports); https://tuneping.com/#pricing.
{"type":"https://tuneping.com/docs/errors/#forbidden_tier","title":"Upgrade required","status":403,…,"required_tier":"pro"}
The codes are the API's: 401 unauthenticated, 403 forbidden_tier (with required_tier) and forbidden_scope, 404 not_found, 422 validation_failed (with errors[]), 429 rate_limited / quota_exceeded, 503 unavailable. Each is described in the error catalogue. Parameters are checked before any request with the API's own error codes, so a malformed id or date costs no quota. A network failure or timeout is 0 network_error.
A missing or ill-typed required argument (q absent, or limit as a string) gives the SDK's generic An error occurred invoking '<tool>'., because it is rejected before the tool runs; check the argument names and types against tools/list.
Resources have no tool error result: they answer a JSON-RPC error instead, -32002 for an unknown station (-32602 on protocol versions that report unknown resources as invalid parameters) and -32602 for a malformed id.
Rate limits
Every tool call is one API request (a retry is a second one) and counts against the key's rate limit and quota. Without a key the limits are per IP address: 2 requests per second with bursts of 5, 1,000 per day, batches of 20 stations.
A Retry-After of at most 10 seconds on a 429 or 503 is honoured once; nothing else is retried. A spent quota (quota_exceeded) is returned at once. The one write, create_wishlist, sends an Idempotency-Key, so its retry cannot create a second wishlist.
Set TUNEPING_MCP_LOG=Debug to log every API call (method, path and query, status, request id, any Retry-After wait) to stderr; most clients show it in their MCP log.