omnisocials

Official OmniSocials MCP server — manage social media from any MCP client.

Community: Submitted by a user or imported; check the owner before granting accessOnlineAPI key requiredGlobalFreeCan modify data

What it can do

  • List Accounts: List all connected social media accounts for the active workspace. If the user has multiple workspaces, call list_workspaces first so they can pick which one to work with.
  • Get Account: Get details of a specific connected social media account.
  • List Posts: List posts in the workspace. Filter by status to see drafts, scheduled, published, or failed posts. Returns a formatted table with content preview, channels, dates, and post IDs. The conte

What data it sees

Do you need an account

An API key from the service settings is required

Official OmniSocials MCP server — manage social media from any MCP client.

Schedule and publish posts, stories, and reels across 11 platforms: Instagram, Facebook, LinkedIn (Profile + Company Page), YouTube, TikTok, X, Pinterest, Bluesky, Threads, Mastodon, and Google Business.

Capabilities (42 tools)

  • Posts — create, update, delete, publish, retry, list, and read full post details, including per-platform captions, media, and X/Bluesky/Mastodon/Threads chained threads
  • Media — upload images/video/PDF (auto-split into carousel slides), manage folders, check platform compatibility before posting
  • Analytics — per-post and account-level stats, best-times-to-post recommendations
  • Hashtag Sets — save and reuse tagged groups across posts
  • Social Inbox — read and reply to DMs, comments, and mentions
  • Webhooks — subscribe to post lifecycle events

Authentication Requires an OmniSocials API key (Settings → API in the app, starts with omsk_live_ or omsk_test_), or OAuth.

Docs: https://docs.omnisocials.com Get an account: https://omnisocials.com

Server tool list (42)

Raw names from tools/list. Only developers need these.

