{"$schema":"https://static.modelcontextprotocol.io/schemas/v1/server-card.schema.json","name":"com.publora/publora","version":"1.0.0","title":"Publora","description":"Schedule and publish social media content across multiple platforms via your Publora account.","websiteUrl":"https://publora.com","remotes":[{"type":"streamable-http","url":"https://mcp.publora.com/mcp","supportedProtocolVersions":["2025-11-25","2025-06-18","2025-03-26","2024-11-05","2024-10-07"],"headers":[{"name":"Authorization","description":"Publora API key as a bearer token: 'Bearer sk_<your_key>'.","isRequired":true,"isSecret":true},{"name":"x-publora-key","description":"Alternative to the Authorization header: send the raw 'sk_<your_key>'.","isRequired":false,"isSecret":true}]}],"serverInfo":{"name":"publora","version":"1.0.0"},"authentication":{"required":true,"schemes":["oauth2","bearer"]},"capabilities":{"tools":{}},"tools":[{"name":"list_connections","description":"List all connected social media accounts (Twitter, LinkedIn, TikTok, etc.)","inputSchema":{"type":"object","properties":{},"additionalProperties":false},"title":"List connected accounts","annotations":{"readOnlyHint":true,"destructiveHint":false,"openWorldHint":true}},{"name":"create_post","description":"Create a post. FASTEST for media posts: pass mediaUrls (public https URLs — Publora downloads them server-side) together with scheduledTime to create AND schedule in one call; platforms like Instagram, TikTok, YouTube require media before scheduling. Manual alternative for local files: omit scheduledTime to create a draft, upload media with get_upload_url + HTTP PUT + complete_media (once per file), then call update_post with status 'scheduled'.","inputSchema":{"type":"object","properties":{"content":{"type":"string","description":"Post text content"},"platforms":{"type":"array","items":{"type":"string"},"description":"Array of platform identifiers in the exact format returned by list_connections. Each value must be the 'platformId' field copied verbatim from a connection object, e.g. ['twitter-1985855679454986200', 'linkedin-abc123']. IMPORTANT: Do NOT invent or guess IDs — always call list_connections first and use the exact platformId values. Exception: 'publora-playground' is a connection-test target that requires no real connection — the post is acknowledged and discarded, nothing is published; when used it must be the ONLY entry."},"scheduledTime":{"description":"Optional ISO 8601 datetime for publishing, e.g. '2025-03-01T12:00:00Z'. Omit to create a draft. Scheduling a media-requiring platform without media fails validation — pass mediaUrls in the same call to avoid that.","type":"string"},"mediaUrls":{"description":"Optional array (max 10) of public https URLs of images/videos to attach. Downloaded and validated server-side (jpeg, png, gif, webp, tiff, avif, mp4, mov, webm; images ≤25MB, videos ≤150MB). Must be DIRECT file URLs — share-page links (Google Drive, Dropbox pages) will fail.","type":"array","items":{"type":"string"}},"platformSettings":{"description":"Per-platform publishing options, keyed by platform. Example: { instagram: { coverUrl: 'https://.../cover.jpg' }, youtube: { title: 'My video', madeForKids: false }, tiktok: { viewerSetting: 'SELF_ONLY' }, linkedin: { repostEnabled: true, repostParentUrn: 'urn:li:share:123', repostVisibility: 'PUBLIC' } }. Unknown platforms or fields are rejected.","type":"object","properties":{"instagram":{"type":"object","properties":{"videoType":{"type":"string","enum":["REELS","STORIES"]},"shareToFeed":{"type":"boolean"},"coverUrl":{"description":"Public https URL of a JPEG Reels cover image","anyOf":[{"type":"string","format":"uri","pattern":"^https:\\/\\/.*"},{"type":"string","const":""}]}},"additionalProperties":false},"tiktok":{"type":"object","properties":{"viewerSetting":{"type":"string","enum":["PUBLIC_TO_EVERYONE","MUTUAL_FOLLOW_FRIENDS","FOLLOWER_OF_CREATOR","SELF_ONLY"]},"allowComments":{"type":"boolean"},"allowDuet":{"type":"boolean"},"allowStitch":{"type":"boolean"},"commercialContent":{"type":"boolean"},"brandOrganic":{"type":"boolean"},"brandedContent":{"type":"boolean"}},"additionalProperties":false},"youtube":{"type":"object","properties":{"privacy":{"type":"string","enum":["private","public","unlisted"]},"title":{"type":"string"},"madeForKids":{"type":"boolean"},"tags":{"type":"array","items":{"type":"string"}},"categoryId":{"type":"string"},"playlist":{"type":"object","properties":{"id":{"type":"string"},"platformId":{"type":"string"}},"required":["id","platformId"],"additionalProperties":false}},"additionalProperties":false},"threads":{"type":"object","properties":{"replyControl":{"anyOf":[{"type":"string","enum":["everyone","accounts_you_follow","mentioned_only"]},{"type":"string","const":""}]}},"additionalProperties":false},"telegram":{"type":"object","properties":{"disableNotification":{"type":"boolean"},"disableWebPagePreview":{"type":"boolean"},"protectContent":{"type":"boolean"}},"additionalProperties":false},"linkedin":{"type":"object","properties":{"repostEnabled":{"type":"boolean"},"repostParentUrn":{"description":"Canonical LinkedIn post URN to repost; activity URNs are not supported","anyOf":[{"type":"string","pattern":"^urn:li:(?:share|ugcPost):[A-Za-z0-9_-]+$"},{"type":"string","const":""}]},"repostVisibility":{"type":"string","enum":["PUBLIC","CONNECTIONS"]}},"additionalProperties":false}},"additionalProperties":false},"idempotencyKey":{"description":"Optional retry key. Reusing it with the identical request replays the original response without creating another post.","type":"string","minLength":1}},"required":["content","platforms"],"additionalProperties":false},"title":"Create post","annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":true}},{"name":"get_post","description":"Get details of a scheduled post group and its platform-specific posts","inputSchema":{"type":"object","properties":{"postGroupId":{"type":"string","description":"Post group ID"}},"required":["postGroupId"],"additionalProperties":false},"title":"Get post","annotations":{"readOnlyHint":true,"destructiveHint":false,"openWorldHint":true}},{"name":"update_post","description":"Edit an existing draft/scheduled post without deleting it. You may change its base content, target platforms, status, scheduled time, per-platform settings, and/or APPEND media from public https URLs via mediaUrls. Before changing platforms, call list_connections and copy each target's platformId verbatim. Published/failed posts and posts already being published are not editable. Scheduling re-validates the complete final content/media/platform state; omitting a field keeps its current value.","inputSchema":{"type":"object","properties":{"postGroupId":{"type":"string","description":"Post group ID"},"content":{"description":"Optional replacement base post text. Updates every target without an explicit platform-specific override. An empty string is allowed for a draft; scheduling still enforces each platform's content/media rules.","type":"string"},"platforms":{"description":"Optional replacement target platform array. Call list_connections first and copy its platformId values verbatim. This replaces the whole set; [] is allowed only while the post remains a draft.","type":"array","items":{"type":"string"}},"status":{"description":"New status: 'draft' or 'scheduled'","type":"string","enum":["draft","scheduled"]},"scheduledTime":{"description":"New scheduled time in ISO 8601 format","type":"string"},"mediaUrls":{"description":"Optional array (max 10) of public https URLs of images/videos to APPEND to this post. Same rules as create_post: direct file URLs only; jpeg, png, gif, webp, tiff, avif, mp4, mov, webm; images ≤25MB, videos ≤150MB.","type":"array","items":{"type":"string"}},"platformSettings":{"description":"Per-platform publishing options to merge (same shape as create_post). Unknown platforms or fields are rejected.","type":"object","properties":{"instagram":{"type":"object","properties":{"videoType":{"type":"string","enum":["REELS","STORIES"]},"shareToFeed":{"type":"boolean"},"coverUrl":{"description":"Public https URL of a JPEG Reels cover image","anyOf":[{"type":"string","format":"uri","pattern":"^https:\\/\\/.*"},{"type":"string","const":""}]}},"additionalProperties":false},"tiktok":{"type":"object","properties":{"viewerSetting":{"type":"string","enum":["PUBLIC_TO_EVERYONE","MUTUAL_FOLLOW_FRIENDS","FOLLOWER_OF_CREATOR","SELF_ONLY"]},"allowComments":{"type":"boolean"},"allowDuet":{"type":"boolean"},"allowStitch":{"type":"boolean"},"commercialContent":{"type":"boolean"},"brandOrganic":{"type":"boolean"},"brandedContent":{"type":"boolean"}},"additionalProperties":false},"youtube":{"type":"object","properties":{"privacy":{"type":"string","enum":["private","public","unlisted"]},"title":{"type":"string"},"madeForKids":{"type":"boolean"},"tags":{"type":"array","items":{"type":"string"}},"categoryId":{"type":"string"},"playlist":{"type":"object","properties":{"id":{"type":"string"},"platformId":{"type":"string"}},"required":["id","platformId"],"additionalProperties":false}},"additionalProperties":false},"threads":{"type":"object","properties":{"replyControl":{"anyOf":[{"type":"string","enum":["everyone","accounts_you_follow","mentioned_only"]},{"type":"string","const":""}]}},"additionalProperties":false},"telegram":{"type":"object","properties":{"disableNotification":{"type":"boolean"},"disableWebPagePreview":{"type":"boolean"},"protectContent":{"type":"boolean"}},"additionalProperties":false},"linkedin":{"type":"object","properties":{"repostEnabled":{"type":"boolean"},"repostParentUrn":{"description":"Canonical LinkedIn post URN to repost; activity URNs are not supported","anyOf":[{"type":"string","pattern":"^urn:li:(?:share|ugcPost):[A-Za-z0-9_-]+$"},{"type":"string","const":""}]},"repostVisibility":{"type":"string","enum":["PUBLIC","CONNECTIONS"]}},"additionalProperties":false}},"additionalProperties":false},"idempotencyKey":{"description":"Optional retry key. Reusing it with the identical request prevents repeated media appends and replays the original response.","type":"string","minLength":1}},"required":["postGroupId"],"additionalProperties":false},"title":"Update post","annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":true}},{"name":"delete_post","description":"Delete a scheduled post group and all its platform-specific posts","inputSchema":{"type":"object","properties":{"postGroupId":{"type":"string","description":"Post group ID"}},"required":["postGroupId"],"additionalProperties":false},"title":"Delete post","annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":true,"openWorldHint":true}},{"name":"get_upload_url","description":"Get a presigned S3 URL to upload a LOCAL media file (image/video) for a post. Call ONCE per file — repeat calls with the same fileName return the same mediaId instead of adding duplicate slots; use unique fileNames for different files. Next: HTTP PUT the raw bytes to uploadUrl with a Content-Type header EQUAL to the contentType passed here (the signature enforces it), then call complete_media with the mediaId. If the file is already reachable at a public URL, prefer create_post/update_post with mediaUrls — no upload steps at all.","inputSchema":{"type":"object","properties":{"postGroupId":{"type":"string","description":"Post group ID to attach media to"},"fileName":{"type":"string","description":"File name, e.g. 'photo.jpg'"},"contentType":{"type":"string","description":"MIME type, e.g. 'image/jpeg' or 'video/mp4'"},"type":{"type":"string","enum":["image","video"],"description":"Media type: 'image' or 'video'"}},"required":["postGroupId","fileName","contentType","type"],"additionalProperties":false},"title":"Get media upload URL","annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":true}},{"name":"complete_media","description":"Finalize an uploaded media file after the HTTP PUT to the presigned uploadUrl: Publora probes the bytes server-side and marks the media 'ready' for scheduling. Call once per uploaded mediaId (from get_upload_url), then schedule via update_post. Not needed for media attached via mediaUrls.","inputSchema":{"type":"object","properties":{"mediaId":{"type":"string","description":"MediaFile ID returned by get_upload_url"}},"required":["mediaId"],"additionalProperties":false},"title":"Complete media upload","annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":true}},{"name":"delete_media","description":"Remove one media file from a post group — e.g. a duplicate or never-uploaded slot that blocks scheduling with MEDIA_COUNT_EXCEEDED or media-not-ready errors. Find mediaIds via get_post (its 'media' array shows status per file). Deleting media from a scheduled post demotes it to draft; re-schedule with update_post afterwards.","inputSchema":{"type":"object","properties":{"mediaId":{"type":"string","description":"MediaFile ID to delete (see the 'media' array in get_post)"}},"required":["mediaId"],"additionalProperties":false},"title":"Delete media","annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":true,"openWorldHint":true}},{"name":"prune_media_reference","description":"Repair MEDIA_REFERENCE_MISSING by removing a stale/dangling media reference from an owned post group, even when the MediaFile row is absent or inaccessible. Use delete_media for normal existing media rows.","inputSchema":{"type":"object","properties":{"postGroupId":{"type":"string","description":"Post group ID that contains the stale media reference"},"mediaId":{"type":"string","description":"Stale media ID reported in the MEDIA_REFERENCE_MISSING error"}},"required":["postGroupId","mediaId"],"additionalProperties":false},"title":"Prune media reference","annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":true,"openWorldHint":true}},{"name":"list_posts","description":"List scheduled/published posts with filtering by status, platform, date range","inputSchema":{"type":"object","properties":{"status":{"description":"Filter by post status","type":"string","enum":["draft","scheduled","published","failed","partially_published"]},"platform":{"description":"Filter by platform name, e.g. 'linkedin', 'twitter'","type":"string"},"fromDate":{"description":"Start date filter (ISO 8601)","type":"string"},"toDate":{"description":"End date filter (ISO 8601)","type":"string"},"page":{"description":"Page number (default: 1)","type":"number"},"limit":{"description":"Results per page (default: 20, max: 100)","type":"number"},"sortBy":{"description":"Sort field (default: createdAt)","type":"string","enum":["createdAt","updatedAt","scheduledTime"]},"sortOrder":{"description":"Sort direction (default: desc)","type":"string","enum":["asc","desc"]},"responseFormat":{"description":"'detailed' (default) returns full post content; 'concise' truncates each post's content to a preview to save tokens.","type":"string","enum":["concise","detailed"]}},"additionalProperties":false},"title":"List posts","annotations":{"readOnlyHint":true,"destructiveHint":false,"openWorldHint":true}},{"name":"linkedin_create_reaction","description":"React to a LinkedIn post (like, praise, etc.)","inputSchema":{"type":"object","properties":{"postedId":{"type":"string","description":"LinkedIn post URN, e.g. 'urn:li:share:123456'"},"platformId":{"type":"string","description":"Platform connection ID"},"reactionType":{"type":"string","enum":["LIKE","PRAISE","EMPATHY","INTEREST","APPRECIATION","ENTERTAINMENT"],"description":"Reaction type"}},"required":["postedId","platformId","reactionType"],"additionalProperties":false},"title":"LinkedIn: react to post","annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":true}},{"name":"linkedin_delete_reaction","description":"Remove your reaction from a LinkedIn post","inputSchema":{"type":"object","properties":{"postedId":{"type":"string","description":"LinkedIn post URN"},"platformId":{"type":"string","description":"Platform connection ID"}},"required":["postedId","platformId"],"additionalProperties":false},"title":"LinkedIn: remove reaction","annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":true,"openWorldHint":true}},{"name":"linkedin_create_comment","description":"Post a comment on a LinkedIn post","inputSchema":{"type":"object","properties":{"postedId":{"type":"string","description":"LinkedIn post URN, e.g. 'urn:li:share:123456' or 'urn:li:ugcPost:123456'"},"platformId":{"type":"string","description":"Platform connection ID, e.g. 'linkedin-XxxYyy'"},"message":{"type":"string","description":"Comment text (max 1250 characters). Supports mentions: @{urn:li:person:Dk968RHxiO|John Smith} or @{urn:li:organization:12345678|Company Name}. Person IDs must be raw native app-scoped member IDs returned by Publora. If starting from list_connections platformId such as linkedin-Dk968RHxiO, remove the leading linkedin- before building the person URN; IDs copied from linkedin.com profile URLs are rejected."},"parentComment":{"description":"Parent comment URN for nested replies","type":"string"}},"required":["postedId","platformId","message"],"additionalProperties":false},"title":"LinkedIn: comment on post","annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":true}},{"name":"linkedin_delete_comment","description":"Delete a comment from a LinkedIn post","inputSchema":{"type":"object","properties":{"postedId":{"type":"string","description":"LinkedIn post URN the comment belongs to"},"commentId":{"type":"string","description":"Comment URN to delete, e.g. 'urn:li:comment:(urn:li:ugcPost:xxx,123456)'"},"platformId":{"type":"string","description":"Platform connection ID"}},"required":["postedId","commentId","platformId"],"additionalProperties":false},"title":"LinkedIn: delete comment","annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":true,"openWorldHint":true}},{"name":"linkedin_create_reshare","description":"Reshare (repost) an existing LinkedIn post to your feed, optionally with commentary","inputSchema":{"type":"object","properties":{"platformId":{"type":"string","description":"Platform connection ID, e.g. 'linkedin-XxxYyy'"},"parent":{"type":"string","description":"URN of the post to reshare, e.g. 'urn:li:share:123456' or 'urn:li:ugcPost:123456'"},"commentary":{"description":"Optional commentary shown above the reshare (max 3000 characters)","type":"string","maxLength":3000},"visibility":{"description":"Reshare visibility (default: PUBLIC). CONNECTIONS is only valid for personal accounts.","type":"string","enum":["PUBLIC","CONNECTIONS"]}},"required":["platformId","parent"],"additionalProperties":false},"title":"LinkedIn: repost/reshare a post","annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":true}},{"name":"linkedin_list_mentionables","description":"List LinkedIn people captured from engagement (comments/reactions) on your connected company pages. Each entry carries the native app-scoped person id — the only id namespace LinkedIn resolves in mentions — plus a ready-to-use mention token in the form @{urn:li:person:ID|Name}. Requires a paid plan (403 UPGRADE_REQUIRED otherwise).","inputSchema":{"type":"object","properties":{"q":{"description":"Name filter (substring)","type":"string"},"limit":{"description":"Max results (default 25)","type":"integer","minimum":1,"maximum":100}},"additionalProperties":false},"title":"LinkedIn: list mentionable people","annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":true}}],"resources":[],"prompts":[],"_meta":{"com.publora/docs":"https://docs.publora.com/mcp/openclaw","com.publora/authType":"oauth2-or-bearer"}}