项目介绍
MCP 服务 · 2026-11-09 更新 · 本站尚未实测
@upload-post/mcp 是 Upload-Post 官方提供的 Model Context Protocol(MCP)服务器,构建在其官方 upload-post SDK 与公开 REST API 之上。它本身不是社媒平台,也不自带账号;作用是把 Upload-Post 的发布、排期、数据分析和互动管理能力,以工具形式暴露给兼容 MCP 的 AI 客户端(README 举例:ChatGPT、Claude Desktop、Claude Code、Cursor、Gemini CLI、Perplexity Computer 等)。
README 说明它接入 TikTok、Instagram、YouTube、LinkedIn、Facebook、Pinterest、Threads、Bluesky、X、Google Business、Discord、Telegram 等平台;Reddit 发布当前关闭,并称本地 stdio 模式启动后可见 58 个 upload-post 工具。
核心能力
点击能力名称查看输入、输出和原文依据。
按 README 的工具分组,主要能力包括:
- 发布:
upload_video、upload_photos、upload_text、upload_document,以及 ChatGPT 内的open_upload_studio文件发布组件;一次调用可通过platform列表把同一视频分发到 TikTok、Reels、Shorts 等多平台。 - 媒体暂存:
create_media_upload、complete_media_upload、get_media_upload、delete_media_upload。 - 状态与历史:
get_status、get_job_status、get_history、get_media;异步上传返回request_id,需轮询get_status。 - 排期:
list_scheduled、cancel_scheduled、edit_scheduled。 - 分析:
get_analytics、get_total_impressions、get_post_analytics、get_cached_post_analytics、get_platform_metrics、get_audience、get_suggestions。 - 互动与评论管理:评论读取、创建、删除、回复、隐藏、置顶、点赞、编辑等;另有 DM 与自动回复相关工具(
send_dm、start_autodm等)。 - 账号与页面:
get_account_info、list_users、get_connect_link、Facebook 主页、LinkedIn 页面、Pinterest 画板、Google Business 位置与评论等。 - TikTok 专用:音乐趋势、音乐搜索、位置搜索、发布设置。
- FFmpeg 任务:
submit_ffmpeg_job等,在 Upload-Post 编码服务器上执行,按输入/输出中较长时长计费(README 原文:Usage is charged at the longer of the input and output durations)。
写操作类工具采用两步确认:未传 confirmed: true 时返回 confirmation_required 与预览,不实际执行。
实际应用场景
适用场景
- 希望用对话式 AI 客户端完成多平台内容发布与排期的运营人员;
- 需要统一查询跨平台帖子数据、受众与推荐标签的分析场景;
- 需要批量处理评论、私信自动回复的社媒运营;
- 需要以 MCP 方式把社媒能力接入自有 AI 工作流的开发者。
安装与接入
ChatGPT / Claude 官方连接器:README 称 Upload-Post 是 OpenAI 审核插件与 Claude 连接器目录中的官方连接器,可在客户端内搜索 Upload-Post 后安装/连接并用 OAuth 登录,无需粘贴 MCP URL。
手动添加托管服务:MCP 地址为 https://mcp.upload-post.com/mcp,认证方式为 OAuth;也可使用自己的 Upload-Post API Key。Gemini CLI 安装命令(原文):
gemini extensions install https://github.com/Upload-Post/upload-post-mcp
Claude Code 添加命令(原文):
claude mcp add --transport http upload-post https://mcp.upload-post.com/mcp
本地 stdio 模式:由 MCP 客户端启动 npx -y @upload-post/mcp,需设置环境变量 UPLOAD_POST_API_KEY。API Key 获取位置按 README 为 app.upload-post.com 的 API Keys 页面。
自托管 HTTP 模式:仓库提供 Dockerfile,README 说明可部署在 Fly、Railway、Render、Cloud Run 等 Docker 平台,端口 8080,健康检查路径 /healthz;每个用户请求需携带自己的 Authorization: ApiKey <key>(或 Bearer)。
开发调试:README 给出 git clone、npm install、npm run build 及 node dist/index.js、--http --port 8080 启动方式,可用 npx @modelcontextprotocol/inspector 检查工具。具体参数与换行以原文为准。
Transport
streamable-http
Auth
OAuth 或 Upload-Post API Key(Authorization: ApiKey <key> / Bearer <key>)
使用条件与限制
- 是否需要部署
- 服务入口见官方资料,客户端与认证要求见正文
- 是否需要注册
- 账号和认证要求见正文;未明确说明的项目仍待核验
- 是否需要 API Key
- 待核验
- 费用与额度
- 费用边界见正文,项目许可不代表模型或托管服务免费
- 所需权限
- 待核验
- 许可证与服务条款
- 待核验
权限费用与限制
- 需要 Upload-Post 账号与 API Key;部分平台需先在 Upload-Post 面板连接社媒账号,并先调用
create_user创建 profile。 - 发布、私信、评论回复等写操作需
confirmed: true,否则仅返回预览。 - README 明确 FFmpeg 任务按时长计费;Upload-Post 服务本身的套餐价格与免费额度未在资料中给出,待向官方核验。
- X 平台回复/编辑/转发有每日上限(README 称免费计划 20、付费计划 200,UTC 0 点重置);分析实时接口限流为 100 请求/5 分钟。
- Reddit 发布目前被关闭,
reddit不作为可选平台,请求会返回reddit_unavailable。 - 托管模式默认不允许读取服务器本地文件(
UPLOAD_POST_MCP_ALLOW_LOCAL_FILES默认 false)。 - README 称 MCP 容器不持久化用户数据,仅转发请求;OAuth token 在上游后端哈希存储。该声明为项目自述,安全性未经本站实测核验。
- 国内网络能否直连
mcp.upload-post.com、各社媒平台的账号可用性,资料未说明,待核验。 - Windows 本地支持情况资料未明确,待核验。
国内使用情况
网友核验 用户经验分享
与站长实测独立,不代表官方认证或本站已实测。
最近核验:暂无有效核验。参与人数按当前项目有效身份去重;不同核验项分别统计,不合并成用户总结果。仅统计审核通过且未过期的记录。
分项统计、有效期与样本量
| 核验项目 | 有效人数 | 成功 / 部分 / 失败 / 无法判断 | 成功率 | 最近核验 / 有效期 |
|---|---|---|---|---|
| 官方网站访问 | 0 | 0 / 0 / 0 / 0 | 样本不足 | 暂无 / 7 天 |
| GitHub 仓库访问 | 0 | 0 / 0 / 0 / 0 | 样本不足 | 暂无 / 7 天 |
| 安装与运行 | 0 | 0 / 0 / 0 / 0 | 样本不足 | 暂无 / 30 天 |
| 官方服务国内连接 / MCP 连接 | 0 | 0 / 0 / 0 / 0 | 样本不足 | 暂无 / 30 天 |
| 国内网络情况 | 0 | 0 / 0 / 0 / 0 | 样本不足 | 暂无 / 7 天 |
| 国内模型兼容性 | 0 | 0 / 0 / 0 / 0 | 样本不足 | 暂无 / 30 天 |
| 自定义 API | 0 | 0 / 0 / 0 / 0 | 样本不足 | 暂无 / 30 天 |
| 海外服务依赖 | 0 | 0 / 0 / 0 / 0 | 样本不足 | 暂无 / 30 天 |
| 海外账号要求 | 0 | 0 / 0 / 0 / 0 | 样本不足 | 暂无 / 30 天 |
| 中文界面 | 0 | 0 / 0 / 0 / 0 | 样本不足 | 暂无 / 30 天 |
| 客户端兼容性 | 0 | 0 / 0 / 0 / 0 | 样本不足 | 暂无 / 30 天 |
| 账号及费用信息 | 0 | 0 / 0 / 0 / 0 | 样本不足 | 暂无 / 30 天 |
成功率 = 成功 ÷(成功 + 部分成功 + 失败);无法判断不计入分母。至少 5 份可判断结果才显示成功率。账号、费用、海外依赖与语言等事实项,成功表示核查结论与资料吻合,具体观察另行统计;不表示免费或没有海外依赖。不同网络、客户端和版本的结果不能直接等同。
最近有效核验
暂无审核通过且未过期的网友核验。欢迎提交真实使用记录。
查看全部有效核验 →国内实测状态:国内可用性待核验。协议兼容和官方资料说明不等于国内网络可用。
- 官方网站访问
- 尚未实测 待核验
- GitHub 仓库访问
- 尚未实测 待核验
- 官方服务国内连接
- 尚未实测 待核验
- 国内网络情况
- 尚未实测 待核验
- 国内模型兼容性
- 尚未实测 待核验
- 自定义 API
- 尚未实测 待核验
- 海外服务依赖
- 尚未实测 待核验
- 海外账号要求
- 尚未实测 待核验
- 中文界面
- 尚未实测 待核验
- 国内最后核验时间
- 尚未实测
许可与国内使用
仓库 LICENSE 为 MIT(Copyright (c) 2026 Upload-Post),覆盖仓库代码。需注意:代码开源不等于 Upload-Post 托管服务或其代理的社媒平台接口免费;托管 MCP 服务、API 调用额度、FFmpeg 编码等费用规则需以官方说明为准。第三方模型与各社媒平台的授权、地域可用性由各自服务方决定。资料未说明服务的部署地域及国内合规适配情况,面向国内用户使用时应自行核验网络可达性、数据出境与平台规则。
项目资料与来源
字段依据与待核验事项
access:来源确认 · Prefer to wire it up by hand? In ChatGPT on the web, open **Plugins**, click **+**, choose **Add custom MCP server**, point it at `https://mcp.upload-post.com/mcp` and set authentication to **OAuth**.
project_type:来源确认 · Official **Model Context Protocol (MCP)** server for [Upload-Post](https://www.upload-post.com).
原始 README(折叠技术参考)
# @upload-post/mcp
Official **Model Context Protocol (MCP)** server for [Upload-Post](https://www.upload-post.com).
[](https://smithery.ai/servers/upload-post/social-media-mcp) [](https://glama.ai/mcp/servers/Upload-Post/upload-post-mcp) [](https://www.npmjs.com/package/@upload-post/mcp)
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`](https://www.npmjs.com/package/upload-post) SDK and the public Upload-Post REST API.
---
## Use it in ChatGPT (no setup)
Upload-Post is a plugin reviewed by OpenAI, listed in ChatGPT's **Plugins** (formerly Apps). [Open Upload-Post in ChatGPT](https://chatgpt.com/plugins/plugin_asdk_app_6a1fed02ff5c8191b4060c5807221f26) or search for *Upload-Post* in **Plugins**, click **Install**, then **Connect**, and sign in with OAuth. There is no MCP URL to paste, and you get the [Upload Studio](#chatgpt-video-upload-ui) widget for publishing video files, photos and carousels straight from your computer. OpenAI dots use the same plugin: select it in your dot's **Customize → Plugins** and review its permissions.
Prefer to wire it up by hand? In ChatGPT on the web, open **Plugins**, click **+**, choose **Add custom MCP server**, point it at `https://mcp.upload-post.com/mcp` and set authentication to **OAuth**. Custom MCP servers get write actions on Business, Enterprise and Edu workspaces.
## Use it in Claude (no setup)
Upload-Post is an official connector in Claude's connector directory. [Add Upload-Post to Claude](https://claude.ai/directory/upload-post), or in claude.ai, Claude Desktop or Claude Cowork open **Customize → Connectors**, browse the directory and search for *Upload-Post*, then click **Connect** and sign in with OAuth. There is no MCP URL to paste. On Team and Enterprise an owner may need to allow it first in **Organization settings**.
Prefer to wire it up by hand? Open **Customize → Connectors**, click **+ Add**, then **Add custom connector**, paste `https://mcp.upload-post.com/mcp`, click **Continue** and sign in with OAuth. Custom connectors work on every Claude plan (the Free plan allows one). In Claude Code: `claude mcp add --transport http upload-post https://mcp.upload-post.com/mcp`, or install the official Upload-Post plugin from the Claude directory.
## Use it in Perplexity Computer
Open **Plugins** in the sidebar (or **Customize → Connectors**), then **Add custom → Connector → Add MCP connector → Remote**. Use `https://mcp.upload-post.com/mcp` with the Streamable HTTP transport and OAuth (or your Upload-Post API key), click **Add**, then click the Upload-Post card to sign in.
## Use it in Gemini CLI
Install the extension and sign in with OAuth the first time Gemini calls a tool:
```bash
gemini extensions install https://github.com/Upload-Post/upload-post-mcp
```
---
## 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.):
```jsonc
{
"mcpServers": {
"upload-post": {
"command": "npx",
"args": ["-y", "@upload-post/mcp"],
"env": { "UPLOAD_POST_API_KEY": "YOUR_API_KEY" }
}
}
}
```
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.
```jsonc
{
"mcpServers": {
"upload-post": {
"url": "https://mcp.your-domain.com/mcp",
"headers": {
"Authorization": "ApiKey YOUR_OWN_UPLOAD_POST_API_KEY"
}
}
}
}
```
`Authorization: Bearer <key>` is also accepted, for clients that only allow Bearer.
---
## What can the agent do?
The server exposes Upload-Post API tools plus one ChatGPT App UI launcher.
| Group | Tools |
|---------------|-------|
| Upload | `upload_video`, `upload_photos`, `upload_text`, `upload_document`, `open_upload_studio` |
| Media staging | `create_media_upload`, `complete_media_upload`, `get_media_upload`, `delete_media_upload` |
| Status | `get_status`, `get_job_status`, `get_history`, `get_media` |
| Schedule | `list_scheduled`, `cancel_scheduled`, `edit_scheduled` |
| Analytics | `get_analytics`, `get_total_impressions`, `get_post_analytics`, `get_cached_post_analytics`, `get_platform_metrics` |
| Audience | `get_audience`, `get_suggestions` |
| Users | `get_account_info`, `list_users`, `get_connect_link`, `create_user`, `delete_user`, `generate_jwt`, `validate_jwt` |
| Pages/boards | `get_facebook_pages`, `get_linkedin_pages`, `get_pinterest_boards`, `get_google_business_locations`, `get_google_business_reviews`, `reply_to_google_business_review`, `get_reddit_detailed_posts` |
| Posts | `retry_post`, `unpublish_post` |
| Comments | `get_post_comments`, `create_comment`, `delete_comment`, `reply_to_comment`, `public_reply_to_comment`, `hide_comment`, `like_comment`, `pin_comment`, `edit_comment`, `hold_comment`, `approve_comment`, `ignore_comment`, `set_post_comments`, `comment_action` |
| TikTok | `tiktok_music_trending`, `tiktok_music_search`, `tiktok_location_search`, `tiktok_publishing_settings` |
| DMs | `send_dm`, `list_dm_conversations`, `start_autodm` (Instagram DM or public auto-reply on TikTok, YouTube, Facebook, Threads, Bluesky), `list_autodms`, `get_autodm_logs`, `pause_autodm`, `resume_autodm`, `stop_autodm`, `delete_autodm`, `manage_autodms` |
| FFmpeg | `submit_ffmpeg_job`, `get_ffmpeg_job`, `download_ffmpeg_result`, `get_ffmpeg_consumption` |
| Queue | `get_queue_settings`, `update_queue_settings`, `preview_queue` |
Async uploads return a `request_id`. The agent should poll `get_status` until `success: true`.
### Per-platform: TikTok / YouTube / Instagram / LinkedIn / Facebook
One server covers every network; this is what each of the big five uses. Publishing always goes through the same upload tools with a `platform` list, so a single call can post one video to TikTok, Reels and Shorts at once.
| Platform | Publish | Network-specific helpers | Engage and measure |
|----------|---------|--------------------------|--------------------|
| TikTok | `upload_video` (videos, drafts), `upload_photos` (photo carousels) | `tiktok_publishing_settings`, `tiktok_music_trending`, `tiktok_music_search`, `tiktok_location_search` | `get_post_comments`, `create_comment`, `comment_action`, `get_post_analytics`, `get_audience`, `start_autodm` (public auto-reply) |
| YouTube | `upload_video` (videos and Shorts, with title, description and tags) | | `get_post_comments`, `comment_action` (hide / hold), `get_post_analytics`, `get_analytics`, `start_autodm` (public auto-reply) |
| Instagram | `upload_video` (Reels, Stories), `upload_photos` (posts, carousels, Stories) | | `get_post_comments`, `reply_to_comment`, `comment_action`, `send_dm`, `list_dm_conversations`, `start_autodm` (comment-to-DM), `get_post_analytics` |
| LinkedIn | `upload_text`, `upload_photos`, `upload_video`, `upload_document` (PDF, PPT, DOCX documents) | `get_linkedin_pages` (post as a company page) | `get_post_comments`, `get_post_analytics`, `get_analytics` |
| Facebook | `upload_video`, `upload_photos`, `upload_text` | `get_facebook_pages` (pick the page) | `get_post_comments`, `comment_action` (hide / like / edit), `start_autodm` (public auto-reply), `get_post_analytics` |
Search terms people use for these: TikTok MCP server, YouTube MCP server / YouTube uploader MCP, Instagram MCP server, LinkedIn MCP server, Facebook MCP server.
### 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. Reads are cached for up to 60 seconds (up to 2 minutes on X); a comment created or deleted through Upload-Post by the same account shows up on the next read.
- `create_comment` on X — links are removed unless the account is allowed links on X (reported in `warnings`); a message that was only links fails with 400 `x_links_not_allowed`. Replies, post edits and reposts on X share a daily limit per X account (20 on the free plan, 200 on paid plans, reset at 00:00 UTC); past it the API answers 429 `x_daily_write_limit` with `retry_after` and a `Retry-After` header.
- `hide_comment`, `like_comment`, `pin_comment`, `edit_comment`, `hold_comment`, `approve_comment`, `ignore_comment`, `set_post_comments` — one tool per moderation operation. `comment_action` does the same through a single `action` argument and stays available everywhere except ChatGPT, which only gets the one-operation tools (the same goes for `manage_autodms`).
- `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`. They run on Upload-Post's encoding servers, each in its own working directory. `full_command` is a single ffmpeg invocation with `{input}` (or `{input0}`, `{input1}`…) and `{output}` placeholders, run without a shell: it must start with `ffmpeg`, every `-i` must be an `{input}`/`{inputN}` placeholder or a `-f lavfi` source, and `{output}` is the only output. Protocols or URLs inside the command (`file:`, `concat:`, `subfile,`…), the `movie=`/`amovie=`/`sendcmd` filters, absolute paths other than fonts under `/usr/share/fonts`, `..`, `-safe 0` and shell metacharacters are rejected with a 400. Endless commands (`-stream_loop -1`, `-loop 1`, a lavfi source without a duration) need `-t`, `-shortest` or `-frames`, and `-stream_loop` goes from -1 to 100. Usage is charged at the longer of the input and output durations. Poll `get_ffmpeg_job` until completion, then call `download_ffmpeg_result`; it returns the result URL without streaming the processed binary through MCP. Nothing is posted until you pass that URL to an upload tool.
### Confirmation before acting
Tools that publish or reach other people as the user run in two steps. Called without `confirmed: true`, they return `status: "confirmation_required"` with a preview of exactly what would go out (text, media, platforms or recipients, buttons, keywords, timing) and do nothing; the model shows it to the user and repeats the call with `confirmed: true` once they approve. This covers `upload_video`, `upload_photos`, `upload_text`, `upload_document`, `send_dm`, `reply_to_comment`, `public_reply_to_comment`, `create_comment`, `reply_to_google_business_review`, `edit_comment`, `unpublish_post`, `start_autodm` and `manage_autodms` with `action: "start"`, plus `comment_action` / `hide_comment` when they edit a comment or ban its author. `delete_user` deletes only when `confirm_username` repeats the profile name. ChatGPT, which asks the user before every write tool, keeps the single-step tools.
Reddit publishing is switched off while the Reddit app is unavailable: `reddit` is not offered as a platform and asking for it fails with `reddit_unavailable`. Self-hosters can set `UPLOAD_POST_REDDIT_ENABLED=true` once it is back.
### ChatGPT video upload UI
`open_upload_studio` renders a ChatGPT Apps component for file-based publishing: one video, or one or more images (up to 10, for a carousel; pass `mediaType: "photos"`). For each file the widget creates a short-lived Upload-Post/R2 staging upload, PUTs the file directly to R2 and completes the upload, one file at a time, then calls `upload_video` or `upload_photos` with the returned temporary media URLs in order. For photos it can instead hand the staged URLs back to ChatGPT ("Send to ChatGPT"), which then calls `upload_photos` itself, e.g. with a different caption per platform. `upload_video` and `upload_photos` refuse `/mnt/data` and sandbox attachment paths and point the model at the Studio.
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:
```json
[
{
"AllowedOrigins": ["*"],
"AllowedMethods": ["PUT", "GET", "HEAD"],
"AllowedHeaders": ["*"],
"ExposeHeaders": ["ETag"],
"MaxAgeSeconds": 3600
}
]
```
---
## Local / development
```bash
git clone https://github.com/Upload-Post/upload-post-mcp.git
cd upload-post-mcp
npm install
npm run build
# stdio (default — used by Claude Desktop, Cursor)
UPLOAD_POST_API_KEY=... node dist/index.js
# HTTP streamable (for hosted deployments)
UPLOAD_POST_API_KEY=... node dist/index.js --http --port 8080
```
Inspect the live tool surface with the official inspector:
```bash
npx @modelcontextprotocol/inspector node dist/index.js
```
---
## Configuration
| Env var | Mode | Default | Description |
|---------------------------|---------|------------------------------------|------------------------------------------|
| `UPLOAD_POST_API_KEY` | stdio | — (required) | 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. |
| `UPLOAD_POST_MCP_MAX_BODY_MB` | http | `10` | Largest `/mcp` request body accepted; bigger ones get `413`. |
| `UPLOAD_POST_MCP_VALIDATE_API_KEYS` | http | `true` | Check a pasted API key once against `GET /uploadposts/me` (cached) before opening a session for it; a rejected key gets `401`. |
| `UPLOAD_POST_MCP_KEY_CHECK_TTL_MS` | http | `600000` | How long an accepted key is trusted before it is checked again. |
| `UPLOAD_POST_MCP_MAX_SESSIONS` | http | `2000` | Live sessions kept in memory; past it the longest-idle one is closed. |
| `UPLOAD_POST_MCP_MAX_SESSIONS_PER_KEY` | http | `200` | Live sessions per credential; past it that credential's own longest-idle session is closed. |
| `UPLOAD_POST_MCP_SESSION_TTL_MS` | http | `1800000` | Idle time after which a session is closed. |
| `UPLOAD_POST_MCP_AUTH_FAIL_MAX` | http | `20` | `401`s on `/mcp` one client IP may collect per window; past it that IP gets `429` + `Retry-After` (sessions and keys already verified still pass; one request per probe interval is checked normally). `0` turns it off. |
| `UPLOAD_POST_MCP_AUTH_FAIL_WINDOW_S` | http | `300` | Window for the count above; a held IP is released this long after its last failure. |
| `UPLOAD_POST_MCP_AUTH_FAIL_PROBE_S` | http | `30` | While held, one request per this many seconds goes through the normal auth path. |
| `UPLOAD_POST_MCP_TRUST_PROXY` | http | `auto` | Whether the client IP is the last `X-Forwarded-For` hop: `auto` (only when the peer is a private address, i.e. a local reverse proxy), `true` or `false`. |
| `UPLOAD_POST_MCP_ALLOW_LOCAL_FILES` | http | `false` | Let tools read media from file paths on the server's disk. Off for a shared host (only http(s) URLs are accepted); turn it on only when the HTTP server runs next to your own files. stdio always allows paths. |
| `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…):
1. Point the PaaS at this repo and select **Dockerfile** as the build pack.
2. **Port: 8080** (matches `EXPOSE 8080`).
3. **Environment variables**: none are required. Optionally set `UPLOAD_POST_BASE_URL` if you point at staging.
4. **Health check path**: `/healthz` (HTTP, port 8080).
5. **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:
```jsonc
{
"mcpServers": {
"upload-post": {
"url": "https://mcp.your-domain.com/mcp",
"headers": {
"Authorization": "ApiKey USER_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.
Local test of the production image:
```bash
docker build -t upload-post-mcp .
docker run --rm -p 8080:8080 upload-post-mcp
curl http://localhost:8080/healthz # → {"ok":true}
curl -i -X POST http://localhost:8080/mcp \
-H "content-type: application/json" \
-d '{}' # → 401 (no Authorization)
```
---
## Tips for prompting the agent
- 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 and image files. It avoids local-path handoff issues by uploading to short-lived Upload-Post/R2 staging, then passing temporary media URLs to `upload_video` / `upload_photos`.
- 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), and in `--http` mode the whole request at `UPLOAD_POST_MCP_MAX_BODY_MB` (default 10 MB, roughly 7 MB of video once base64-encoded) — 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.
Full Upload-Post privacy policy (data collection, retention, third-party sharing, contact, GDPR/CCPA): **https://upload-post.com/privacy**
To revoke a connector's access at any time, open **Connected Apps** in [app.upload-post.com](https://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.
- OAuth metadata is served both at `/.well-known/oauth-protected-resource` and at the path-inserted form `/.well-known/oauth-protected-resource/mcp` (RFC 9728 §3.1), and likewise for `oauth-authorization-server`.
- In `--http` mode the server writes one stderr line per failed request and per OAuth/well-known request (method, path, status, auth scheme, user agent — never query strings or credentials). Set `UPLOAD_POST_MCP_ACCESS_LOG=all` to log every request or `off` to disable it.
- 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, and the tools that publish or message people also require an explicit `confirmed: true` (see [Confirmation before acting](#confirmation-before-acting)).
Report a security issue: **info@upload-post.com** (encrypted PGP available on request).
---
## License
MIT © Upload-Post
历史前置条件说明(补充参考)
需 Upload-Post 账号与 API Key(获取于 app.upload-post.com);部分客户端需 OAuth 登录。本地模式需 Node.js/npx 环境(版本要求待核验);自托管需 Docker 环境。FFmpeg 任务按时长计费,服务套餐价格与免费额度待向官方核验。国内网络可达性与社媒账号可用性待核验。
项目来源与清单版权许可
https://github.com/Upload-Post/upload-post-mcp
整理来源:https://github.com/slavakurilyak/awesome-ai-agents
清单版权与许可:
MIT License
Copyright (c) 2024 Slava Kurilyak
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.