Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Find and play music/videos via youtube_search + open_url
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-17 | ✗→✓ | ▲ Improved | 207% | 0% |
| case-18 | ✗→✓ | ▲ Improved | 257% | 0% |
| case-21 | ✗→✓ | ▲ Improved | 274% | 0% |
| case-22 | ✗→✓ | ▲ Improved | 231% | 0% |
| case-07 | ✓→✗ | ▼ Worse | 810% | 0% |
Resolve "play X" / "listen to X" / "find me a song" requests by chaining web_search (optional, for fuzzy intent) → youtube_search (REQUIRED, returns real watch URLs) → open_url (REQUIRED, must use a URL that came back from youtube_search). The user hears the song in their default browser without you having to drive a controlled browser session.
<execute_action> "Play X" is the authorization. You're not asking permission, you're confirming the action by doing it. Once youtube_search returns a URL that clears the rules below, fire open_url in the same turn — no deliberation step between picking and opening.
If any of these phrases form in your reply draft, you have stopped short of the action:
Replace the offer with the call: invoke open_url, then write the report. The user's default browser is the feedback channel — not your prose. </execute_action>
<selection_rule> How strict you should be about picking a candidate from youtube_search depends on how specific the user's query is.
VAGUE query — no specific song or artist named. Examples: "play me a popular song", "play me a hindi song", "play something upbeat", "put on some jazz", "play a sad song".
→ Walk the youtube_search candidates top-down and pick the first one whose title does NOT match any of the anti-patterns below. That is the answer. Do not score candidates further; do not look for "the most official one"; do not compare view counts. The first non-anti-pattern wins.
Anti-pattern keywords (match case-insensitively against title):
#shorts format)Why this matters: vague queries on YouTube often surface reaction clips or lyric videos near the top because they pull engagement. The user said "play a popular song" — they want to hear the song, not someone reacting to it. The anti-pattern filter is what keeps the vague rule from opening "the way they know every word! 🧡" instead of the actual track.
If every candidate in the top 5 matches at least one anti-pattern, that is a legitimate fallback: take the honest-fallback path (results page).
SPECIFIC query — the user named a song or artist. Examples: "play Bohemian Rhapsody", "play Despacito", "play Tum Hi Ho", "play Sabrina Carpenter".
→ Require a substring match between the candidate's title field and either the song title (preferred) or the artist name (acceptable when artist-only was given). Pick the highest-ranked candidate that matches. Then open. Do not keep looking once a match is found — the top match is the answer.
Honest fallback (youtube.com/results) is for one situation only: youtube_search returned zero candidates, OR every candidate is structurally wrong (channel page only, livestream when the user wanted a song). It is NOT for "results exist but they don't feel polished enough." If youtube_search returned a list and you can't pick one, the rule above failed — re-read it. </selection_rule>
Every successful run of this skill calls these tools, in order:
web_search — OPTIONAL pass 1: identify the specific songtitle + artist. Skip this when the user already named a song.
youtube_search — REQUIRED pass 2: get real /watch?v= URLsfor that specific song. Query: <title> <artist> official audio.
open_url — exactly once, with a URL that came back fromyoutube_search (verbatim — copy the url field from the tool result, do not retype it from memory).
Then you report. A run that stops after youtube_search is a FAILED run — the user does not hear anything until open_url fires. The runtime enforces both conditions:
without calling every tool in required_tools, your reply is discarded and you are asked to retry. Cap of 2 corrective retries.
open_url receives ayoutube.com/watch?v=<id> URL whose <id> was NOT returned by any youtube_search call this turn, the call is blocked before the browser opens, an error is fed back to you, and you are asked to call youtube_search and pick a real URL. Cap of 2 corrective retries; on exceed the user sees an honest-failure message, not a confabulated open.
Anti-patterns the planner sometimes drifts into — do NOT do these:
/watch?v=ID URL from memory or prose. The 11characters after v= are NEVER something you type — they are always copied verbatim from a youtube_search result. The provenance gate exists precisely because the model has been caught inventing IDs. Don't.
youtube_search. web_search returns prose snippetsand articles ABOUT songs, not autoplay-friendly URLs. youtube_search is the only tool whose results clear the provenance gate.
media-search skill mid-run after thesearches returned. The skill is already loaded; re-viewing wastes a turn and signals a hung agent. Run youtube_search, then open_url.
user's "play me a song" request is the consent — proceed to open_url.
youtube_search result whose title does notcontain the song you committed to. Artist-only match is not enough; "Alex Warren live stream" is not "Alex Warren — Ordinary."
"lyric", "cover", "reaction", "best of", "compilation", "mashup", "1 hour", or "loop". Those don't match what the user asked for.
The user's request matches any of these patterns:
"play a sad song"
cooking video about ramen"
If the user says "open spotify" or "open youtube" without a specific piece, that's an open_url direct call — not this skill.
The goal of pass 1 is to commit to a specific <title> and <artist> before you pick any URL. Build the query from what the user actually said:
| User said | Pass-1 query | |---|---| | "play me a popular song" | Billboard Hot 100 #1 this week | | "play me something upbeat" | top upbeat pop songs 2026 | | "play me a sad song" | top ballads 2026 spotify | | "play me kendrick lamar" | kendrick lamar most popular song 2025 | | "play me a hindi song" | bollywood top hindi songs this week | | "play me jazz" | jazz standards essential listening | | "play Despacito" | (skip pass 1 — title already given) | | "play Bohemian Rhapsody" | (skip pass 1 — title already given) |
After pass 1, announce your commitment in your reasoning (this is NOT yet shown to the user — that comes after open_url):
> Picked: <title> by <artist> (reason: e.g. "currently #1 on > Billboard Hot 100").
If the user already named a specific song, pass 1 is unnecessary — the title and artist are already given. Skip directly to pass 2.
youtube_search)Call youtube_search with a query of the form:
"<title>" <artist> official audioWrap the title in double quotes. youtube_search hits the public YouTube results page, parses real /watch?v= URLs out of the response, and returns a list of { videoId, url, title, channel, durationText? } objects. The URLs in that list are the only URLs the URL provenance gate will let open_url consume — anything else is blocked before the browser opens.
URL-selection rules (apply in order to the youtube_search results, not to web_search snippets):
title field MUST explicitly contain the songtitle you committed to in pass 1. Artist-only mention is not enough. If the title doesn't name the song, skip it.
title contains "Official Audio","Official Music Video", "Official Video", or "Official Lyric Video". These are usually the artist's verified upload.
title contains any of: "live stream","LIVE", "24/7", "lyric video" (unless specifically requested), "cover", "covered by", "reaction", "react to", "best of", "compilation", "mashup", "remix" (unless specifically requested), "hour" (e.g. "1 hour version"), "loop". These don't match what the user asked for.
the honest-fallback path (below). Do not loosen the rules to force a pick.
Call open_url with the url field of the chosen youtube_search result — copy it verbatim, do not retype. Exactly once, in the same turn that youtube_search returned. Don't print the URL and ask. Don't list the candidates. Don't pause for confirmation. YouTube's /watch?v= pages autoplay in the user's browser; the autoplay is the answer to "did it work?", not your prose. This step is REQUIRED.
The URL provenance gate validates open_url's url argument against this turn's youtube_search results before the browser launches. If you somehow type a watch URL that wasn't in the result list (you composed an ID, you reused one from training data, you typo'd while copying), the call is rejected, an error is fed back to you, and you are asked to try again with a real URL from youtube_search. The user's browser does not open until the URL clears the gate.
open_url has fired, not before)Format your reply so the user sees what you picked AND why. The title must be announced together with the URL in a single message that follows the open_url call, never as an offer before it:
> Picked: <title> by <artist> — <one-line reason>. Opened > <url>.
For a fuzzy intent ("popular song") the reason explains your substitution ("currently #1 on Billboard"). For a specific request ("play Despacito") the reason can be brief ("the official video").
If pass 2 returns no result that satisfies all the URL-selection rules above, the fallback executes in the same turn — it is not something you offer to the user.
open_url tohttps://www.youtube.com/results?search_query=<title>+<artist> (URL-encoded). Same turn. No "want me to?" question. No "let me know if".
open_url fires, report what executed:> Couldn't find a verified official upload for <title> by > <artist>. Opened > youtube.com/results?search_query=<title>+<artist> — autoplay > doesn't fire on results pages, so pick a tile to start > playback.
Do NOT claim "now playing" — that would be the same fabrication Phase 23.1 was built to catch. Do NOT ask "should I open the search page?" — that's the offer-style anti-pattern Phase 23.3 specifically forbids.
User prompt: "play me a popular song"
The <selection_rule> block says: vague query → walk youtube_search candidates top-down, skip anti-patterns, take the first clean one. No officialness scoring beyond that.
Expected flow:
web_search to commit to a currentchart-topper (e.g. "Opalite" by Taylor Swift). This is for query construction, not for filtering YouTube results.
youtube_search("Opalite Taylor Swift") →returns 5 candidates:
{ title: "the way they know every word! 🧡 Opalite by Taylor Swift", channel: "Krystal" } — title contains "they know every word" → anti-pattern match, skip.{ title: "Taylor Swift - Opalite (Official Audio)", channel: "Taylor Swift" } — no anti-pattern keyword → first clean candidate, pick.open_url(<candidate 2 .url>) — copy the urlfield verbatim. The provenance gate sees the id in the ledger, lets the call through.
<url>."
If every candidate in the top 5 hits an anti-pattern keyword — e.g. all reactions, all lyric videos — that is the legitimate fallback case (Example 5). Do not loosen the anti-pattern filter to force a pick.
User prompt: "play Tum Hi Ho"
The <selection_rule> block says: specific query → require a title or artist substring match in the candidate before opening.
Expected flow:
web_search.youtube_search("Tum Hi Ho") → list of candidates.whose title field contains "Tum Hi Ho" (case-insensitive substring is fine). The highest-ranked match is the answer — not "the most official-looking one further down the list."
open_url(<that result's url, verbatim>).<url>."If the very top result's title contains "Tum Hi Ho", that's the match. Stop looking. Open it.
User prompt: "play Despacito"
Expected flow:
web_search.youtube_search("Despacito Luis Fonsi") → topresult's title contains "Despacito". Match found at rank 1.
open_url(<top result.url>).<url>."
User prompt: "play me kendrick lamar"
This is between vague and specific: the artist is named, so "Not Like Us by random tribute act" is wrong, but the song isn't — so the bar is "any candidate whose channel or title is Kendrick Lamar's actual catalog."
Expected flow:
2025") to commit to a likely song (e.g. "Not Like Us"). This step is optional but useful for query construction.
youtube_search("Kendrick Lamar Not Like Us") →list of candidates.
title or channel namesKendrick Lamar. Top result usually qualifies.
open_url(<that result's url, verbatim>).<url>."
User prompt: "play that lo-fi beats stream"
Expected flow:
web_search("popular lo-fi beats stream 2026") → topresult is "lofi hip hop radio — beats to relax/study to" channel.
audio") → top results are channel pages and livestreams. Every watch URL whose title contains "live" or "24/7" fails reject rule
open_url("https://www.youtube.com/results?search_query=lofi+hip+hop+radio+Lofi+Girl") — this is a /results? URL, not a /watch?v= URL, so the provenance gate does not apply (it's only scoped to watch URLs).
Lofi Girl's stream is a 24/7 livestream, not a single video. Opened YouTube search results; click any livestream tile to start playback."
open_url more than once per request. If the firstcall succeeded, the URL is open in the user's browser. Re-launching is duplicate noise.
/watch?v= autoplays; /results? and /c/ do NOT. Only claim"now playing" when you launched a watch URL.
youtube_search is required, not optional. Skipping it meansthe URL provenance gate has no candidates and any open_url call to a watch URL gets blocked. The gate is structural — there is no way to bypass it from the prompt side.
web_search (pass 1) is optional, but valuable for fuzzyintents. It helps you commit to a specific title before youtube_search. For "play Despacito" you can skip it; for "play me a popular song" you almost certainly need it.
returns articles ABOUT popular songs, not the songs themselves. The whole point of pass 1 is to substitute fuzzy → specific BEFORE youtube_search.
youtube_search result'stitle doesn't literally contain the song title you committed to, that result is not the song you said you'd play. Skip it. If nothing matches, take the honest fallback.
Spotify ("play X on spotify"), tell them: "Spotify integration ships in v4.1. For now I can open the song on YouTube instead — want me to?"
web_search tool (always available — registered at boot)youtube_search tool (always available — registered at boot)open_url tool (always available, BUILTIN_SAFE_TOOLS, auto-approves)Other measured skills in the registry, with their headline benchmark lift.