diff --git a/mcp/worker.js b/mcp/worker.js index bf78b3c..69b38eb 100644 --- a/mcp/worker.js +++ b/mcp/worker.js @@ -9,7 +9,7 @@ const MEDIA = "https://dev.gandora.lol"; const ORIGIN = "https://dev.gandora.lol"; const TRACKS = { sky: "/asmr/audio/kokoro-sky.mp3", rain: "/asmr/audio/bed-rain.mp3" }; const PROBE_URI = "ui://gandora/probe"; -const RADIO_URI = "ui://gandora/radio-v3"; +const RADIO_URI = "ui://gandora/station"; const UI_MIME = "text/html;profile=mcp-app"; const UI_META = { ui: { csp: { resourceDomains: [MEDIA], connectDomains: [MEDIA] }, prefersBorder: true } }; const APP_ONLY = { ui: { visibility: ["app"] } }; @@ -36,6 +36,10 @@ const TOOLS = [ description: "Shows the gandora audio probe player in chat (an MCP App).", inputSchema: { type: "object", properties: {} }, _meta: { ui: { resourceUri: PROBE_URI }, "ui/resourceUri": PROBE_URI } }, + { name: "dj_station", title: "Open a gandora station", + description: "Preferred over dj_play. You are the gandora DJ: write the opening track sheet plus a station theme. A self-running station card appears in chat: it renders and plays your track, then keeps the station going by itself (a small DJ model writes each following sheet, steered by the listener's Like / Not this / Next), with no messages to the chat. Its play log reaches you as model context. Call again only for a new station or a change of direction.", + inputSchema: SHEET, + _meta: { ui: { resourceUri: RADIO_URI }, "ui/resourceUri": RADIO_URI } }, { name: "dj_play", title: "Play a track on gandora", description: "You are the gandora DJ. Write the opening track sheet (and a station theme) and put it on air: a player card appears in chat, renders the song (ACE-Step, ~15-60 s), speaks your dj_line, then plays. The card then keeps the station going on its own (a small DJ model writes the following sheets in your style, steered by the listener's Like / Not this / Next), so you do not need to call dj_play per track. Its play log and reactions reach you as model context. Call dj_play again only when the listener asks you for a change of direction or a new station.", inputSchema: SHEET, @@ -78,7 +82,7 @@ async function callTool(name, a, env) { const r = await fetch(MEDIA + path); if (!r.ok) return fail("fetch " + r.status); return { content: [{ type: "text", text: "clip" }], structuredContent: { id: a.id || "sky", mimeType: "audio/mpeg", data: b64(await r.arrayBuffer()) } }; } - if (name === "dj_play") { + if (name === "dj_play" || name === "dj_station") { const sheet = { ...a, seconds: clamp(a.seconds, 30, 120, 60), seed: hash32(a.title + "|" + a.tags) }; return { content: [{ type: "text", text: `On air: "${a.title}" (${sheet.seconds}s). The card is rendering it now; wait for a [gandora] message before playing the next one.` }], structuredContent: sheet }; @@ -143,7 +147,7 @@ async function handle(msg, env) { case "resources/templates/list": return ok({ resourceTemplates: [] }); case "resources/read": if (params && params.uri === PROBE_URI) return ok({ contents: [{ uri: PROBE_URI, mimeType: UI_MIME, text: PROBE_HTML, _meta: UI_META }] }); - if (params && (params.uri === RADIO_URI || params.uri === "ui://gandora/radio")) return ok({ contents: [{ uri: RADIO_URI, mimeType: UI_MIME, text: RADIO_HTML, _meta: UI_META }] }); + if (params && (params.uri === RADIO_URI || params.uri === "ui://gandora/radio" || params.uri === "ui://gandora/radio-v3")) return ok({ contents: [{ uri: RADIO_URI, mimeType: UI_MIME, text: RADIO_HTML, _meta: UI_META }] }); return err(-32002, "resource not found"); default: if (id === undefined) return null;