{
  "serverInfo": {
    "name": "SkedCast MCP",
    "version": "1.0.0",
    "description": "Read and publish social posts, manage clients + connected accounts, and read analytics across 10 platforms — the SkedCast programmatic surface over MCP."
  },
  "transport": {
    "type": "streamable-http",
    "endpoint": "https://api.skedcast.com/mcp"
  },
  "authentication": {
    "type": "oauth2",
    "protectedResourceMetadata": "https://api.skedcast.com/.well-known/oauth-protected-resource/mcp"
  },
  "availability": {
    "status": "generally-available"
  },
  "capabilities": {
    "tools": [
      {
        "name": "list_scheduled_posts",
        "description": "List the workspace's posts (queue, drafts, published) newest-first, with optional status / client / platform filters and free-text search (`q`) over captions, per-platform overrides, client names and account handles.",
        "scope": "posts.read",
        "readOnly": true
      },
      {
        "name": "list_post_targets",
        "description": "List individual post DESTINATIONS (one row per platform account) with optional status / client / account / platform / free-text filters and a SCHEDULE-time window — the tool for \"what publishes on this date\". Carries `total` and `totalPosts`, so a caller can tell a full page from a complete set.",
        "scope": "posts.read",
        "readOnly": true
      },
      {
        "name": "get_post",
        "description": "Get one post with its per-platform variants and fan-out targets.",
        "scope": "posts.read",
        "readOnly": true
      },
      {
        "name": "list_clients",
        "description": "List the workspace's client groups (brands / influencers).",
        "scope": "clients.read",
        "readOnly": true
      },
      {
        "name": "list_accounts",
        "description": "List connected social accounts, optionally filtered by client or platform.",
        "scope": "accounts.read",
        "readOnly": true
      },
      {
        "name": "get_analytics_overview",
        "description": "Reach / impressions / engagement plus a per-platform rollup over a date window.",
        "scope": "analytics.read",
        "readOnly": true
      },
      {
        "name": "get_post_analytics",
        "description": "Per-post performance (complements get_analytics_overview): one post’s lifetime per-target breakdown by postId, or a keyset page of per-post rows with filters.",
        "scope": "analytics.read",
        "readOnly": true
      },
      {
        "name": "get_account_analytics",
        "description": "One channel’s totals and daily series, plus the two things no other tool carries: profile-level activity (profile views, link taps, video plays) and follower demographics.",
        "scope": "analytics.read",
        "readOnly": true
      },
      {
        "name": "list_library_assets",
        "description": "List content-library (DAM) assets with optional kind / folder / label / client / status / search filters. Filter `status: \"transcoded\"` for assets that are actually publishable; every row also carries `status`, `publishable` and `ready`.",
        "scope": "media.read",
        "readOnly": true
      },
      {
        "name": "get_capabilities",
        "description": "The per-platform composing rulebook: caption and hashtag limits, media rules per kind (counts, formats, file-size ceiling, duration bounds, aspect ratios), post types, native-scheduling window, default cadence, first-comment support, and the `variants[].overrides` keys each platform requires or accepts. Read it before composing instead of discovering a limit by failing a publish.",
        "scope": "posts.read",
        "readOnly": true
      },
      {
        "name": "get_agency",
        "description": "The current workspace profile — name, plan, app mode, settings, and the caller’s role.",
        "scope": "agency.read",
        "readOnly": true
      },
      {
        "name": "list_reports",
        "description": "List the workspace's saved analytics report definitions.",
        "scope": "reports.read",
        "readOnly": true
      },
      {
        "name": "get_best_times",
        "description": "Worker-derived best posting times (strongest-first), optionally by client / platform.",
        "scope": "besttimes.read",
        "readOnly": true
      },
      {
        "name": "list_approvals",
        "description": "List the workspace's approval queue (pending and decided requests), newest-first, with optional status / post filters.",
        "scope": "approvals.read",
        "readOnly": true
      },
      {
        "name": "get_posting_limits",
        "description": "Anti-ban posting limits (cadence): one account by socialAccountId, or every active channel plus the tenant bypass flag when omitted.",
        "scope": "accounts.read",
        "readOnly": true
      },
      {
        "name": "get_quota",
        "description": "Adaptive quota pool: your scarce-bucket budget (guaranteed slice, used, burst headroom, tomorrow’s guarantee tonight, reset clock) — read before planning a batch.",
        "scope": "accounts.read",
        "readOnly": true
      },
      {
        "name": "list_campaigns",
        "description": "List the workspace's campaigns (named post groupings), optionally filtered by client / status.",
        "scope": "campaigns.read",
        "readOnly": true
      },
      {
        "name": "decide_approval",
        "description": "Approve, reject, or request changes on a pending approval request (approve releases the gated post for publishing). A write action — confirm with the user before calling.",
        "scope": "approvals.decide",
        "readOnly": false
      },
      {
        "name": "validate_post",
        "description": "Dry-run a compose request and get the per-platform verdict WITHOUT writing anything — same arguments as create_post, no post row and no draft. Returns `{ valid, platforms[], estimatedCostUsd }`. Does not run cadence or quota admission, so `valid: true` means the shape is acceptable, not that a publish cannot later be deferred.",
        "scope": "posts.compose",
        "readOnly": true
      },
      {
        "name": "revise_post",
        "description": "Change the CONTENT of an already-scheduled post — caption, media, cover, alt text, per-platform overrides — without disturbing its destinations. Send only what you are changing. Refuses, naming them, when a destination has already published or is about to. An edit to an APPROVED post returns it to review. A write action — confirm with the user before calling.",
        "scope": "posts.compose",
        "readOnly": false
      },
      {
        "name": "create_post",
        "description": "Compose a post and fan it out across the selected accounts (or save it as a draft). Retry-safe: retrying with identical arguments replays the original result instead of creating a duplicate. Note: two publish-now calls with byte-identical content and targets are treated as the same post and coalesced permanently — to intentionally publish the same content twice, vary the content or use distinct scheduled times. For platform-specific settings (e.g. YouTube privacy, TikTok privacy level, Pinterest board, Telegram chat), set `variants[].overrides`. That same object carries `firstComment`: a reply posted automatically once the destination goes live, so a \"link in the comments\" post needs no manual follow-up. Supported on X, LinkedIn, YouTube, Instagram, Facebook, Threads, Telegram and Bluesky (not TikTok or Pinterest); spell it exactly `firstComment`. Overrides are free-form — an unrecognised key is accepted and silently ignored — and the comment is best-effort: if it fails the post still publishes and nothing is reported, so confirm on the published post. A write action — confirm with the user before calling.",
        "scope": "posts.compose",
        "readOnly": false
      },
      {
        "name": "update_post",
        "description": "Replace a draft post in place — its content, media, targets, and schedule. Only draft posts can be updated. Platform-specific settings live in `variants[].overrides`, same as create_post, including `firstComment`. A write action — confirm with the user before calling.",
        "scope": "posts.compose",
        "readOnly": false
      },
      {
        "name": "import_media",
        "description": "Ingest an external image / video into the content library by URL — the file must already be reachable at a public URL. For bytes you already hold (an image you just rendered, or a local file) use `upload_media` instead. A write action — confirm with the user before calling.",
        "scope": "media.manage",
        "readOnly": false
      },
      {
        "name": "upload_media",
        "description": "Upload a LOCAL image / video into the content library and get back a media id for create_post. Sends the file inline (≤18 MiB) or returns a presigned URL for larger files. A write action — confirm with the user before calling.",
        "scope": "media.manage",
        "readOnly": false
      },
      {
        "name": "reschedule_post",
        "description": "Move a pending post target to a new time (absolute scheduledAt or relative shiftMinutes). A write action — confirm with the user before calling.",
        "scope": "posts.compose",
        "readOnly": false
      },
      {
        "name": "bulk_post_target_action",
        "description": "Reschedule, retry or cancel up to 5000 post targets in one call — the batch form of reschedule_post / retry_post / cancel_post. Returns `{ jobId, accepted, skipped, skippedReasons? }`; a skipped target is reported, never silently dropped. A write action — confirm with the user before calling.",
        "scope": "posts.compose",
        "readOnly": false
      },
      {
        "name": "cancel_post",
        "description": "Cancel a still-pending post target before it publishes. A write action — confirm with the user before calling.",
        "scope": "posts.compose",
        "readOnly": false
      },
      {
        "name": "retry_post",
        "description": "Re-queue a failed or rate-limited post target so it publishes again. A write action — confirm with the user before calling.",
        "scope": "posts.compose",
        "readOnly": false
      },
      {
        "name": "set_post_recycle",
        "description": "Set, update, or disable a post's evergreen recycle settings (interval / times / end date / max recycles). A write action — confirm with the user before calling.",
        "scope": "posts.compose",
        "readOnly": false
      },
      {
        "name": "shift_campaign",
        "description": "Move a whole campaign's pending schedule by a relative number of minutes. A write action — confirm with the user before calling.",
        "scope": "campaigns.manage",
        "readOnly": false
      },
      {
        "name": "list_webhooks",
        "description": "List the workspace's outbound webhook endpoints (url, subscribed events, active flag; secret masked).",
        "scope": "webhooks.read",
        "readOnly": true
      },
      {
        "name": "create_webhook",
        "description": "Register an outbound webhook endpoint for HMAC-signed event deliveries. Subscribe to any subset of the full 40-event catalog — see /developers/webhooks, or the `webhooks` block in openapi.json, for the complete list with payload fields. Returns the signing secret once. A write action — confirm with the user before calling.",
        "scope": "webhooks.manage",
        "readOnly": false
      },
      {
        "name": "delete_webhook",
        "description": "Delete an outbound webhook endpoint by id (deliveries cascade). A write action — confirm with the user before calling.",
        "scope": "webhooks.delete",
        "readOnly": false
      },
      {
        "name": "create_client",
        "description": "Create a new client group (a brand or influencer whose accounts are managed together). A write action — confirm with the user before calling.",
        "scope": "clients.manage",
        "readOnly": false
      },
      {
        "name": "create_connect_invite",
        "description": "Mint a shareable self-connect link so a client can connect the requested platforms themselves; returns the link and its expiry. A write action — confirm with the user before calling.",
        "scope": "accounts.connect",
        "readOnly": false
      },
      {
        "name": "get_webhook",
        "description": "Read one webhook endpoint — URL, subscribed events, active state, consecutive failures, and when/why it was auto-disabled. The signing secret is never returned here.",
        "scope": "webhooks.read",
        "readOnly": true
      },
      {
        "name": "list_webhook_deliveries",
        "description": "The delivery log for one endpoint — each attempt with its event type, response status, error and timing. How you find out WHY an endpoint stopped receiving.",
        "scope": "webhooks.read",
        "readOnly": true
      },
      {
        "name": "update_webhook",
        "description": "Change an endpoint’s URL, event list, or active state — including re-enabling one auto-disabled after repeated failures. The event list REPLACES the previous one. A write action — confirm with the user before calling.",
        "scope": "webhooks.manage",
        "readOnly": false
      },
      {
        "name": "test_webhook",
        "description": "Send a `webhook.test` delivery and report the outcome, proving an endpoint is reachable and verifying signatures without waiting for a real event. Performs a real outbound request — confirm with the user before calling.",
        "scope": "webhooks.manage",
        "readOnly": false
      },
      {
        "name": "rotate_webhook_secret",
        "description": "Mint a new signing secret and return it ONCE — it is not readable again. The previous secret keeps verifying for a short overlap so the customer can redeploy without dropping deliveries. A write action — confirm with the user before calling.",
        "scope": "webhooks.manage",
        "readOnly": false
      },
      {
        "name": "get_campaign",
        "description": "Read one campaign in full — name, description, status, colour, date range, owning client and post counts.",
        "scope": "campaigns.read",
        "readOnly": true
      },
      {
        "name": "create_campaign",
        "description": "Create a campaign — a named group of posts with an optional client, colour and date range. Posts are added with assign_to_campaign. A write action — confirm with the user before calling.",
        "scope": "campaigns.manage",
        "readOnly": false
      },
      {
        "name": "update_campaign",
        "description": "Change a campaign’s name, description, status, colour or date range. A write action — confirm with the user before calling.",
        "scope": "campaigns.manage",
        "readOnly": false
      },
      {
        "name": "delete_campaign",
        "description": "Delete a campaign. The posts in it are NOT deleted — they lose the grouping and keep their own schedules. A write action — confirm with the user before calling.",
        "scope": "campaigns.manage",
        "readOnly": false
      },
      {
        "name": "assign_to_campaign",
        "description": "Add up to 500 existing posts to a campaign. Grouping only — no post’s content or schedule changes. A write action — confirm with the user before calling.",
        "scope": "campaigns.manage",
        "readOnly": false
      },
      {
        "name": "unassign_from_campaign",
        "description": "Remove posts from a campaign. Grouping only — the posts and their schedules are untouched. A write action — confirm with the user before calling.",
        "scope": "campaigns.manage",
        "readOnly": false
      },
      {
        "name": "get_client",
        "description": "Read one client group in full — name, type, approval policy, posting rules, brand voice and default timezone.",
        "scope": "clients.read",
        "readOnly": true
      },
      {
        "name": "update_client",
        "description": "Change a client’s name, type, approval policy, posting rules, brand voice or default timezone. A write action — confirm with the user before calling.",
        "scope": "clients.manage",
        "readOnly": false
      },
      {
        "name": "delete_client",
        "description": "Archive a client group. OWNER ONLY — a Manager holding the clients.manage scope cannot do this. A write action — confirm with the user before calling.",
        "scope": "clients.manage",
        "readOnly": false
      },
      {
        "name": "get_post_target",
        "description": "Read a single fan-out destination in full — status, scheduled time, platform, account, published permalink and external id, and its failure reason when it failed.",
        "scope": "posts.read",
        "readOnly": true
      },
      {
        "name": "get_post_recycle",
        "description": "Read one post’s evergreen re-posting configuration and the worker’s bookkeeping. Returns null when the post is not enrolled.",
        "scope": "posts.read",
        "readOnly": true
      },
      {
        "name": "get_account",
        "description": "Read one connected social account in full — platform, handle, display name, avatar, status (including whether it needs re-authorisation) and owning client.",
        "scope": "accounts.read",
        "readOnly": true
      },
      {
        "name": "get_approval",
        "description": "Read one approval request with the gated post’s preview and roll-up, so a reviewer can see what they are deciding on before calling decide_approval.",
        "scope": "approvals.read",
        "readOnly": true
      },
      {
        "name": "get_media_asset",
        "description": "Read one content-library asset in full — filename, kind, size, dimensions, duration, folder, labels, alt text, pipeline status, and the publishable/ready flags plus the per-platform readiness map.",
        "scope": "media.read",
        "readOnly": true
      },
      {
        "name": "get_client_analytics",
        "description": "One client group’s analytics — totals, a per-platform breakdown and a time series. A different projection from get_analytics_overview filtered by client.",
        "scope": "analytics.read",
        "readOnly": true
      },
      {
        "name": "get_report",
        "description": "Read one report definition with its run history — each run’s status, window, and whether an export is available.",
        "scope": "reports.read",
        "readOnly": true
      },
      {
        "name": "download_report_export",
        "description": "Get a time-limited download URL for one completed report run’s export file. The URL expires — fetch it when you are ready to use it.",
        "scope": "reports.read",
        "readOnly": true
      },
      {
        "name": "clear_post_recycle",
        "description": "Turn OFF evergreen re-posting for a post. Idempotent. Already-scheduled recycled copies are not retracted — cancel those separately. A write action — confirm with the user before calling.",
        "scope": "posts.compose",
        "readOnly": false
      }
    ]
  }
}