Skip to main content
Glama

tiktok_publish

Publish videos to TikTok using automated browser sessions. Upload video files, add captions, and include hashtags through authenticated Creator Center access.

Instructions

Publish a video to TikTok. Requires authenticated session. Uses browser automation via Creator Center.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
video_pathYesPath to the video file to upload
captionYesVideo caption/description
hashtagsNoList of hashtags (without #)

Implementation Reference

  • The publish_video method uses Playwright to navigate to the TikTok Creator Center, upload the specified video file, add a caption and hashtags, and click the post button.
    async def publish_video(
        self,
        video_path: str,
        caption: str,
        hashtags: list[str] = None,
    ) -> dict:
        """Publish a video to TikTok via Creator Center."""
        result = {"success": False}
    
        if not os.path.exists(video_path):
            result["error"] = f"Video file not found: {video_path}"
            return result
    
        try:
            page = await self.goto_tiktok("/creator-center/upload")
            await asyncio.sleep(5)
    
            # Check if redirected to login
            if "login" in page.url:
                result["error"] = "Not authenticated. Run tiktok_session with action=check_login first."
                return result
    
            # Find file input and upload
            file_input = await page.query_selector('input[type="file"]')
            if not file_input:
                # Sometimes hidden, try to find it
                file_input = await page.wait_for_selector('input[type="file"]', timeout=15000)
    
            if not file_input:
                result["error"] = "Could not find upload input"
                return result
    
            await file_input.set_input_files(os.path.abspath(video_path))
            logger.info(f"Uploaded video file: {video_path}")
    
            # Wait for processing
            await asyncio.sleep(15)
    
            # Find caption/description field
            caption_field = await page.query_selector('div[contenteditable="true"]')
            if not caption_field:
                caption_field = await page.wait_for_selector(
                    'div[contenteditable="true"]', timeout=20000
                )
    
            if caption_field:
                # Clear existing text
                await caption_field.click()
                await page.keyboard.press("Control+a")
                await page.keyboard.press("Backspace")
                await asyncio.sleep(0.5)
    
                # Type caption
                await caption_field.type(caption, delay=random.randint(20, 50))
    
                # Add hashtags
                if hashtags:
                    await caption_field.type("\n\n", delay=50)
                    hashtag_text = " ".join(f"#{tag.lstrip('#')}" for tag in hashtags)
                    await caption_field.type(hashtag_text, delay=random.randint(20, 50))
    
                await asyncio.sleep(3)
    
            # Dismiss tutorial overlay if present
            try:
                overlay = await page.query_selector('[class*="joyride"], [class*="overlay"]')
                if overlay:
                    await overlay.click()
                    await asyncio.sleep(1)
            except:
                pass
    
            # Click Post button
            post_selectors = [
                'button:has-text("Post")',
                'button:has-text("Publier")',
                '[data-e2e="post"]',
                'button[class*="submit"]',
            ]
    
            post_btn = None
            for sel in post_selectors:
                try:
                    post_btn = await page.query_selector(sel)
                    if post_btn and await post_btn.is_visible() and await post_btn.is_enabled():
                        break
                    post_btn = None
                except:
                    continue
    
            if not post_btn:
                result["error"] = "Could not find Post button"
                return result
    
            await post_btn.click()
            await asyncio.sleep(5)
    
            # Handle "Continue to post?" dialog
            for _ in range(12):
                try:
                    post_now = await page.query_selector(
                        'button:has-text("Post now"), button:has-text("Publier maintenant")'
                    )
                    if post_now and await post_now.is_visible():
                        await post_now.click()
                        await asyncio.sleep(3)
                except:
                    pass
    
                # Check for success
                if "/content" in page.url:
                    result["success"] = True
                    result["message"] = "Video published successfully"
                    return result
    
                # Check for success message
                success_el = await page.query_selector(
                    ':has-text("Your video is"), :has-text("being uploaded")'
                )
                if success_el:
                    result["success"] = True
                    result["message"] = "Video upload in progress"
                    return result
    
                await asyncio.sleep(10)
    
            result["error"] = "Publish timed out — no confirmation received"
    
        except Exception as e:
            result["error"] = str(e)
    
        return result
  • The server-side handler that receives the 'tiktok_publish' tool call and executes the browser.publish_video() method.
    elif name == "tiktok_publish":
        result = await browser.publish_video(
            arguments["video_path"],
            arguments["caption"],
            arguments.get("hashtags", []),
        )
        return [TextContent(type="text", text=json.dumps(result, indent=2, ensure_ascii=False))]
  • Definition of the 'tiktok_publish' tool in the TOOLS list, including input schema.
    Tool(
        name="tiktok_publish",
        description="Publish a video to TikTok. Requires authenticated session. Uses browser automation via Creator Center.",
        inputSchema={
            "type": "object",
            "properties": {
                "video_path": {"type": "string", "description": "Path to the video file to upload"},
                "caption": {"type": "string", "description": "Video caption/description"},
                "hashtags": {
                    "type": "array",
                    "items": {"type": "string"},
                    "description": "List of hashtags (without #)",
                },
            },
            "required": ["video_path", "caption"],
        },
    ),

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/follox42/tiktok-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server