MCP Integration
Workflows

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:

  1. Call list_connections to find the TechReviews channel
  2. Call get_platform_capabilities("youtube") to confirm payload fields
  3. Convert "Friday 6 PM Kyiv time" → 2026-05-02T18:00:00+03:00
  4. Call create_assignment with "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):

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_assignment to fix the URL, then retry
  • Expired token — re-authorize the connection via get_oauth_connect_url with connectionId

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

ToolUse case
get_channel_statsCurrent subscriber count, total views, post count for a channel
get_post_statsCurrent 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

PlatformDefault if not specifiedSafe practice
YouTube"private" — video is hiddenAlways set "public" or "unlisted" explicitly
Facebook"EVERYONE" — post is publicSpecify "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