list_accountsList all connected social media accounts for the active workspace. If the user has multiple workspaces, call list_workspaces first so they can pick which one to work with.
get_accountGet details of a specific connected social media account.
list_postsList posts in the workspace. Filter by status to see drafts, scheduled, published, or failed posts. Returns a formatted table with content preview, channels, dates, and post IDs. The content cell shows the default caption; if a post has per-platform overrides (e.g. a custom X version) the preview is suffixed with *(per-platform)* — call `get_post` to see every variant.
get_recent_platform_postsFetch the user's most recent posts straight from their connected platform APIs (Instagram, TikTok, X, YouTube, Facebook, LinkedIn, and more), INCLUDING content published outside OmniSocials. Use this when list_posts is empty — e.g. a brand-new workspace that has not published through OmniSocials yet — so you can still analyze the user's real content. Each post includes normalized `engagement` plus every raw metric the platform reported (Instagram: reach/views/saves/shares from per-post insights; TikTok: average_time_watched/full_video_watched_rate/total_time_watched/favorites/reach when the workspace enabled TikTok comments). Metrics only appear where the platform exposes them for historical posts (X, TikTok, Bluesky, Mastodon, Instagram, Facebook, YouTube); Threads, Pinterest, and Google Business return captions only. Records also carry `duration_seconds` — the video length in whole seconds — where the platform's listing API reports it (currently TikTok and YouTube); null for images and platforms that don't expose it. LinkedIn personal profiles can't be listed live (LinkedIn grants apps no such permission), so their results are posts published through OmniSocials with their latest collected stats. Fetched live for most platforms, so expect a few seconds of latency; X results may come from a snapshot up to 24h old (X bills per returned post) — the snapshot refreshes right after the user publishes to X through OmniSocials. Output is a human-readable summary table PLUS a 'Structured data' JSON block carrying, for every post, the platform's own post id (the stable dedupe key), a permalink, the FULL untruncated caption, and exact-integer metrics — use that block when ingesting or storing native posts rather than the rounded/truncated table. Requires the analytics:read scope.
get_postGet full details of a specific post including content, channels, media (with URLs), first comment, Instagram collaborators/user tags/location/Trial Reel state/Reel cover (thumbnail_type + thumb_offset in ms), dates, live URLs, per-platform publish errors, and retry linkage (`retry_of` / `retries` — a published post with empty published_urls and `retries` set is a resolved failure whose live URLs live on the retry post) — enough to fully verify a scheduled post without opening the dashboard. When a post has per-platform caption overrides (e.g. a shorter X version alongside the default), every variant is rendered as its own labeled block under `### Content` so you can see exactly what each platform will publish. X threads are rendered under `### X Thread` with each tweet labeled in publish order — read this to see the full chained tweet text, since thread-only posts have no caption in `content`. A LinkedIn poll is rendered under `### LinkedIn Profile Poll` and/or `### LinkedIn Page Poll` (independent per channel) with the question, options, and duration. After a post is published, `published_urls` maps each platform to the live post URL (only platforms that successfully posted appear).
get_calendarGet a content calendar showing scheduled and published posts organized by day of the week.
create_postCreate a new social media post, story, or reel. IMPORTANT — Before calling this tool, make sure you have all required information from the user. If anything is missing, ASK the user before calling: 0. **Workspace**: If the user has only one workspace, just use it (no need to ask). If the user has named a workspace in their request, switch to it once and remember it for the rest of the conversation. If multiple workspaces exist and the user has NOT named one, call `list_workspaces` and ASK which workspace to post to before continuing. Never silently pick a workspace when more than one exists — each workspace has different connected channels and different audiences. After a successful create/schedule/publish, mention the workspace name in your reply for clarity (e.g. "Scheduled to the Acme workspace for Tuesday 3pm"). 1. **Content/caption**: What text should the post have? If captions differ per platform, use one call with an object: { "default": "fallback text", "linkedin": "long version", "threads": "short version", "x": "short version", "bluesky": "short version" }. Always prefer one call per topic. RESPECT character limits per platform (see below). 2. **Channels**: Which platforms to post to? Call `list_accounts` to show available options for the active workspace. Ask the user which channels to use. 3. **Schedule**: When should it be published? (Or save as draft?) 4. **Media** (REQUIRED for some types): - Stories: ALWAYS require an image or video. A story can carry up to 10 media items: each item is one slide, published as its own story in the order given. Ask the user which slides and in what order. - Reels: ALWAYS require a video. - Instagram posts: ALWAYS require at least one image or video. - TikTok posts: ALWAYS require at least one image or video. - Pinterest posts: ALWAYS require an image AND a board_id. - Other platforms (LinkedIn Profile, LinkedIn Page, X, Bluesky, etc.): Media is optional. 5. **Platform-specific options** (ask only when relevant): - **Pinterest board (auto-default to first board)**: If Pinterest is in `channels` and `pinterest.board_id` is NOT provided, do NOT block on asking the user — and do NOT skip Pinterest. Instead: 1. Call `get_account` on the Pinterest account — the response includes a `Pinterest Boards` table with each board's name and ID. 2. Use the FIRST board in that list as `pinterest.board_id` automatically. 3. In your reply, explicitly mention which board you used (e.g. "Posted to your 'Marketing' board on Pinterest — let me know if you'd prefer a different one and I'll move it.") so the user can correct course. If the user named a board ("post to my Marketing board") or specified one in the request, match it (case-insensitive) against the list and use that one instead of the first. - YouTube: Title, privacy status, tags? - TikTok: Privacy level? - **X threads vs long-form**: A chained "thread" (the user explicitly asks for one) → pass `x.thread_parts` as an array of 2–25 `{ text }` objects (each ≤ 280 chars); the top-level `content` is then ignored for X. A single **long-form** post on a Premium / Premium+ account → just put the full text (up to 25,000 chars) in `content` — no threading needed (check `platform_details.subscription_type` via list_accounts). On free / Basic, X caps a single post at 280 chars, so either split into a thread or shorten. Never cram "1/", "2/" prefixes into `content` — that posts one tweet, not a thread. - **X posts containing a link cost credits**: X bills API posts whose text contains a URL at a premium; OmniSocials passes that through as prepaid credits at X's exact rate (20 credits ≈ $0.20 per URL-containing tweet; threads charged per link-containing part). The create response includes a `warnings` entry (`x_url_post_credits`) with the cost and current balance — relay it to the user. Credits are only deducted after the post successfully publishes; a failed publish is never charged. If the balance can't cover it at publish time, only the X target fails (its error says to top up at https://app.omnisocials.com/credits) and the post can be retried after topping up. Posts without links stay free — never remove a user's link to dodge the fee without asking them. Scheduling is also gated up front: every scheduled X link post reserves its cost, and a create/schedule that would push the reserved total past the balance is refused with a 402 `x_credits_insufficient` error (details carry credits_required / credits_balance / credits_reserved) — tell the user to top up or remove the link, don't silently retry. **VIDEO DURATION CAPS — MUST RESPECT THESE (returns 400 validation_error when exceeded):** | Platform | Post mode | Reel mode | |----------|-----------|-----------| | Facebook | 240 min | **90 s** | | YouTube Short | (no Post mode) | **3 min** | | X | 140 s | N/A | | Bluesky | 180 s | N/A | | Threads | 5 min | N/A | | TikTok | 10 min | 10 min | | LinkedIn / LinkedIn Page | 10 min | N/A | | Instagram | 15 min | 15 min | | Pinterest | 15 min | N/A | | Reddit | 15 min | N/A | | Mastodon | (instance-dependent) | N/A | **VIDEO FILE-SIZE CAPS — MUST RESPECT THESE (validated via ffprobe at schedule/publish time; drafts exempt):** | Platform | Video cap | |----------|-----------| | Mastodon | 99 MB | | Bluesky | 100 MB | | Instagram | 300 MB | | X (free tier) | 512 MB | | Threads / Reddit | 1 GB | | Pinterest | 2 GB | | Facebook / TikTok | 4 GB | | LinkedIn / LinkedIn Page | 5 GB | | YouTube | 256 GB | Upload requests are capped at **100 MB** on top of these; anything bigger gets rejected with `code: file_too_large` before the validator runs. Cap-violation error shape (one sentence per offending platform): ```json { "error": { "code": "validation_error", "message": "Facebook only allows videos up to 1min 30s — yours is 1min 34s. Trim the video or deselect Facebook." } } ``` Before sending an oversized video, warn the user and offer to trim, deselect that platform, or split. Drafts can still be created without media — the validator only runs when transitioning to scheduled or publish_now. **CHARACTER LIMITS — MUST RESPECT THESE:** | Platform | Max chars | Notes | |----------|-----------|-------| | X (free / Basic) | 280 | Long posts require Premium or Premium+ — Basic does NOT count | | X (Premium / Premium+) | 25,000 | Check platform_details.subscription_type on the account | | Bluesky | 300 | Counted in graphemes | | Mastodon | 500 | | | Threads | 500 | | | YouTube | 500 | Description field | | Pinterest | 500 | Pin description | | Instagram | 2,200 | Caption | | TikTok | 2,200 | | | LinkedIn | 3,000 | | | Facebook | 63,206 | Very generous | When posting to multiple platforms with different limits, ALWAYS use per-platform captions. For example, if posting to LinkedIn (3000 chars) and X (280 chars), use: { "default": "full version", "x": "shortened version" }. Never post content that exceeds a platform's limit. Channel IDs you can pass: instagram, facebook, threads, linkedin (personal profile), linkedin_page (company page), youtube, tiktok, pinterest, x, bluesky, mastodon. `linkedin` and `linkedin_page` are independent. A workspace can have both connected and post to each separately. Always confirm with list_accounts which are actually connected. Do NOT call this tool without media when creating stories, reels, Instagram posts, TikTok posts, or Pinterest posts — it will fail. **IMPORTANT - When the user shares an image or screenshot in chat**: You MUST upload it before creating the post. Do NOT skip the image. Do NOT create a text-only draft when an image was provided. Follow these steps: 1. Call `upload_media` with `method="upload_url"` to get upload instructions 2. Use code execution to upload the image file to OmniSocials 3. Use the returned media ID in `media_ids` when creating the post Only if code execution is completely unavailable, save as a draft and tell the user to add the image at app.omnisocials.com.
create_and_publish_postCreate a new post and publish it immediately (no scheduling). Same media rules, channel IDs, and workspace selection rule as create_post apply (only one workspace → use it; multiple workspaces with no named one → ask first; named workspace → use and remember; mention workspace name on success). `linkedin` (personal profile) and `linkedin_page` (company page) are independent channels. **Pinterest board (auto-default to first board)**: If Pinterest is in `channels` and `pinterest.board_id` is NOT provided, do NOT block on asking — and do NOT skip Pinterest. Call `get_account` on the Pinterest account, take the FIRST board from the returned boards list, and pass its `id` as `pinterest.board_id`. In your reply, mention which board you used (e.g. "Published to your 'Marketing' board on Pinterest — let me know if you'd prefer a different one.") so the user can redirect. If the user named a specific board in the request, match it (case-insensitive) against the list and use that one instead. **X threads**: For a chained X thread, pass `x.thread_parts` (2–25 `{ text }` parts, each <= 280 chars). Do NOT split into "1/", "2/" inside `content` — that posts a single tweet, not a thread. **X posts containing a link cost credits**: X bills API posts whose text contains a URL at a premium; OmniSocials passes that through as prepaid credits at X's exact rate (20 credits ≈ $0.20 per URL-containing tweet). Credits are only deducted once the post publishes successfully (a failed publish is never charged) — if the balance can't cover it at publish time, the X target alone fails with a top-up message (https://app.omnisocials.com/credits) while other platforms still publish. Relay any `x_url_post_credits` warning and X failure to the user; never strip their link to avoid the fee without asking. If the balance (minus credits reserved by scheduled X link posts) can't cover this post, the request is refused up front with a 402 `x_credits_insufficient` error instead of failing at publish.
update_postUpdate an existing post. Only draft and scheduled posts can be updated. **Per-platform options (`youtube`, `pinterest`, `instagram`, `tiktok`, `google_business`)** are accepted here, same shape as in `create_post`. Pass an object — never a JSON-encoded string. For YouTube Shorts, the **title** lives at `youtube.title`; the **video description** lives in `content` (or `content.youtube` for a per-platform override). They are not the same field — changing the caption does NOT rename the Short. **X threads**: To convert an existing draft into a chained X thread, pass `x.thread_parts` as a 2–25 entry array of `{ text }` objects (each ≤ 280 chars). Pass `x.thread_parts: null` to revert to single-tweet mode. Do NOT shove "1/", "2/" into `content` — that's a single tweet, not a thread.
delete_postDelete a post by ID. This action cannot be undone.
publish_postPublish a draft or scheduled post immediately. Only draft and scheduled posts can be published — for a failed or partially failed (warning) post use `retry_post` instead, which re-publishes only the failed platforms.
retry_postRetry the failed platforms of a failed or partially failed post — on the same post, no duplicate created. Use when a post has status `failed` (every platform failed) or `warning` (some failed, some published): only the FAILED platforms are re-published; platforms that already succeeded are never posted again. Runs asynchronously (usually within a few minutes) — poll `get_post` afterwards: on success the status becomes `published` and `published_urls` gains the platform's live URL. Max 3 retries per platform; after that, recreate the post. Note `publish_post` refuses failed posts; this is the tool for them.
search_locationsSearch for an Instagram location to tag on a post. Use this whenever the user wants to post WITH a place/location (e.g. "tag my dealership", "post this at the café"). It returns matching real venues with their addresses and a location ID. Flow: call with the place name → present the options → once the user picks, pass that result's `id` as `location_id` on create_post / create_and_publish_post / update_post. Instagram only. Notes: use a SPECIFIC venue name (a broad brand like "Starbucks" returns individual store locations). If the user already has a numeric Facebook Place ID, you can pass it straight to create_post; it's validated at publish and a bad one returns a clear error. If results are empty with a permission note, the workspace's Facebook app can't search arbitrary places — tell the user to use their own business Page's ID.
search_instagram_audioSearch Instagram's licensed music catalog for a track to attach to a REEL. Use this whenever the user wants to post a reel WITH music/a song/a sound (e.g. "post this reel with trending audio", "add that song to it"). Flow: call with a song/artist/keyword (or NO query for currently trending audio) → present the options → once the user picks, pass that result's `audio_id` as `instagram.audio_id` on create_post / create_and_publish_post / update_post. Optionally mix with `instagram.audio_volume` / `instagram.video_volume` (0-100; set video_volume 0 to fully replace the video's own sound). Instagram Reels only — feed posts, carousels, and Stories can't take music via the API (Meta limitation). Notes: only tracks Meta licenses for third-party publishing appear, so the selection can differ from the Instagram app. Requires the workspace to have a Facebook account connected whose Page is linked to this Instagram account — if results say Facebook is needed, tell the user to connect it under Settings → Organisation → Workspaces.
list_mediaList media files in the workspace. Shows each file's name, folder, type, size, a preview link, and its media ID for use when creating posts. To reuse an existing graphic, SEARCH for it by name here FIRST instead of re-uploading — re-uploading the same image creates duplicates. Organize assets into folders with create_folder / list_folders.
upload_mediaUpload media to the library. Three methods supported: 1. **url** — import from a PUBLIC https URL (e.g. a hosted image link). NEVER pass a local sandbox path — the URL must be reachable from our servers. 2. **base64_data** — pass base64-encoded file data directly. ONLY use this for tiny files (≲50 KB). MCP tool inputs are token-capped — anything larger gets silently truncated and uploads a corrupted file. For any user-pasted image, use upload_url instead. 3. **upload_url** — PREFERRED for any file the user attached to the chat. Call with method="upload_url" to get a one-time presigned URL plus a ready-to-run snippet. Execute the snippet in your code-execution tool against the actual file path. The response JSON contains the media id you pass to create_post. Size limits: base64/direct uploads are capped at **100 MB**. For anything larger — up to **1 GB** — use a public **url** (fetched server-side, bypasses the cap), OR have the user upload the file in the OmniSocials Library UI at https://app.omnisocials.com/library . IMPORTANT: if the user has a large LOCAL file (over ~100 MB) with no public url, do NOT tell them to compress it — point them to the Library UI link above. Large videos (over 100 MB) are processed in the background — the response status is "processing" and the file is NOT usable in a post until it becomes "ready" (re-check with list_media). Compatibility: every upload response includes a "compatibility" summary of any CONNECTED platforms that would reject the file (e.g. too large for Instagram). If there are warnings, RELAY them and ask the user whether to continue before posting — the file still uploads and can post to platforms that accept it. To check BEFORE uploading, call check_media_compatibility first. PDF = carousel: upload a PDF (via a public url, or base64_data with mime_type "application/pdf", or the upload_url snippet) and it is split into one image slide per page (max 20). The response lists a Media ID for EVERY slide — pass ALL of them, in order, as media_ids to create_post to post the deck as a carousel. On LinkedIn the slides post as a native swipeable DOCUMENT; on Instagram, TikTok, Threads and Pinterest as an image carousel. This is how a user posts an existing slide deck (Canva/PowerPoint/Figma exported to PDF) as a carousel. Supported: JPEG, PNG, GIF, WebP, MP4, MOV, AVI, PDF. For user-attached files: ALWAYS try upload_url first. Call upload_media with method="upload_url", then in your code-execution sandbox run the returned Python (ChatGPT Code Interpreter, file at /mnt/data/<filename>) or curl (Claude Code Execution) snippet against the actual file path. Only react to a failure AFTER actually executing the snippet.
check_media_compatibilityCheck whether a video/image will be accepted by the platforms CONNECTED to this workspace, BEFORE uploading or posting. Use this when the user gives you a file (URL) so you can warn them upfront — e.g. "this 995 MB video won't post to Instagram (max 300 MB), continue?" — then confirm before uploading. Provide ONE of: a public 'url' (size/type read via HEAD), an existing 'media_id', or 'size_bytes' + 'mime'.
delete_mediaDelete a media file by ID.
update_mediaRename an existing media file or move it into a folder (so it's findable later instead of re-uploading it). Only the fields you pass are changed.
list_foldersList the media-library folders in this workspace (Finder-style organization). Returns each folder's id, name, parent, and item count. Use a folder id with list_media (folder_id) or update_media (folder_id), or a folder name with upload_media (folder).
create_folderCreate a media-library folder. Use it to organize assets (e.g. a 'win-graphics' folder), then upload into it with upload_media (folder) or move files with update_media (folder_id).
list_hashtag_setsList the saved hashtag sets in this workspace. Apply one to a new post by passing its name via create_post (hashtag_set) — the tags are appended to the captions or, with hashtag_placement='first_comment', posted as the auto first comment.
create_hashtag_setSave a reusable, named group of hashtags (e.g. 'Fitness Brand' -> #fitness #gym #workout). Tags may include or omit the leading '#'; they are deduped case-insensitively and kept in order (max 100). Then apply the set to any new post via create_post (hashtag_set).
update_hashtag_setRename a hashtag set and/or replace its tags. 'hashtags' replaces the FULL list — to add or remove tags, pass the complete new list (see list_hashtag_sets for the current tags). Posts that already used the set are unaffected.
delete_hashtag_setDelete a saved hashtag set. Posts that already used it keep their hashtags — the tags were merged into their captions at create time.
get_post_analyticsGet detailed analytics for a specific published post. Renders every metric the platform reported — impressions, reach, views, saves, likes, comments, shares, clicks, engagements, and more — per platform. TikTok videos also carry average_time_watched, full_video_watched_rate, total_time_watched, favorites and reach when the workspace enabled TikTok comments (Business API authorization).
get_posts_analyticsGet analytics for several published posts at once (up to 100), each totalled into impressions and engagements. Use this instead of calling get_post_analytics in a loop — it is a single request rather than one per post.
get_analytics_overviewGet analytics overview with total posts, impressions, engagements, engagement rate, and per-platform breakdown. Use `period` for a rolling window (7d / 30d / 90d) or `start_date` + `end_date` for a custom range. The response echoes the resolved range and today's date — read those before assuming a year from training data (e.g. when the user says 'April', use the most recent April, not April from your training cutoff).
get_account_analyticsGet account-level analytics for all connected platforms: followers, following, posts, and the day values the platform reports for the snapshot date (impressions/views, reach, engagement, profile_views, link_clicks, follows_gained, follows_lost, Google Business calls/direction_requests/website_clicks/average_rating/review_count, Pinterest monthly_views). Rows with day values carry `period: "daily"`. Audience objects appear when available: `demographics` (+ demographics_unit) and `online_followers` (UTC hour to followers online). Present as a sorted table by follower count. Metric semantics: LinkedIn profile rows keep a lifetime total under `impressions_lifetime` (all content ever, including posts published outside OmniSocials) and `period: "lifetime"` when no daily breakdown is served. A missing key means the platform does not report it. Each metrics object carries a `note` explaining its scope where semantics are non-obvious; always relay that note to the user.
get_best_timesGet recommended posting times (day + hour) for one platform, computed from the workspace's own posting history: publish time × engagement of every post published there, recency-weighted and outlier-damped, bucketed in the user's timezone, and blended with when the account's followers are online when the platform provides that (Instagram, TikTok Business; `basis: own_data_and_audience`, response carries `audience_online`). Returns the top 3 recommended slots plus a per-day breakdown. When the workspace has fewer than 15 analyzed posts on the platform, the audience-online profile alone is used (`basis: audience`) or industry-average defaults are returned (`basis: defaults`) with how many more posts unlock personalized recommendations — tell the user that so the numbers aren't mistaken for their own audience data. Use this before scheduling when the user asks 'when should I post?' or hasn't specified a time. Requires the analytics:read scope.
list_webhooksList all configured webhooks with their status, events, and last trigger time.
create_webhookCreate a new webhook. Available events: post.scheduled, post.published, post.failed
get_webhookGet details of a specific webhook by ID.
update_webhookUpdate a webhook's URL, events, or active status.
delete_webhookDelete a webhook by ID.
rotate_webhook_secretRotate the signing secret for a webhook. The new secret will only be shown once.
list_inbox_conversationsList social inbox conversations (Instagram/Facebook DMs, comments, mentions, LinkedIn company-page comments/mentions, and X DMs where the workspace has opted into X DMs), newest activity first. Cursor-paginated: pass the returned cursor to get the next page.
get_inbox_conversationGet the full message history of one inbox conversation, oldest first. Use the conversation_id from list_inbox_conversations.
mark_inbox_readMark all incoming messages in a conversation as read.
reply_to_inboxReply to an existing inbox conversation (DM, comment, or mention) on Instagram, Facebook, LinkedIn, or X. You can only reply to conversations that already exist. Meta direct-message replies must be within the platform's 24-hour messaging window. X replies are DM-only and use 2 prepaid credits per send (X's API fee passed through at cost) — a 402 insufficient_credits error means the organisation needs to top up at https://app.omnisocials.com/credits; relay that link to the user rather than retrying.
list_workspacesList all workspaces the user has access to. Workspace selection rule: if only one workspace is available, just use it (no need to ask). If the user has named a workspace in their request (e.g. 'post to my Acme workspace'), proceed with that workspace and remember it for the rest of the conversation. If multiple workspaces exist and the user has NOT named one, call this tool, present the list, and ASK the user which one to use before posting, switching, or fetching analytics. After a successful action, mention the workspace name in your reply for clarity.
switch_workspaceSwitch the active workspace by NAME. All subsequent tool calls (posts, media, analytics, etc.) will operate on the selected workspace. Pass the workspace name exactly as shown in list_workspaces (its id or list position also work, but the name is preferred and unambiguous). Only call this when the user has explicitly named the target workspace, or after the user has picked one from list_workspaces. Never call switch_workspace silently when the user hasn't specified a workspace - ask first.