ContentStudio
Grow your social media with AI agent that knows your brand ContentStudio knows your brand, audience, and goals, then plans and creates on-brand content…
Community: Submitted by a user or imported; check the owner before granting accessOnlineNo sign-inGlobalFreeCan modify data
What it can do
- Ping: Responds with pong
- Validate Token: Validate API token via /api/v1/me
- Fetch Workspaces: Get user workspaces with pagination
What data it sees
Do you need an account
No: the server works without sign-in
Grow your social media with AI agent that knows your brand ContentStudio knows your brand, audience, and goals, then plans and creates on-brand content, publishes across your channels, and learns from performance. Review anything before it goes live, or let AI run fully on autopilot.
Server tool list (21)
Raw names from tools/list. Only developers need these.
| ping | Responds with pong |
| validate_token | Validate API token via /api/v1/me |
| fetch_workspaces | Get user workspaces with pagination |
| fetch_social_accounts | Get workspace social accounts with optional platform filter and pagination |
| fetch_content_categories | Get content categories for a workspace. Use this when user wants to create a content category post. Categories contain pre-configured accounts. |
| fetch_posts | List posts in a workspace with optional filters. Each post also returns linkedin_options and approval_workflow ({ workflow_id, workflow_name, status, current_level, total_levels, notes, submitted_at, levels[] }) when set. |
| create_post | Create a new post in a workspace. 📅 CURRENT DATE: 2026-08-27 (Thursday) Use this to calculate dates like "tomorrow", "next Sunday", "coming Monday", etc. ⚠️ BEFORE CALLING THIS TOOL - MUST SHOW PREVIEW & GET CONFIRMATION: 1. Show preview with ALL details (workspace, accounts, content, post_type, publish_type, schedule, approval) 2. Ask: "Create this post? (yes/no/change)" 3. ONLY call this tool after user says "yes" ❌ NEVER call without preview and user confirmation! ⚠️ SCHEDULE DATE RULE - CRITICAL: If user mentions ANY date/day/time (e.g., "Sunday", "tomorrow", "next week", "Jan 20"), ALWAYS pass scheduled_at. This applies to ALL publish_types (draft, queued, scheduled, content_category). Examples: - "draft for Sunday" → calculate Sunday's date from current date and pass it - "queue post for tomorrow" → pass tomorrow's date - "create draft for next Monday" → pass next Monday's date MULTI-DAY POSTS: "4 posts for next 4 days" → different scheduled_at for each (Day+1, Day+2, Day+3, Day+4). ✅ APPROVAL — TWO MUTUALLY EXCLUSIVE OPTIONS (pick at most one): A) LEGACY simple approval — approvers (array of user_ids), approve_option ("anyone"|"everyone"), approval_notes. - First call discover({ query: "team members" }) + execute_read to get workspace members and their user_ids (not a dedicated tool). The post creator CANNOT be an approver. - "anyone" = any single approver can approve; "everyone" = all approvers must approve. B) APPROVAL WORKFLOW (multi-level) — approval_workflow: { workflow_id, notes? } to ATTACH a workflow. - Get workflow_id (the id) from fetch_approval_workflows. - On create you can only ATTACH (workflow_id). The workflow_action form is UPDATE-only. Do NOT combine A and B. If user does NOT mention approval → omit both. ✅ LABELS & CAMPAIGNS (OPTIONAL): - discover({ query: "fetch labels" }) + execute_read to get available label IDs, then pass as labels array (max 20) — not a dedicated tool - discover({ query: "fetch campaigns" }) + execute_read to get available campaign IDs, then pass as campaign_id — not a dedicated tool 🔗 LINKEDIN OPTIONS (OPTIONAL, LinkedIn accounts only): - linkedin_options = { title? (≤255), poll? { question (≤140), options (2-4, each ≤30), duration (ONE_DAY|THREE_DAYS|SEVEN_DAYS|FOURTEEN_DAYS) } }. - LinkedIn CAROUSEL is AUTO-derived: set post_type="carousel" and attach 2+ images — no flag needed. - LinkedIn POLL: set top-level post_type="poll" and pass linkedin_options.poll. A poll MUST be text-only (no media). 👥 COLLABORATORS (OPTIONAL): - facebook_collaborators: string[] (≤10) — Facebook Reel page invites (→ facebook_options.collaborators). - instagram_collaborators: string[] (≤3) — Instagram co-author usernames (→ instagram_options.collaborators). Rejected with a 422 validation error if instagram_trial_reel.enabled=true is also set — do not combine. 🎬 INSTAGRAM TRIAL REEL (OPTIONAL, Instagram accounts only): - instagram_trial_reel = { enabled? (default false), graduation_strategy? ("SS_PERFORMANCE" default | "MANUAL") }. Publishes a reel shown to non-followers first — not on the profile grid or in follower feeds. - Requires post_type="reel" EXACTLY (not "feed+reel") and a video. Feed/carousel/story posts are rejected. - SS_PERFORMANCE auto-graduates to followers if it performs well; MANUAL requires graduating by hand in the Instagram app (no API for that). - instagram_collaborators combined with enabled=true is a 422 validation error (request fails). Instagram share-to-story, if also requested, is silently dropped for that post instead (not an error). - Not available when the workspace publishes Instagram via the mobile app. 🔀 PER-PLATFORM CONTENT OVERRIDES (OPTIONAL): - platform_overrides = { <platform>: { content: { text?, post_type?, media?: { images?, video? } } } }, keyed by facebook/instagram/twitter/linkedin/pinterest/youtube/tiktok/gmb/tumblr/threads/bluesky/telegram. - text and post_type each merge INDEPENDENTLY with the common text_content/post_type — an override with only media still inherits the common text/post_type. - media is ATOMIC: an override whose content includes a media key defines that platform's media ENTIRELY (no field-by-field fallback to the common media); omitting the media key entirely inherits the common images/video wholesale. This matters because some platforms (e.g. TikTok) can never support mixed images+video. - Omit platform_overrides entirely to publish the same common content to every platform. 🎨 FACEBOOK COLORED BACKGROUND (OPTIONAL, Facebook only): - When the user asks for a "colored background" on a text-only Facebook post, first call list_facebook_text_backgrounds to pick a preset, then pass its id as facebook_background_id. Text-only posts only. 🖼️ FACEBOOK CAROUSEL (OPTIONAL, Facebook accounts only): - Pass facebook_carousel to publish a multi-card carousel on Facebook. Its own post format — do NOT also send content images/video for the same Facebook post. - facebook_carousel = { cards: [{ image (url, required), link (url, required), title? (≤255), description? (≤1000) }] (2-10), call_to_action?, end_card?, end_card_url?, accounts? }. is_carousel_post=true is set automatically. 🧵 THREADS MULTI-THREAD (OPTIONAL, Threads accounts only): - threads = [{ message? , media? (url[], ≤10), media_ids? (string[], ≤10) }] (MAX 10). Each item needs a message OR at least one media. Threads allows mixed images+video. 🐦 TWITTER THREADED TWEETS (OPTIONAL, Twitter/X accounts only): - twitter = [{ message?, media? (url[], ≤10), media_ids? (string[], ≤10) }] (MAX 10). Each item needs a message OR at least one media. A single tweet cannot mix images+video and allows at most one video. 💬 FIRST COMMENT (OPTIONAL): - first_comment = { message (≤2000, required), accounts? }. The backend REQUIRES accounts when a message is given and they MUST be a subset of the post accounts. 📎 MEDIA LIBRARY ASSETS (OPTIONAL): media_ids (≤10) attach existing media-library assets. Alternative to images/video URLs. Required: workspace_id, text_content, accounts (array), publish_type, post_type. For content_category: use content_category_id instead of accounts. Success data: { id, post_url|null, warning? }. EXAMPLES: 1) Plain text draft: { workspace_id, text_content: "Hello world", accounts: ["ACC"], publish_type: "draft", post_type: "feed" } 2) Text + single image (scheduled): { ..., images: ["https://img/1.jpg"], publish_type: "scheduled", post_type: "feed", scheduled_at: "2026-01-10 10:00:00" } 3) LinkedIn carousel (auto-derived): { ..., accounts: ["LI_ACC"], post_type: "carousel", images: ["https://img/1.jpg","https://img/2.jpg"] } 4) LinkedIn poll (text-only): { ..., accounts: ["LI_ACC"], post_type: "poll", linkedin_options: { poll: { question: "Best day to post?", options: ["Mon","Wed","Fri"], duration: "SEVEN_DAYS" } } } 5) LinkedIn title: { ..., accounts: ["LI_ACC"], post_type: "feed", linkedin_options: { title: "Our Q3 recap" } } 6) Content category (no accounts): { workspace_id, text_content, accounts: [], publish_type: "content_category", post_type: "feed", content_category_id: "CAT" } 7) Legacy approval: { ..., approvers: ["USER_A","USER_B"], approve_option: "everyone", approval_notes: "Please review tone" } 8) Approval workflow (attach): { ..., approval_workflow: { workflow_id: "WF_ID", notes: "Route through legal" } } 9) Collaborators: { ..., accounts: ["FB_ACC"], post_type: "reel", facebook_collaborators: ["PAGE_ID"], instagram_collaborators: ["co_author"] } 10) Facebook carousel: { ..., accounts: ["FB_ACC"], post_type: "carousel", facebook_carousel: { cards: [{ image: "https://img/a.jpg", link: "https://shop/a", title: "A" }, { image: "https://img/b.jpg", link: "https://shop/b" }], call_to_action: "SHOP_NOW" } } 11) First comment: { ..., accounts: ["ACC"], first_comment: { message: "🔗 link in bio", accounts: ["ACC"] } } 12) Instagram trial reel: { ..., accounts: ["IG_ACC"], post_type: "reel", video: "https://cdn/clip.mp4", instagram_trial_reel: { enabled: true, graduation_strategy: "MANUAL" } } 13) Per-platform overrides (common content everywhere except TikTok gets its own video, no images): { ..., accounts: ["FB_ACC","TT_ACC"], post_type: "feed", images: ["https://img/a.jpg"], platform_overrides: { tiktok: { content: { media: { video: "https://cdn/clip.mp4" } } } } } |
| update_post | Update an existing post in a workspace (PUT). Sends the SAME body as create_post — resend the full post body plus the post_id to update. ⚠️ STATUS RULE: An update is ONLY allowed when the post status is NOT "published" and NOT "processing". Otherwise the backend returns 422. ✅ APPROVAL — TWO MUTUALLY EXCLUSIVE OPTIONS (pick at most one): A) LEGACY simple approval — approvers, approve_option ("anyone"|"everyone"), approval_notes. B) APPROVAL WORKFLOW (multi-level) — approval_workflow. On UPDATE there are TWO forms (still mutually exclusive with the legacy block): - ATTACH a workflow: { workflow_id, notes? } (workflow_id = id from fetch_approval_workflows). - ACT on the post's EXISTING attached workflow: { workflow_action, notes? } where workflow_action ∈ restart | resume | renotify_current | keep | remove. - Provide EXACTLY ONE of workflow_id / workflow_action. Do NOT combine A and B. All other fields behave exactly like create_post: linkedin_options (title/poll; LinkedIn carousel auto-derived from post_type="carousel" + 2+ images; poll needs post_type="poll" and text-only), facebook_collaborators (≤10), instagram_collaborators (≤3), instagram_trial_reel (enabled/graduation_strategy — requires post_type="reel" + video; silently drops collaborators/share-to-story when enabled), platform_overrides (per-platform text/post_type/media — media is atomic per platform), facebook_background_id, facebook_carousel, threads, twitter, first_comment, media_ids, labels, campaign_id. Success data: { id, post_url|null, warning? }. EXAMPLES: 1) Edit text + reschedule: { workspace_id, post_id, text_content: "Updated copy", accounts: ["ACC"], publish_type: "scheduled", post_type: "feed", scheduled_at: "2026-02-01 09:00:00" } 2) Attach a workflow: { workspace_id, post_id, text_content: "...", accounts: ["ACC"], publish_type: "draft", post_type: "feed", approval_workflow: { workflow_id: "WF_ID", notes: "Legal review" } } 3) Act on existing workflow (restart): { workspace_id, post_id, text_content: "...", accounts: ["ACC"], publish_type: "draft", post_type: "feed", approval_workflow: { workflow_action: "restart" } } 4) Remove the attached workflow: { ..., approval_workflow: { workflow_action: "remove" } } |
| delete_post | Delete a post by ID |
| fetch_approval_workflows | List the multi-level approval workflows configured for a workspace. Each workflow: { id, name, is_default, levels: [{ level_number, title, rule: "anyone"|"everyone", members: [{ user_id }] }] }. Use a workflow's id as approval_workflow.workflow_id when attaching it via create_post or update_post. |
| approve_post | Approve or reject a single post that is under review with an optional comment. |
| fetch_comments | List comments on a post with pagination. |
| add_comment | Add a comment or internal note to a post. |
| list_facebook_text_backgrounds | List the Facebook colored-background presets available for text-only posts. Call this before create_post when the user asks for a "colored background", "red background", "gradient background", etc. on a Facebook post. Pick a preset whose description matches the user's intent and pass its `id` as `facebook_background_id` to create_post. Each preset has: id, type (solid/gradient/image), background_color (CSS value), color (text color), description, category. ⚠️ Backgrounds only apply to plain text posts — if images or video are attached, create_post will reject the request. |
| fetch_optimal_times | Best time to post: analyses the historical engagement of a workspace's connected accounts and returns the highest-scoring posting times, both pooled across accounts (global) and per account (individual). A "slot" is one recommended posting time — a weekday and an hour (e.g. Wednesday 14 = 2 PM on Wednesdays), not a specific date. Slots come back ranked best-first. The slot counts below only decide how many ranked results to return; they never change the analysis. Omit `entities` to analyse every connected account — that is the usual call. Times are always returned in the workspace timezone, echoed back as `meta.timezone` — there is no timezone parameter. Use this before create_post to pick a schedule time. |
| fetch_media | List media assets (images, videos, GIFs) from a workspace's media library. Returns a paginated flat list (Laravel paginator metadata + a `data` array). Each media asset uses `id`. Use this to find media IDs/URLs to attach to posts. All filters are optional. |
| upload_media | Import a media asset into a workspace's media library from a public URL. This imports media from a publicly accessible URL only — local file upload is NOT supported via MCP. Provide a direct, public URL to an image, video, or GIF. Optionally place it in a specific folder. Returns 201 with the created media asset (uses `id`). |
| help | Interactive help and guidance for ContentStudio MCP tools |
| discover | Search the long-tail operation map by keyword across the analytics, inbox, connections, organization, team, and workspace-admin toolsets. Returns matching operation names, HTTP methods, descriptions, and parameter summaries. Examples: discover({ query: "facebook top posts" }), discover({ query: "hide comment" }), discover({ toolset: "analytics" }) to browse everything in a toolset. |
| execute_read | Run a READ-ONLY operation from the discover map by name (e.g. an Analytics report). Get the operation name and its param shape from discover first — this refuses write operations. Example: execute_read({ operation: "facebook_analytics_summary", params: { workspace_id, platform_id, start_date, end_date } }). |
| execute_write | Run a WRITE operation from the discover map by name (e.g. an Inbox action — reply, hide, tag, merge). Get the operation name and its param shape from discover first — this refuses read-only operations. Destructive operations (discover reports destructive: true) delete or irreversibly change data — confirm with the user before calling those. Example: execute_write({ operation: "inbox_hide_comment", params: { workspace_id, comment_id } }). |