Skip to content

Fix MCP upload rate limits and Instagram Reel duration caps#205

Merged
paulocastellano merged 17 commits into
mainfrom
fix/mcp-upload-rate-limit-and-reel-durations
Jul 25, 2026
Merged

Fix MCP upload rate limits and Instagram Reel duration caps#205
paulocastellano merged 17 commits into
mainfrom
fix/mcp-upload-rate-limit-and-reel-durations

Conversation

@paulocastellano

@paulocastellano paulocastellano commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Rate-limit signed uploads (POST /api/uploads/{token}) by workspace (60/min) with a high IP backstop (1200/min), so ChatGPT’s shared egress no longer buckets all tenants together
  • MCP upload size caps reuse config('trypost.media.max_size_mb') via Media\Type (video ceiling 1GB); uploads stream to storage via addMediaFromPath (no full-file file_get_contents); failed persist releases the upload claim so retries work
  • RequestMediaUploadTool returns max_bytes + max_bytes_by_type so agents preflight image/PDF/video correctly
  • Content-type media rules (sizes, durations, accepts, aspect bounds) live on ContentType, clamped to hard upload caps, and shared via Inertia / API / MCP — Instagram Reel → 15 min, Facebook Reel stays 90 s
  • List Pinterest boards and Discord channels via REST + MCP (needed for meta.board_id / meta.channel_id); boards follow Pinterest bookmark pagination and return {boards, truncated}; web board picker warns when truncated
  • Signed upload TTL lives in config/trypost.php (MEDIA_SIGNED_UPLOAD_URL_TTL_MINUTES, legacy fallback MCP_UPLOAD_URL_TTL_MINUTES)
  • API social-account lookups use SocialAccountPolicy (404 deny-as-not-found for cross-tenant)

Ops / deploy notes

  • For 1GB MCP video uploads to work in production, PHP/nginx limits must be ≥ 1GB: upload_max_filesize, post_max_size, and client_max_body_size

Automated tests

  • Upload / rate-limit / per-type size / streaming path tests
  • Content-type media rules (unit + API + MCP + Inertia share)
  • Pinterest boards API/MCP (happy, empty, 401, 502) + publisher pagination + truncated
  • Discord channels API/MCP (happy, upstream failure, wrong platform)
  • SocialAccountPolicy tenancy

Manual test plan

Ops

  • Confirm production PHP/nginx upload limits are ≥ 1GB
  • After deploy, confirm Nightwatch no longer shows cross-tenant 429s on /api/uploads/{token} under ChatGPT batch uploads

MCP / ChatGPT

  • list-content-types: Instagram Reel max_video_duration_sec = 900, Facebook Reel = 90
  • Request a signed upload, upload a real video under the 1GB cap, then create a post that uses that media
  • Upload several media files in a short burst from ChatGPT and confirm you don’t get spurious 429s (workspace bucket, not shared IP)
  • list-pinterest-boards for a connected account → pick a board_id → create/schedule a Pinterest pin with platforms[].meta.board_id and confirm it publishes to the right board
  • list-discord-channels → pick a channel_id → create/schedule a Discord post with platforms[].meta.channel_id and confirm it lands in the right channel
  • Attempt to publish a Pinterest pin without board_id and confirm the validation error is clear

Web app

  • Open the post editor with a Pinterest account selected → board dropdown loads; if the account has many boards, confirm the truncated warning appears when applicable
  • Create and publish a Pinterest post from the web UI with a chosen board
  • Create and publish a Discord post from the web UI with a chosen channel
  • Create an Instagram Reel with a video longer than 90s (but ≤ 15 min) and confirm the editor accepts it (and rejects > 15 min)
  • Create a Facebook Reel and confirm the editor still caps at 90s
  • Spot-check GIF attach on Discord/Telegram still works; Instagram feed still requires media

Automation editor

  • Open an automation Generate/publish config with Pinterest → boards load and truncated warning works the same as the post editor

