Social media MCP: publish, schedule & analyze posts on TikTok, Instagram, YouTube, LinkedIn & X
com.upload-post/mcp (Social Media MCP)
This Model Context Protocol (MCP) server enables MCP-compatible AI agents to publish, schedule, and analyze social media posts across TikTok, Instagram, YouTube, LinkedIn, and X. It is the official MCP server for Upload-Post.
๐ ๏ธ Key Features
Publish posts
Schedule posts
Analyze posts
Supports TikTok, Instagram, YouTube, LinkedIn, and X
๐ Use Cases
Multi-platform social media publishing workflows
Automated scheduling of content to multiple networks
Post performance review and analysis for supported platforms
โก Developer Benefits
Integrates with MCP-compatible agents (e.g., Claude Desktop, Claude Code, Cursor)
Centralized access to social media operations via a single MCP server
Package available as @upload-post/mcp
โ ๏ธ Limitations
Only described capabilities are publish, schedule, and analyze; no other features are specified
Supported platforms are limited to TikTok, Instagram, YouTube, LinkedIn, and X
Lets any MCP-compatible AI agent (ChatGPT, Claude Desktop, Claude Code, Cursor, โฆ) publish, schedule, analyze and manage social media across TikTok, Instagram, YouTube, LinkedIn, Facebook, Pinterest, Threads, Reddit, Bluesky, X, Google Business, Discord, Telegram and more with a single API key.
Built on top of the official upload-post SDK and the public Upload-Post REST API.
Use it in ChatGPT (no setup)
Upload-Post is a reviewed app in the ChatGPT app directory. Add Upload-Post in ChatGPT โ or search for Upload-Post under Apps โ click Connect and sign in with OAuth. No Developer mode, no MCP URL to paste, and you get the Upload Studio widget for publishing video files straight from your computer.
Prefer to wire it up by hand? Enable Developer mode in Settings โ Apps โ Advanced settings, click Create app, point it at https://mcp.upload-post.com/mcp and set authentication to OAuth.
Two ways to run it yourself
A) Local stdio (single-user) โ simplest
The server runs on your machine, spawned by the MCP client. Add to ~/.claude/mcp.json (or Cursor settings, etc.):
Get your API key at https://app.upload-post.com โ API Keys. Restart the client โ you should see 58 upload-post tools.
B) Hosted HTTP (multi-tenant) โ share one server with many users
Run the server on any Docker-capable host (Fly, Railway, Cloud Run, your own boxโฆ) and let each user connect with their own Upload-Post API key. The server stores nothing per user.
Async uploads return a request_id. The agent should poll get_status until success: true.
One tool per question, not per network
The Upload-Post API has no endpoint per social network: it has an endpoint per question, and a platform parameter saying who is being asked. get_post_comments, get_post_analytics, get_audience, get_suggestions and comment_action all work that way, so an agent learns one shape and reuses it for every network. Only the four tiktok_* tools are network-specific, because what they return (the Commercial Music Library, TikTok places, TikTok's per-account publishing settings) exists only on TikTok.
get_audience โ who follows the profile, where they are, when they are online, what they tap. Also benchmark_categories, and the niche averages to compare against when benchmarkCategory is set. The server clamps the window to at most 60 days ending before today, so a wider range is trimmed rather than rejected, and range in the response says which window was used.
get_suggestions โ hashtags (with view_count) or related keyword searches, told apart by type, not by a different tool.
get_post_comments โ top-level comments on a post, or, with commentId, the replies under one of them.
comment_action โ moderate a comment on TikTok (hide / like / pin), Facebook (hide / like / edit), Instagram (hide, or enable / disable comments on a post), YouTube (hide / hold) and Threads (hide / approve / ignore). Each value carries its own inverse, so nothing is permanent.
get_post_analytics โ per-post metrics. post_metrics is whatever the platform reports, so its shape varies: on TikTok it adds retention, impression_sources, audience_types, new_followers, reach and the watch times (average_time_watched, total_time_watched, full_video_watched_rate).
Errors are shared too: platform_not_supported (400, with the list of the networks that can answer), invalid_parameter (400), tiktok_reconnect_required (400), reauth_required (409) and 502 when the upstream network fails.
TikTok capabilities
get_post_comments, create_comment, delete_comment and comment_action accept platform: "tiktok", and firstComment works on TikTok like on every other network.
What a TikTok account can do depends on how it is connected. list_users returns a capabilities array on each TikTok account โ music, location, cover_image, cover_timestamp, draft, video_privacy, photo_privacy, profile_analytics, comments, trend_search โ and each tool's description names the one it needs. They are granted when the user connects TikTok, so an account connected before a capability existed has to reconnect before the matching tools answer; that is what a tiktok_reconnect_required error means.
get_media and get_cached_post_analytics are cursor-paginated: feed the response's next_cursor back as cursor until has_more is false. LinkedIn, Discord and Telegram do not support media cursors and accept limit only. Prefer get_cached_post_analytics over get_post_analytics when scanning many posts โ it replays previously fetched results and so avoids the live analytics rate limit of 100 requests / 5 minutes. Only contains posts previously fetched through a live per-post endpoint; there is no background refresh, so captured_at is the last time that post was read live.
FFmpeg jobs accept one public URL through input_url or multiple URLs through files. Poll get_ffmpeg_job until completion, then call download_ffmpeg_result; it returns the result URL without streaming the processed binary through MCP.
ChatGPT video upload UI
open_upload_studio renders a ChatGPT Apps component for file-based video publishing. The widget creates a short-lived Upload-Post/R2 staging upload, PUTs the local video directly to R2, completes the upload, then calls upload_video with the returned temporary media URL.
The widget speaks the ChatGPT Apps SDK bridge, so it is only advertised to ChatGPT (detected from clientInfo at initialize; override the match with UPLOAD_POST_STUDIO_CLIENTS=<regex>). Every other host (claude.ai, Claude Desktop, Claude Code, Cursor, โฆ) does not see the tool or its ui:// resource; instead create_media_upload / complete_media_upload are exposed to the model with step-by-step guidance, and upload_video tells the assistant to ask for a public URL or send the user to the dashboard when the client cannot PUT the file itself.
The staging object is deleted after 24 hours whether it is used or not. Scheduled/queued posts remain safe because upload_video copies the temporary URL into the existing durable scheduler storage before execution.
Claude and other MCP clients can use the same flow without the ChatGPT UI: call create_media_upload, PUT the file to upload_url, call complete_media_upload, then pass media_url to upload_video.
Set UPLOAD_POST_R2_CONNECT_DOMAINS on the MCP host to the comma-separated origins used by the backend's R2 signed URLs when they differ from the defaults (for example https://<account>.r2.cloudflarestorage.com,https://<bucket>.<account>.r2.cloudflarestorage.com) so the ChatGPT component CSP allows the browser PUT.
The R2 bucket CORS policy must allow browser uploads. A restrictive policy can include your actual widget origin; for fastest validation, use:
Single user's Upload-Post API key. Ignored in --http mode โ keys come per request.
UPLOAD_POST_BASE_URL
both
https://api.upload-post.com/api
Override for self-hosted / staging.
UPLOAD_POST_MCP_PORT
http
8080
Port for --http mode.
OPENAI_APPS_CHALLENGE_TOKEN
http
Current Upload-Post challenge token
Optional override for ChatGPT Apps domain verification at /.well-known/openai-apps-challenge.
CLI flags:
--http โ start the streamable HTTP transport instead of stdio
--port <n> โ port for HTTP mode
HTTP endpoints:
POST /mcp โ JSON-RPC over MCP streamable HTTP. Requires Authorization: ApiKey <key> (or Bearer <key>) on every request. The key is the user's own Upload-Post API key; the server uses it only for that session and stores nothing.
GET /healthz โ liveness probe, always open. Returns {"ok":true}.
Auth model in --http mode is the same pattern Resend, Tavily, Brave Search and other API-key-native services use for their hosted MCPs: the upstream key is the auth.
Deploy with Docker
The repo ships with a multi-stage Dockerfile and a .dockerignore. On any Docker-capable PaaS (Fly.io, Railway, Render, Cloud Run, fly machines, your own boxโฆ):
Point the PaaS at this repo and select Dockerfile as the build pack.
Port: 8080 (matches EXPOSE 8080).
Environment variables: none are required. Optionally set UPLOAD_POST_BASE_URL if you point at staging.
Health check path: /healthz (HTTP, port 8080).
Domain: attach a domain, e.g. mcp.your-domain.com, and provision TLS (most PaaS do this automatically via Let's Encrypt).
Deploy. The server is now ready for any number of users. Each user adds the endpoint to their MCP client config with their own Upload-Post API key:
Without an Authorization header the server returns 401. The header is the only credential โ invalid Upload-Post keys will surface as upstream errors on the first tool call.
Prefer public URLs over local paths when uploading โ local paths only work if the MCP server runs on the user's machine.
In ChatGPT Apps, prefer open_upload_studio for user-selected video files. It avoids local-path handoff issues by uploading to short-lived Upload-Post/R2 staging, then passing a temporary media URL to upload_video.
In any other client with a local file: if the client can run HTTP requests (Claude Code, Cursor, a script), stage it with create_media_upload โ PUT the bytes to upload_url โ complete_media_upload, then pass the returned media_url to upload_video. Hosted chats without that ability (claude.ai) need a public HTTPS URL or the dashboard at https://app.upload-post.com.
To send video bytes directly (a client that holds the file rather than a URL), pass videoBase64 to upload_video instead of videoPathOrUrl. The server writes it to a temp file, uploads, then deletes it. Inline bytes are capped at UPLOAD_POST_MAX_INLINE_MB (default 100 MB) โ for larger videos use a public URL.
Always create the profile first (create_user) and connect socials in the Upload-Post dashboard before publishing.
For scheduled posts, pass ISO 8601 dates with timezone, e.g. "2026-12-25T10:00:00Z" + "timezone": "Europe/Madrid".
Privacy & data handling
This server is a stateless proxy to the Upload-Post API. Per request, the only data it processes is the user's API key (or OAuth access token resolved to one) and the arguments of the tool call being executed. No user data is persisted by the MCP container itself.
What we receive per request: the Authorization header, the MCP tool name + arguments, and any media URLs/paths the agent passes.
What we forward: the tool arguments to the Upload-Post API on behalf of the authenticated user.
What we store: nothing per-user. OAuth tokens are stored upstream in the Upload-Post backend, hashed (SHA-256), so a breach of token storage cannot impersonate users.
What we log: HTTP method, path, status code, and an opaque request ID. No tool arguments, no API keys, no tokens.
To revoke a connector's access at any time, open Connected Apps in app.upload-post.com.
Security
All traffic is TLS-terminated at the edge (HTTPS only).
/mcp requires a valid Authorization header on every request; OAuth access tokens are short-lived (1 h access + 90 d refresh with rotation per RFC 6749 ยง10.4).
The server validates the Origin header against an allow-list (claude.ai, claude.com, chatgpt.com, chat.openai.com, app.upload-post.com, localhost) to mitigate DNS-rebinding attacks from browser-based clients. Extend with OAUTH_EXTRA_ALLOWED_ORIGINS (comma-separated) when self-hosting behind a custom dashboard.
If ChatGPT shows redirect_uri not on allow-list during OAuth, add the exact redirect_uri from the failing authorize request to the Upload-Post backend OAuth redirect allow-list. For ChatGPT clients this is typically on https://chatgpt.com/.../oauth/callback or https://chat.openai.com/.../oauth/callback.
OAuth redirect callbacks are pre-allowed for: Claude (claude.ai/claude.com), ChatGPT, Cursor, VS Code (stable + Insiders), Smithery, Glama, Toolhouse, Perplexity (standard + Enterprise), Mistral Studio debugger and Postman โ plus any http://localhost/loopback redirect (RFC 8252), which covers Claude Code, Windsurf, Cline, Continue, Goose, Gemini CLI and other mcp-remote-style clients. Platforms without a documented fixed callback (e.g. Grok, Le Chat production) are added on request.
All tools declare MCP readOnlyHint/destructiveHint annotations so clients can surface confirmation prompts for destructive operations.
Report a security issue: info@upload-post.com (encrypted PGP available on request).
License
MIT ยฉ Upload-Post
Install
Remote endpoint
Streamable HTTP
Hosted server - connect over the network, no local install.