Workflows
End-to-end patterns for common tasks with the Pharlo MCP server. Each workflow shows the tool sequence, example prompts, and SDK code for custom agents.
For tips on phrasing prompts effectively, see Prompt Engineering Tips.
Publish content
The standard pattern for publishing a video or post.
Check platform capabilities
Call get_platform_capabilities to discover supported content types, payload fields, and file size limits for the target platform.
Find the channel
Call list_connections to find the connectionId of the channel to publish to. If no channel is connected yet, see Connect a new channel below.
Create the assignment
Call create_assignment with connectionId, type, mediaUrl, payload (title, description, privacy), and optionally scheduledAt.
Poll status
Call get_assignment to check the status. A successful publish moves through pending → uploading → uploaded → processing → published.
Example prompt:
"Upload https://cdn.example.com/demo.mp4 (opens in a new tab) to my 'TechReviews' YouTube channel. Title: 'Product Demo v3', make it public, schedule for Friday at 6 PM Kyiv time."
Claude will:
- Call
list_connectionsto find the TechReviews channel - Call
get_platform_capabilities("youtube")to confirm payload fields - Convert "Friday 6 PM Kyiv time" →
2026-05-02T18:00:00+03:00 - Call
create_assignmentwith"privacy": "public"explicitly set
YouTube's default privacy is private. Always say "public", "unlisted", or "private" explicitly in your prompt — otherwise the video uploads but stays hidden. See Privacy defaults.
SDK example:
import anthropic
client = anthropic.Anthropic()
response = client.beta.messages.create(
model="claude-opus-4-7",
max_tokens=1024,
betas=["mcp-client-2025-04-04"],
mcp_servers=[
{
"type": "url",
"url": "https://pharlo.io/_mcp",
"authorization_token": "mcp_at_your_token",
}
],
messages=[
{
"role": "user",
"content": (
"Upload https://cdn.example.com/demo.mp4 to my 'TechReviews' YouTube channel. "
"Title: 'Product Demo v3', public, schedule for 2026-05-02T18:00:00+03:00."
),
}
],
)
for block in response.content:
if hasattr(block, "text"):
print(block.text)For custom agents, pass scheduled times as explicit ISO 8601 strings with timezone offsets (e.g. 2026-05-02T18:00:00+03:00). When prompting Claude interactively, plain language like "Friday at 6 PM Kyiv time" works — Claude converts it automatically.
Schedule multiple videos in one prompt
Give Claude the full list upfront. It plans all the tool calls before executing, and can confirm each before proceeding.
Example prompt:
"Schedule 3 videos to my 'TechReviews' channel, all public, all Kyiv time (UTC+3):
- Monday at 10 AM: https://cdn.example.com/v1.mp4 (opens in a new tab) — 'April Launch'
- Wednesday at 10 AM: https://cdn.example.com/v2.mp4 (opens in a new tab) — 'Behind the Scenes'
- Friday at 6 PM: https://cdn.example.com/v3.mp4 (opens in a new tab) — 'Q2 Recap'
Confirm each before creating."
Claude calls create_assignment three times in sequence, with scheduledAt offsets calculated for each.
SDK example — batch scheduling:
import anthropic
client = anthropic.Anthropic()
batch = [
{"title": "April Launch", "url": "https://cdn.example.com/v1.mp4", "at": "2026-04-28T10:00:00+03:00"},
{"title": "Behind the Scenes", "url": "https://cdn.example.com/v2.mp4", "at": "2026-04-30T10:00:00+03:00"},
{"title": "Q2 Recap", "url": "https://cdn.example.com/v3.mp4", "at": "2026-05-02T18:00:00+03:00"},
]
lines = "\n".join(
f"- {item['at']}: {item['url']} — title '{item['title']}', public"
for item in batch
)
response = client.beta.messages.create(
model="claude-opus-4-7",
max_tokens=2048,
betas=["mcp-client-2025-04-04"],
mcp_servers=[
{
"type": "url",
"url": "https://pharlo.io/_mcp",
"authorization_token": "mcp_at_your_token",
}
],
messages=[
{
"role": "user",
"content": (
"Schedule these 3 videos to my 'TechReviews' YouTube channel:\n"
+ lines
),
}
],
)
for block in response.content:
if hasattr(block, "text"):
print(block.text)Connect a new channel
Connecting a channel requires the user to complete an OAuth screen in their browser. Keep this as a separate task from publishing.
Get the OAuth URL
Call get_oauth_connect_url with the target platform. The response includes a url to open and a ticketId to poll with.
User completes OAuth
The user opens the URL in their browser, selects their Google/Facebook account, and grants permissions on the consent screen.
Confirm it appears
Call list_connections and look for the new channel. The ticket has a 10-minute lifetime; if the user takes longer, repeat from step 1.
Example prompt:
"I need to connect my second YouTube channel to Pharlo."
Claude calls get_oauth_connect_url("youtube") and returns a URL. After you complete the consent screen, it calls list_connections to confirm the new channel appears.
To re-authorize an existing channel with an expired token, pass connectionId to get_oauth_connect_url. This refreshes the token without losing the connection's history or settings.
SDK example:
import anthropic
client = anthropic.Anthropic()
mcp = {
"type": "url",
"url": "https://pharlo.io/_mcp",
"authorization_token": "mcp_at_your_token",
}
response = client.beta.messages.create(
model="claude-opus-4-7",
max_tokens=512,
betas=["mcp-client-2025-04-04"],
mcp_servers=[mcp],
messages=[{"role": "user", "content": "Get me an OAuth URL to connect a new YouTube channel."}],
)
print(response.content[0].text)
# Claude prints the URL — user opens it in their browser and completes consent.
# After they return, ask Claude to list connections to confirm the new channel appeared.Retry a failed assignment
Inspect the failure
Call get_assignment to read the errors array. Common causes: quota exceeded, bad media URL, expired channel token.
Fix the root cause
- Quota exceeded — wait and retry later
- Bad media URL — update the assignment with
update_assignmentto fix the URL, then retry - Expired token — re-authorize the connection via
get_oauth_connect_urlwithconnectionId
Retry
Call retry_assignment. The assignment is re-queued without creating a duplicate.
Example prompt:
"My last upload failed. Check why and retry it if it makes sense."
Claude calls get_assignment to read the error, explains the root cause, and calls retry_assignment if the issue is transient.
SDK example:
import anthropic
client = anthropic.Anthropic()
response = client.beta.messages.create(
model="claude-opus-4-7",
max_tokens=512,
betas=["mcp-client-2025-04-04"],
mcp_servers=[
{
"type": "url",
"url": "https://pharlo.io/_mcp",
"authorization_token": "mcp_at_your_token",
}
],
messages=[
{
"role": "user",
"content": "Check assignment a1b2c3d4-e5f6-7890-abcd-ef1234567890 — it failed. What went wrong and should I retry it?",
}
],
)
for block in response.content:
if hasattr(block, "text"):
print(block.text)Check analytics
Pharlo exposes two live analytics tools — one for a whole channel, one for a single post. Both call the platform API in real time. See Analytics REST reference for the underlying API.
Pick the right tool
| Tool | Use case |
|---|---|
get_channel_stats | Current subscriber count, total views, post count for a channel |
get_post_stats | Current views, likes, comments, shares for a published post |
Get the IDs
For channel stats you need a connectionId — get it from list_connections. For post stats you also need a postId — get it from list_assignments with status: "published".
Request the stats
Each call hits the platform API and counts against your Pharlo bill. Cache the result in your app if you display it in a frequently refreshed UI.
Example prompts:
"How many subscribers does my 'TechReviews' channel have right now?"
"How is my latest YouTube video performing?"
"Get current view and like counts for the videos I uploaded this week."
SDK example — channel snapshot:
import anthropic
client = anthropic.Anthropic()
response = client.beta.messages.create(
model="claude-opus-4-7",
max_tokens=1024,
betas=["mcp-client-2025-04-04"],
mcp_servers=[
{
"type": "url",
"url": "https://pharlo.io/_mcp",
"authorization_token": "mcp_at_your_token",
}
],
messages=[
{
"role": "user",
"content": "Give me the current subscriber count and total views for all my YouTube channels.",
}
],
)
for block in response.content:
if hasattr(block, "text"):
print(block.text)Timezone rules
Always pass scheduled times with an explicit timezone offset.
| ✅ Correct | ❌ Wrong |
|---|---|
"2026-04-25T18:00:00+03:00" | "2026-04-25T18:00:00" (no offset — ambiguous) |
"2026-04-25T15:00:00Z" | "18:00 Friday" (natural language in API calls) |
When prompting Claude interactively, natural language works — "6 PM Cyprus time" is converted automatically. In SDK code, always use ISO 8601 with an explicit offset.
Privacy defaults
| Platform | Default if not specified | Safe practice |
|---|---|---|
| YouTube | "private" — video is hidden | Always set "public" or "unlisted" explicitly |
"EVERYONE" — post is public | Specify "SELF" or "FRIENDS" if needed |
Common pitfalls
Ambiguous channel name
❌ "Publish this video to YouTube"
✅ "Publish this video to my 'TechReviews' YouTube channel"
If you have multiple channels and don't name one, Claude may ask for clarification or pick the wrong channel. Ask "What channels do I have connected?" first if unsure.
Non-direct media URL
Video assignments require a direct download URL — not a Google Drive or Dropbox share link, which open a web page instead of the raw file. Use a CDN, S3 presigned URL, or any public direct-download link.
Scheduled time in the past
Assignments with a scheduledAt in the past are rejected immediately with a validation error. Double-check the timezone offset if you hit this.
Combining connect + publish in one prompt
Connecting a channel requires a browser OAuth step — it can't run inline with publishing. Run connection setup as a separate task first, then publish. See Connect a new channel.
Example conversation
You: List my YouTube channels and show me their subscriber counts.
Claude: (calls list_connections(platform: "youtube") and get_channel_stats for each)
You have 2 YouTube channels:
- TechReviews — 12,400 subscribers
- Behind The Scenes — 3,200 subscribers
You: Upload https://cdn.example.com/demo.mp4 (opens in a new tab) to TechReviews. Title: "Product Demo v3", public, schedule for tomorrow at 10 AM Berlin time.
Claude: (calls create_assignment with scheduledAt: "2026-04-28T10:00:00+02:00" and "privacy": "public")
Assignment created — ID: a1b2c3d4-e5f6-7890-abcd-ef1234567890. Scheduled for April 28 at 10:00 AM Berlin time (08:00 UTC).
You: What's the status?
Claude: (calls get_assignment("a1b2c3d4-..."))
Status: published YouTube URL: https://youtu.be/dQw4w9WgXcQ (opens in a new tab) Published at 10:02 AM Berlin time.
You: How is it doing? Give me the current views.
Claude: (calls get_post_stats(connectionId, postId: "a1b2c3d4-..."))
The video has 1,820 views, 142 likes, and 23 comments so far.
See also
- Tool Reference — all 15 tools with parameters and annotations
- Prompt Engineering Tips — how to phrase prompts for reliable results
- Connection Setup — connecting Claude.ai or building a custom agent
- MCP Overview — how MCP works, credit surcharge, supported clients
- Assignments REST reference — direct REST API for automation