Search YouTube and manage videos, playlists, comments and subscriptions through the YouTube Data API v3.
YouTube ships in the w6w first-party pack. It declares 16 actions, 2 health checks, and the host runs its code in a sandbox that never sees the credential.
io.w6w.youtubeYouTube gives a workflow the same reach into a channel that its own Studio has — reacting to comments, keeping playlists current, and rating and updating videos — plus public search and metadata lookups that need no account connection at all, through the YouTube Data API.
Videos can be searched, read, updated, rated and deleted, with playlists created, updated, deleted and populated one item at a time. Comment threads on a video or channel can be listed and replied to, and subscriptions read back for a connected account. Read-only calls can also run against public data alone, on an API key with no user connection at all, when a workflow just needs search results or public metadata.
It fits content-operations and reporting workflows — reacting to a new comment, keeping a playlist in sync with a content calendar, or pulling public video metadata for analysis — for a channel owner connected through OAuth, or for public lookups that need no account at all. Video upload is not available: YouTube’s own upload protocol needs a stateful multi-request session this app’s sandboxed execution model cannot hold.
Three routes to the same 16 actions. The Workflow tab is generated from YouTube's own manifest and carries its real ids, so it is copy-pasteable; the Code and CLI examples are the same call for any action on any app, so every app-specific value in them is a blank you fill in.
add-playlist-item Add a video to a playlist. Costs 50 quota units. Not idempotent — adding the same video twice creates two entries.
create-playlist Create a playlist on the authenticated user's channel. Costs 50 quota units. Not idempotent — each call creates a new playlist.
delete-playlist Delete a playlist owned by the authenticated user. Costs 50 quota units. Removes the playlist only — the videos it contained are not affected.
delete-video Permanently delete a video owned by the authenticated user. Costs 50 quota units. Irreversible — there is no undelete.
get-channels Fetch channel detail by ID, @handle, legacy username, or the authenticated user's own channel. Costs 1 quota unit. Request the contentDetails part to get the uploads playlist ID.
get-videos Fetch full detail for up to 50 videos by ID, or list the most-popular chart, or the authenticated user's liked/disliked videos. Costs 1 quota unit regardless of how many IDs are requested.
list-comment-threads List comment threads on a video or channel. Costs 1 quota unit per page. Requires the youtube.force-ssl scope — no other scope can read comments.
list-playlist-items List the entries in a playlist. Costs 1 quota unit per page. Each item's `id` is the membership ID needed to remove it — not the video ID, which is at snippet.resourceId.videoId.
list-playlists List playlists by ID, by channel, or the authenticated user's own. Costs 1 quota unit. Does not include the system uploads playlist — get that ID from Get Channels with the contentDetails part.
list-subscriptions List the channels the authenticated user subscribes to, a public channel's subscriptions, or a partial list of the user's own subscribers. Costs 1 quota unit per page. Requires an OAuth connection for the `mine` and `mySubscribers` filters.
rate-video Like, dislike, or remove the authenticated user's rating on a video. Costs 50 quota units. Use rating `none` to remove an existing rating.
remove-playlist-item Remove an entry from a playlist. Costs 50 quota units. Takes the playlist ITEM ID, not the video ID — find it with List Playlist Items.
reply-to-comment Post a reply to an existing top-level comment. Costs 50 quota units. Requires the youtube.force-ssl scope. This creates replies only — new top-level comments use a different endpoint that this app does not implement.
search Search YouTube for videos, channels and playlists. Costs 1 quota unit but is limited to 100 calls per day in a separate bucket, and each page is another call. Returns pointers — use Get Videos to fetch full video detail.
update-playlist Update a playlist's title, description, tags or privacy. Costs 50 quota units. Destructive — fields omitted from a part you name are CLEARED, and the API requires the title on every update.
update-video Update a video's metadata. Costs 50 quota units. Destructive by design — any field you leave blank inside a part you name is CLEARED, so read the video first and send back complete parts.
A workflow step names the app and the action, and the editor fills in the
connection when you pick one. This is the Step shape from the
workflow spec, carrying YouTube's real ids.
{
"manifestVersion": "2",
"name": "youtube-example",
"steps": [
{
"id": "add-playlist-item",
"uses": {
"app": "io.w6w.youtube",
"action": "add-playlist-item",
"connection": "conn_YOUR_CONNECTION_ID"
},
"with": {
"part": "<part>",
"playlistId": "<playlistId>",
"videoId": "<videoId>"
}
}
]
}add-playlist-item create-playlist get-channels get-videos list-comment-threads +11 more actions available
Every app-specific value here is a blank you have to fill in. An
app action is reached through the connection that authenticates it, so the
address is a connection id, not the app id — and connections belong to your account,
so a public page cannot know yours. Create one for YouTube, then fill in
the three blanks: conn_YOUR_CONNECTION_ID, the action key, and the
parameters that action declares. The call itself is real — the shape is transcribed
from the studio's own snippet builder, which prints the same kind of blanks — but
nothing in it is specific to YouTube. The Workflow tab is where this app's
real ids are.
npm install @w6w/sdkyarn add @w6w/sdkpnpm add @w6w/sdkdeno add npm:@w6w/sdkimport { W6wClient, isActionRun } from "@w6w/sdk";
// Reads W6W_BASE_URL and W6W_TOKEN from the environment when omitted.
const client = new W6wClient();
const envelope = await client.run({
urn: "conn_YOUR_CONNECTION_ID",
action: "create-playlist",
payload: {
part: "<value>",
title: "<value>",
// description: "<value>",
// tags: "<value>",
// defaultLanguage: "<value>",
// privacyStatus: "<value>",
},
});
if (isActionRun(envelope)) console.log(envelope.value); npm install -g @w6w/cli w6w run conn_YOUR_CONNECTION_ID --action create-playlist --payload '{"part":"<value>","title":"<value>"}' Give an AI agent YouTube — without giving it YouTube's credentials. One MCP endpoint exposes every app, function and workflow the caller is entitled to, as tools it can discover and run. Access is granted per team while we onboard.
One tool call{
"name": "w6w_invoke",
"arguments": {
"ref": "app:io.w6w.youtube#add-playlist-item",
"input": {
"part": "<part>",
"playlistId": "<playlistId>",
"videoId": "<videoId>"
}
}
}
Every tool names its target with a single ref. The
app: form above doesn't name a connection at all — the
host resolves which of the caller's YouTube connections to sign
with, and refuses rather than guesses when the answer is ambiguous.
The token is attached host-side, at the moment of the call. It is never a tool argument, never in the model's context, and never in a transcript — so a prompt injection has nothing to exfiltrate.
Tools are derived per end user from what that person has actually connected and is entitled to — not one shared bot identity carrying the union of everyone's access.
Multi-step work runs on the workflow engine and returns a run handle the agent can poll — retries, branching and state survive the conversation that started them.
YouTube's declared health checks are on the surface too, so an agent can tell "the vendor is down" from "your credential expired" before it burns a retry on either.
The MCP surface is part of the hosted platform. YouTube itself is MIT, and the runtime that executes it is source-available (FSL).
YouTube declares its own checks, so its health is a property of the app rather than something the host guesses at.