MCP Radio (mcp-radio.js)
An MCP (Model Context Protocol) stdio server that lets any AI agent — Claude,
opencode, Cursor, etc. — control the whole radio station over its HTTP
/api/station/* API. Zero runtime dependencies; runs on plain Node.js.
Running it
node mcp-radio.js # or: npm run mcp
load-env.js (zero-dep) loads <module dir>/.env into process.env (existing
env wins) at the top of both mcp-radio.js and ai-providers.js. The file
ships with all provider vars commented; STATION_URL / STATION_KEY also live
there.
Registering with opencode (verified 2026-08-07)
Add to your project opencode.json (project scope; restart opencode to load):
{
"mcp": {
"loklok-radio": {
"type": "local",
"command": ["node", "mcp-radio.js"]
}
}
}
Verified end-to-end: initialize → tools/list (28 tools) → station_status →
ping all pass; injecting OPENAI_API_KEY into .env flips ai_list_providers
configured:false→true and ai_chat reaches the provider's real API.
Connection settings
Read from env (or station-ai.json, the AI config file shared with
ai-providers.js):
| Env | Default | Meaning |
|---|---|---|
STATION_URL | http://127.0.0.1:5000 | The running server.js |
STATION_KEY | '' | Must match the server's STATION_KEY, if any (MCP passes x-station-key when set) |
Server auth is optional — STATION_KEY unset = open routes.
AI providers (ai-providers.js)
Zero-dep multi-provider LLM client: openai, anthropic, gemini, groq,
openrouter, deepseek, xai, cohere, together, fireworks, perplexity, cerebras.
Key resolution: env <PROVIDER>_API_KEY → AI_API_KEY → station-ai.json
(AI_CONFIG_PATH). station-ai.json is a full 12-provider template with empty
apiKey placeholders + stationUrl/stationKey; its config is TTL-cached
(5s) so filling in a key applies to a running MCP server without a restart.
Exposes chat() + listProviders().
Tools
| Tool | Action |
|---|---|
station_status | Full live snapshot (now playing, queue, rotation, decks, genre split, weights, dayparts, singers, queries, mood) — always call first |
set_genre_split | Set ARABIC_HITS / EGYPTIAN_TRENDS / ARAB_TECHNO / ENGLISH probabilities (relative, need not sum to 1), persisted |
set_weights | MASHUP_CHANCE, STOCHASTIC_SFX_CHANCE |
set_rotation | BUFFER_MAX, REFRESH_FRACTION, MUSIC_FRESH_MS, ELEMENT_COOLDOWN, COOLDOWN_PERIOD |
set_dayparts / clear_dayparts | Replace/clear custom time-of-day windows ({start,end,hard?,weights?}, wraps when start>end) |
set_mood | Preset (`chill |
set_config | Generic deep patch of the scheduler config |
list_singers / set_singers | Curated singer catalogs per category (mode `replace |
set_queries | Trend-search fallback queries per category |
fetch_content | Targeted download for a category ({category,target,kind}; shorts → TikTok transcoder) |
scrub_content | Archive junk (award shows, lyric videos, news…) from a deck |
live_status / live_register / live_remove / live_vote / live_start / live_stop | Full live-broadcast control (rank, vote, take the channel live, return to mix) |
refresh_content | Full refresh (all categories + sweepers/sfx/mashups + rotation rebuild/push) |
rebuild_rotation / skip_track / force_play | Rotate / skip / force a category to the head |
save_config / reset_config | Persist current config / wipe station-config.json |
station_log | Tail content or server log |
ai_list_providers / ai_chat | Multi-provider LLM calls (OpenAI, Anthropic, Gemini, Groq, OpenRouter, DeepSeek, xAI, Cohere, Together, Fireworks, Perplexity, Cerebras…) via ai-providers.js |
Protocol
MCP stdio = newline-delimited JSON-RPC 2.0. Implements initialize, ping,
resources/list, tools/list, tools/call.
See also
- Station control — the HTTP API these tools wrap.
- API endpoints — the
/api/station/*route table.