{
  "serverInfo": {
    "name": "sprkly-mcp",
    "title": "sprkly",
    "version": "1.0.0",
    "description": "Schedule and manage social media posts across Instagram, TikTok, YouTube, Threads and Facebook. Read the queue, draft and validate captions, schedule and reschedule posts, and route anything sensitive through human approval.",
    "websiteUrl": "https://sprkly.app",
    "icons": [
      {
        "src": "https://sprkly.app/icons/icon-192.png",
        "mimeType": "image/png",
        "sizes": [
          "192x192"
        ]
      },
      {
        "src": "https://sprkly.app/icons/icon-512.png",
        "mimeType": "image/png",
        "sizes": [
          "512x512"
        ]
      },
      {
        "src": "https://sprkly.app/favicon.svg",
        "mimeType": "image/svg+xml",
        "sizes": [
          "any"
        ]
      }
    ]
  },
  "transport": {
    "type": "streamable-http",
    "endpoint": "https://sprkly.app/api/mcp"
  },
  "protocolVersion": "2025-11-25",
  "capabilities": {
    "tools": {
      "listChanged": false
    }
  },
  "auth": {
    "methods": [
      {
        "type": "oauth2",
        "description": "OAuth 2.1 with PKCE. Dynamic Client Registration and Client ID Metadata Documents are both supported.",
        "protectedResourceMetadata": "https://sprkly.app/.well-known/oauth-protected-resource/api/mcp",
        "authorizationServer": "https://sprkly.app",
        "scopesSupported": [
          "profile",
          "mcp:read",
          "mcp:write"
        ]
      },
      {
        "type": "bearer",
        "description": "A sprkly API key (sk_live_…) sent as Authorization: Bearer. Create one in Settings."
      }
    ],
    "docsUrl": "https://sprkly.app/docs/mcp"
  },
  "tools": [
    {
      "name": "sprkly_add_media_from_url",
      "title": "Add media from a URL",
      "description": "Download an image or video from a public link into sprkly and get a media_id back, for reuse across several posts. You usually do NOT need this: sprkly_schedule_post accepts a link directly in media_urls and pulls it into storage itself whenever the target platform requires that. Reach for this tool only when the user wants one media_id to attach to more than one post. Google Drive and Dropbox share links are converted automatically; the file must be shared publicly. Limit 50 MB.",
      "annotations": {
        "title": "Add media from a URL",
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": true
      }
    },
    {
      "name": "sprkly_delete_scheduled_post",
      "title": "Delete a scheduled post",
      "description": "Remove a post from the queue. This is a soft delete. The user can restore it from the Deleted tab for 30 days. Posts that have already published cannot be deleted this way. Always confirm with the user before calling.",
      "annotations": {
        "title": "Delete a scheduled post",
        "readOnlyHint": false,
        "destructiveHint": true,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "sprkly_draft_post",
      "title": "Draft a post",
      "description": "Compose a caption from a content hint and save it as a draft in sprkly, shaped to the tightest caption limit among the target platforms. Returns a draft id; the draft appears under /drafts for the user to review.",
      "annotations": {
        "title": "Draft a post",
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "sprkly_export_automation_template",
      "title": "Export an auto reply template",
      "description": "Download one Instagram auto reply as a shareable template. The file holds the trigger, the keyword and the message, and never a post id, a profile id or any account details, so it is safe to send to someone else. An auto reply that is still a draft has nothing published to export.",
      "annotations": {
        "title": "Export an auto reply template",
        "readOnlyHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "sprkly_get_account_summary",
      "title": "Get account summary",
      "description": "Plan tier, trial state, connected account count, scheduled post counts by status, and the next three upcoming posts. Never returns tokens or secrets.",
      "annotations": {
        "title": "Get account summary",
        "readOnlyHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "sprkly_get_analytics",
      "title": "Get post performance",
      "description": "How the user's published posts actually performed: total views and engagement, week-on-week / month-on-month / year-on-year change, their best posting hour, weekday and content category, and the top posts behind those numbers. Every recommendation carries a `samples` count — say how thin the evidence is rather than presenting a one-post pattern as a finding. Every period-on-period percentage carries the post counts and raw totals it came from: quote those, because a big percentage off a tiny base is not a big change. `topPosts` is grouped by platform and ranked only inside each group; `relativeToPlatformBest` compares a post with others on its OWN platform and never across platforms, so use the absolute `value` and its `metric` label to weigh one platform against another. Instagram contributes likes and comments only, and Threads and Facebook produce no metrics at all, so read `coverage` before comparing platforms.",
      "annotations": {
        "title": "Get post performance",
        "readOnlyHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "sprkly_get_billing_summary",
      "title": "Get billing summary",
      "description": "Subscription status, current plan, period end, purchased handles and the last few billing events. No payment method details; the Stripe customer id is truncated.",
      "annotations": {
        "title": "Get billing summary",
        "readOnlyHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "sprkly_get_post_approval_status",
      "title": "Get approval status",
      "description": "Whether a post is awaiting human review, approved or rejected, including reviewer notes and timestamps.",
      "annotations": {
        "title": "Get approval status",
        "readOnlyHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "sprkly_get_post_status",
      "title": "Get post status",
      "description": "Full detail for one post: status, targets, scheduled and published times, permalink, and the failure reason if it did not publish. Media comes back as mediaIds in slide order, not as links. Ids and profile ids are plumbing: talk to the user about accounts by handle and about posts by their caption, and do not read ids out unless they ask for one.",
      "annotations": {
        "title": "Get post status",
        "readOnlyHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "sprkly_get_tiktok_posting_options",
      "title": "Get TikTok posting options",
      "description": "This creator's allowed TikTok privacy levels and interaction settings, fetched live from TikTok. You usually do NOT need this before scheduling: sprkly_schedule_post checks privacyLevel against this same list itself and, when it is wrong, returns the levels that would work. Call this only when the user asks what their options are, or you want to offer them a choice.",
      "annotations": {
        "title": "Get TikTok posting options",
        "readOnlyHint": true,
        "openWorldHint": true
      }
    },
    {
      "name": "sprkly_import_automation_template",
      "title": "Import an auto reply template",
      "description": "Read an auto reply template, and optionally set it up. With no profile_id NOTHING is written: the template is checked and handed back, which is the safe first call. With a profile_id the automation is created, and it goes live when the trigger can be satisfied. A keyword or every-comment trigger needs post_id as well; without one it saves as a draft that sends nothing until a post is chosen.",
      "annotations": {
        "title": "Import an auto reply template",
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "sprkly_list_connected_social_accounts",
      "title": "List connected accounts",
      "description": "Every ACTIVE social account linked to this sprkly account: platform, handle, follower count, and whether it needs reconnecting. Disconnected/inactive accounts are never listed, so any profileId returned here is a valid posting target. Never returns access tokens.",
      "annotations": {
        "title": "List connected accounts",
        "readOnlyHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "sprkly_list_profiles",
      "title": "List posting targets",
      "description": "The profile ids needed to target a post, with each one's platform and handle. Call this before sprkly_schedule_post.",
      "annotations": {
        "title": "List posting targets",
        "readOnlyHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "sprkly_list_scheduled_posts",
      "title": "List scheduled posts",
      "description": "The post queue, newest first, with a caption preview, targets, status and failure reason. Supports a status filter and cursor pagination.",
      "annotations": {
        "title": "List scheduled posts",
        "readOnlyHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "sprkly_request_post_approval",
      "title": "Request human approval",
      "description": "Submit a draft post for human review. Moves the post to pending_approval and returns an approval id to poll with sprkly_get_post_approval_status. Use this when the user wants a person to sign off before anything publishes.",
      "annotations": {
        "title": "Request human approval",
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "sprkly_schedule_post",
      "title": "Schedule a post",
      "description": "Queue a post for publishing, in ONE call. Attach media by passing the user's link straight to media_urls: sprkly downloads it into storage itself for the platforms that need that, so no upload tool has to run first. Runs the same quota, duplicate-content and platform pre-flight checks as the sprkly app. Instagram and TikTok require media at submission time; YouTube and TikTok require a title, and TikTok also requires platform_meta.tiktok.privacyLevel — just send the level the user asked for and this tool names the allowed values if it is not one of them. It reads the real bytes of the media and the response says what will actually publish on each platform (a Reel, a 3-slide carousel, a photo set, a Page feed video) plus anything worth passing on: relay that to the user. Confirm the date, time and target accounts with the user first. If a target platform has more than one connected account and profile_ids is not given, the tool returns needsAccountChoice with the options instead of scheduling — put that choice to the user, then re-call.",
      "annotations": {
        "title": "Schedule a post",
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": true
      }
    },
    {
      "name": "sprkly_update_scheduled_post",
      "title": "Reschedule or edit a post",
      "description": "Change the caption, publish time, target accounts or attached media on a post that has not published yet. Only posts with status \"scheduled\" can be edited.",
      "annotations": {
        "title": "Reschedule or edit a post",
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "sprkly_validate_post_policy",
      "title": "Validate against platform rules",
      "description": "Check a caption against each target platform's posting rules before scheduling: caption length, media requirements, hashtag ceilings, whether links are clickable, required YouTube titles, and PII or prohibited-content warnings. Pure analysis. Writes nothing.",
      "annotations": {
        "title": "Validate against platform rules",
        "readOnlyHint": true,
        "openWorldHint": false
      }
    }
  ],
  "legacy": {
    "endpoint": "https://mcp.sprkly.app/mcp",
    "note": "Proxies to the endpoint above. Kept for agents configured before the server moved into the app."
  }
}