paulocastellano and others added 17 commits July 24, 2026 21:21
Key signed uploads by workspace so ChatGPT's shared egress IPs don't throttle tenants together, raise the MCP upload cap to 300MB, and expose accurate Reel max durations via API/MCP.

Co-authored-by: Cursor <cursoragent@cursor.com>
Drop the separate ai.mcp.upload.max_size_mb default and reuse Media\Type limits so MCP matches web/API (1GB video ceiling with per-type enforcement).

Co-authored-by: Cursor <cursoragent@cursor.com>
Keep API media upload settings out of the Laravel AI package config so they are not overwritten on package updates.

Co-authored-by: Cursor <cursoragent@cursor.com>
Keep ContentType as the single source of truth and stop hardcoding maxVideoDurationSec in useMediaRules.

Co-authored-by: Cursor <cursoragent@cursor.com>
Give ChatGPT's shared egress more headroom across tenants while keeping the per-workspace cap at 60.

Co-authored-by: Cursor <cursoragent@cursor.com>
Agents need board_id to publish pins; list boards per connected account so create/update can set platforms[].meta.board_id.

Co-authored-by: Cursor <cursoragent@cursor.com>
Stream signed uploads through addMediaFromPath, return per-type max_bytes, harden Pinterest/Discord listing errors and pagination, and keep frontend duration fallbacks when Inertia once-props have not synced.

Co-authored-by: Cursor <cursoragent@cursor.com>
Share the full editor rule set (sizes, durations, accepts, aspect bounds) via Inertia so useMediaRules no longer hardcodes MB/GB math, and expose per-type byte caps on API/MCP content-type listings.

Co-authored-by: Cursor <cursoragent@cursor.com>
Replace repeated workspace_id checks with PostPolicy-style denyAsNotFound tenancy so cross-tenant lookups stay 404 without leaking existence.

Co-authored-by: Cursor <cursoragent@cursor.com>
Replace the 20-page hard stop with a while-loop that follows bookmarks to completion, keeping only safety breaks for a repeated cursor or an absurd page ceiling.

Co-authored-by: Cursor <cursoragent@cursor.com>
Keep Instagram feed requiring media and Discord/Telegram accepting GIFs after centralization, skip the empty workspace rate-limit bucket, and clear the signed upload claim when persistence fails so retries work.

Co-authored-by: Cursor <cursoragent@cursor.com>
…sts.

Align editor/API/MCP size ceilings with trypost.media hard caps, return truncated from Pinterest board pagination stop conditions, and rename the signed-upload claim key and rate limiter away from the MCP-only naming.

Co-authored-by: Cursor <cursoragent@cursor.com>
getBoards now returns {boards, truncated}; pass only the boards array into Inertia pinterestBoards so post and automation pickers keep receiving an array.

Co-authored-by: Cursor <cursoragent@cursor.com>
Share {boards, truncated} via ListPinterestBoards into Inertia and warn in the board picker when pagination stopped early, matching API/MCP.

Co-authored-by: Cursor <cursoragent@cursor.com>
Empty-account mount was clamping target_slide_count to 0 and never raising it when a media-required account was selected, so Pinterest showed a false requires-media error after picking a board.

Co-authored-by: Cursor <cursoragent@cursor.com>
AI generate only produces images, so Video Pin / Reel / TikTok Video are filtered out in previewOnly and rejected server-side with a clear error.

Co-authored-by: Cursor <cursoragent@cursor.com>
… API/MCP.

GenerateNodeValidator and the Generate UI now respect ContentType::minMediaCount, and content-type listings share accept/min flags via toListingArray().

Co-authored-by: Cursor <cursoragent@cursor.com>
@paulocastellano
paulocastellano merged commit b16e0bd into main Jul 25, 2026
5 checks passed
@paulocastellano
paulocastellano deleted the fix/mcp-upload-rate-limit-and-reel-durations branch July 25, 2026 02:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant