@omnisocials/mcp-server 1.34.1 → 1.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/client.d.ts +4 -1
- package/build/index.js +1 -1
- package/build/metrics.d.ts +8 -2
- package/build/metrics.js +93 -2
- package/build/tools/analytics.js +1 -1
- package/build/tools/posts.js +59 -0
- package/build/types.d.ts +17 -0
- package/package.json +1 -1
package/build/client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Account, AnalyticsOverview, AccountAnalyticsEntry, ApiPost, ApiResponse, AudioSearchResult, BestTimesResult, HashtagSet, LocationSearchResult, MediaCheckResult, MediaCompatibility, MediaFolder, MediaItem, PdfUploadResult, PostAnalytics, PublishPostResult, RecentPlatformPost, RetryPostResult, ApprovePostResult, RejectPostResult, InboxConversation, InboxDeleteResult, InboxMessage, InboxNextUnanswered, Webhook } from "./types.js";
|
|
1
|
+
import type { Account, AnalyticsOverview, AccountAnalyticsEntry, ApiPost, ApiResponse, ApiVideoCover, AudioSearchResult, BestTimesResult, HashtagSet, LocationSearchResult, MediaCheckResult, MediaCompatibility, MediaFolder, MediaItem, PdfUploadResult, PostAnalytics, PublishPostResult, RecentPlatformPost, RetryPostResult, ApprovePostResult, RejectPostResult, InboxConversation, InboxDeleteResult, InboxMessage, InboxNextUnanswered, Webhook } from "./types.js";
|
|
2
2
|
export declare function fetchImageAsBase64(url: string): Promise<{
|
|
3
3
|
data: string;
|
|
4
4
|
mimeType: string;
|
|
@@ -168,6 +168,8 @@ export interface CreatePostInput {
|
|
|
168
168
|
y: number;
|
|
169
169
|
image_index?: number;
|
|
170
170
|
}>;
|
|
171
|
+
/** Video thumbnail: { type: "frame"|"custom", thumb_offset?, cover_url?, overrides? }. */
|
|
172
|
+
video_cover?: ApiVideoCover;
|
|
171
173
|
pinterest?: Record<string, unknown>;
|
|
172
174
|
youtube?: Record<string, unknown>;
|
|
173
175
|
instagram?: Record<string, unknown>;
|
|
@@ -248,6 +250,7 @@ export declare class OmniSocialsClient {
|
|
|
248
250
|
y: number;
|
|
249
251
|
image_index?: number;
|
|
250
252
|
}>;
|
|
253
|
+
video_cover?: ApiVideoCover | null;
|
|
251
254
|
pinterest?: Record<string, unknown>;
|
|
252
255
|
youtube?: Record<string, unknown>;
|
|
253
256
|
instagram?: Record<string, unknown>;
|
package/build/index.js
CHANGED
|
@@ -29,7 +29,7 @@ const sessionState = { activeIndex: 0 };
|
|
|
29
29
|
const getActiveClient = () => workspaceClients[sessionState.activeIndex].client;
|
|
30
30
|
const server = new McpServer({
|
|
31
31
|
name: "OmniSocials",
|
|
32
|
-
version: "1.
|
|
32
|
+
version: "1.35.0",
|
|
33
33
|
});
|
|
34
34
|
// Register all tools - pass getter function so tools always use the active workspace's client
|
|
35
35
|
registerPostTools(server, getActiveClient);
|
package/build/metrics.d.ts
CHANGED
|
@@ -16,8 +16,14 @@ type Metrics = Record<string, unknown> | null | undefined;
|
|
|
16
16
|
export declare function getEngagement(platform: string, metrics: Metrics): number;
|
|
17
17
|
/** Best-available impressions ("how many times seen") for a post's metrics. */
|
|
18
18
|
export declare function getImpressions(platform: string, metrics: Metrics): number;
|
|
19
|
-
/** Unique accounts reached
|
|
20
|
-
export declare function getReach(platform: string, metrics: Metrics): number;
|
|
19
|
+
/** Unique accounts reached; null when the platform did not report it. */
|
|
20
|
+
export declare function getReach(platform: string, metrics: Metrics): number | null;
|
|
21
|
+
/** Post-level metrics a platform can never report: platform -> metric key -> reason. */
|
|
22
|
+
export declare const UNSUPPORTED_POST_METRICS: Record<string, Record<string, string>>;
|
|
23
|
+
/** Reason `metricKey` is unavailable on `platform`, or null when it is available. */
|
|
24
|
+
export declare function unsupportedMetricReason(platform: string, metricKey: string): string | null;
|
|
25
|
+
/** True when the platform can never report `metricKey`; a finite stored value overrides the map. */
|
|
26
|
+
export declare function isMetricUnsupported(platform: string, metricKey: string, metrics?: Metrics): boolean;
|
|
21
27
|
/**
|
|
22
28
|
* Engagement rate as a percentage (0-100), suppressed below the impression
|
|
23
29
|
* floor and capped at 100%.
|
package/build/metrics.js
CHANGED
|
@@ -106,10 +106,101 @@ export function getImpressions(platform, metrics) {
|
|
|
106
106
|
return num(m.views) || num(m.impressions) || num(m.reach);
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
|
-
|
|
109
|
+
// Finite number or null: a missing key stays "no data", never 0.
|
|
110
|
+
const finiteOrNull = (v) => {
|
|
111
|
+
if (v === null || v === undefined || v === "")
|
|
112
|
+
return null;
|
|
113
|
+
const n = typeof v === "number" ? v : Number(v);
|
|
114
|
+
return Number.isFinite(n) ? n : null;
|
|
115
|
+
};
|
|
116
|
+
/** Unique accounts reached; null when the platform did not report it. */
|
|
110
117
|
export function getReach(platform, metrics) {
|
|
111
118
|
const m = metrics || {};
|
|
112
|
-
|
|
119
|
+
for (const key of ["reach", "unique_impressions", "members_reached"]) {
|
|
120
|
+
const v = finiteOrNull(m[key]);
|
|
121
|
+
if (v !== null)
|
|
122
|
+
return v;
|
|
123
|
+
}
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
/** Post-level metrics a platform can never report: platform -> metric key -> reason. */
|
|
127
|
+
export const UNSUPPORTED_POST_METRICS = {
|
|
128
|
+
x: {
|
|
129
|
+
reach: "X does not report reach, only impressions.",
|
|
130
|
+
},
|
|
131
|
+
facebook: {
|
|
132
|
+
saves: "Facebook does not report saves.",
|
|
133
|
+
},
|
|
134
|
+
instagram: {
|
|
135
|
+
clicks: "Instagram reports link clicks for stories only (link sticker taps).",
|
|
136
|
+
link_clicks: "Instagram reports link clicks for stories only (link sticker taps).",
|
|
137
|
+
},
|
|
138
|
+
linkedin_page: {
|
|
139
|
+
link_clicks: "LinkedIn Pages report total clicks only, not link clicks separately.",
|
|
140
|
+
saves: "LinkedIn Pages do not report saves.",
|
|
141
|
+
},
|
|
142
|
+
threads: {
|
|
143
|
+
reach: "Threads does not report reach for posts.",
|
|
144
|
+
clicks: "Threads reports link clicks at account level only, not per post.",
|
|
145
|
+
link_clicks: "Threads reports link clicks at account level only, not per post.",
|
|
146
|
+
saves: "Threads does not report saves.",
|
|
147
|
+
},
|
|
148
|
+
youtube: {
|
|
149
|
+
reach: "YouTube does not report reach, only views.",
|
|
150
|
+
clicks: "YouTube does not report clicks.",
|
|
151
|
+
link_clicks: "YouTube does not report clicks.",
|
|
152
|
+
saves: "YouTube does not report saves.",
|
|
153
|
+
},
|
|
154
|
+
tiktok: {
|
|
155
|
+
clicks: "TikTok reports bio link clicks at account level only, not per video.",
|
|
156
|
+
link_clicks: "TikTok reports bio link clicks at account level only, not per video.",
|
|
157
|
+
},
|
|
158
|
+
pinterest: {
|
|
159
|
+
reach: "Pinterest does not report reach, only impressions.",
|
|
160
|
+
shares: "Pinterest does not report shares.",
|
|
161
|
+
},
|
|
162
|
+
bluesky: {
|
|
163
|
+
impressions: "Bluesky does not report impressions or views.",
|
|
164
|
+
reach: "Bluesky does not report reach.",
|
|
165
|
+
clicks: "Bluesky does not report clicks.",
|
|
166
|
+
link_clicks: "Bluesky does not report clicks.",
|
|
167
|
+
video_views: "Bluesky does not report video views.",
|
|
168
|
+
engagement_rate: "Engagement rate needs impressions, which Bluesky does not report.",
|
|
169
|
+
},
|
|
170
|
+
mastodon: {
|
|
171
|
+
impressions: "Mastodon does not report impressions or views.",
|
|
172
|
+
reach: "Mastodon does not report reach.",
|
|
173
|
+
clicks: "Mastodon does not report clicks.",
|
|
174
|
+
link_clicks: "Mastodon does not report clicks.",
|
|
175
|
+
saves: "Mastodon does not report saves.",
|
|
176
|
+
video_views: "Mastodon does not report video views.",
|
|
177
|
+
engagement_rate: "Engagement rate needs impressions, which Mastodon does not report.",
|
|
178
|
+
},
|
|
179
|
+
google_business: {
|
|
180
|
+
impressions: "Google discontinued per-post metrics for Business Profiles in 2023.",
|
|
181
|
+
reach: "Google discontinued per-post metrics for Business Profiles in 2023.",
|
|
182
|
+
likes: "Google discontinued per-post metrics for Business Profiles in 2023.",
|
|
183
|
+
comments: "Google discontinued per-post metrics for Business Profiles in 2023.",
|
|
184
|
+
shares: "Google discontinued per-post metrics for Business Profiles in 2023.",
|
|
185
|
+
saves: "Google discontinued per-post metrics for Business Profiles in 2023.",
|
|
186
|
+
clicks: "Google discontinued per-post metrics for Business Profiles in 2023.",
|
|
187
|
+
link_clicks: "Google discontinued per-post metrics for Business Profiles in 2023.",
|
|
188
|
+
video_views: "Google discontinued per-post metrics for Business Profiles in 2023.",
|
|
189
|
+
engagement: "Google discontinued per-post metrics for Business Profiles in 2023.",
|
|
190
|
+
engagement_rate: "Google discontinued per-post metrics for Business Profiles in 2023.",
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
/** Reason `metricKey` is unavailable on `platform`, or null when it is available. */
|
|
194
|
+
export function unsupportedMetricReason(platform, metricKey) {
|
|
195
|
+
return UNSUPPORTED_POST_METRICS[platform]?.[metricKey] ?? null;
|
|
196
|
+
}
|
|
197
|
+
/** True when the platform can never report `metricKey`; a finite stored value overrides the map. */
|
|
198
|
+
export function isMetricUnsupported(platform, metricKey, metrics) {
|
|
199
|
+
if (!unsupportedMetricReason(platform, metricKey))
|
|
200
|
+
return false;
|
|
201
|
+
if (metrics && finiteOrNull(metrics[metricKey]) !== null)
|
|
202
|
+
return false;
|
|
203
|
+
return true;
|
|
113
204
|
}
|
|
114
205
|
/**
|
|
115
206
|
* Engagement rate as a percentage (0-100), suppressed below the impression
|
package/build/tools/analytics.js
CHANGED
|
@@ -2,7 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import { formatNumber, capitalize, metricRows, metricBreakdownRows, sortMetricLabels } from "../client.js";
|
|
3
3
|
import { getEngagement, getImpressions, getEngagementRate, MIN_IMPRESSIONS_FOR_ENGAGEMENT_RATE, } from "../metrics.js";
|
|
4
4
|
export function registerAnalyticsTools(server, getClient) {
|
|
5
|
-
server.tool("get_post_analytics", "Get analytics/statistics for a specific published post. Renders every metric the platform reported
|
|
5
|
+
server.tool("get_post_analytics", "Get analytics/statistics for a specific published post. Renders every metric the platform reported per platform: impressions, reach, views, saves, likes, comments, shares, clicks, link_clicks, engagements, and more. A missing key means the platform does not report it: reach is absent (not 0) on X, Pinterest, YouTube, Threads, Bluesky and Mastodon; link_clicks (clicks on links in the post) exists for X posts under 30 days, LinkedIn profile, Facebook feed posts, Pinterest and Instagram stories, while clicks is the platform's broadest click count. 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).", {
|
|
6
6
|
post_id: z.string().describe("The numeric OmniSocials post id (the `id` field from list_posts). NOT a platform post id such as a YouTube video id or tweet id."),
|
|
7
7
|
}, { title: "Get Post Analytics", readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async ({ post_id }) => {
|
|
8
8
|
const result = await getClient().getPostAnalytics(post_id);
|
package/build/tools/posts.js
CHANGED
|
@@ -60,9 +60,35 @@ const FACEBOOK_OPTIONS = z
|
|
|
60
60
|
.max(8000)
|
|
61
61
|
.optional()
|
|
62
62
|
.describe("Text auto-posted as the first comment on the Facebook post right after it publishes. Page posts only (the API cannot comment on personal-profile posts). Not posted for Stories."),
|
|
63
|
+
thumbnail_type: z
|
|
64
|
+
.enum(["from-video", "from-library"])
|
|
65
|
+
.nullable()
|
|
66
|
+
.optional()
|
|
67
|
+
.describe("Video thumbnail for Facebook feed videos and reels: 'from-video' uses the frame at thumb_offset, 'from-library' uploads the image at cover_url. Applied after the video is live; a thumbnail failure never fails the post. On update_post, null removes the Facebook override."),
|
|
68
|
+
thumb_offset: z
|
|
69
|
+
.number()
|
|
70
|
+
.optional()
|
|
71
|
+
.describe("Frame timestamp in MILLISECONDS from the start of the video (e.g. 3000 = 0:03). Used with thumbnail_type 'from-video'."),
|
|
72
|
+
cover_url: z
|
|
73
|
+
.string()
|
|
74
|
+
.optional()
|
|
75
|
+
.describe("Custom thumbnail image URL (JPEG/PNG, max 10 MB). Used with thumbnail_type 'from-library'."),
|
|
63
76
|
})
|
|
64
77
|
.optional()
|
|
65
78
|
.describe("Facebook options");
|
|
79
|
+
const VIDEO_COVER_ENTRY = z.object({
|
|
80
|
+
type: z.enum(["frame", "custom"]).describe("'frame' uses the video frame at thumb_offset; 'custom' uploads the image at cover_url."),
|
|
81
|
+
thumb_offset: z.number().int().min(0).optional().describe("Milliseconds into the video (type 'frame'). 3000 = 0:03."),
|
|
82
|
+
cover_url: z.string().optional().describe("Public image URL, JPEG or PNG (type 'custom')."),
|
|
83
|
+
});
|
|
84
|
+
const VIDEO_COVER_OPTIONS = VIDEO_COVER_ENTRY.extend({
|
|
85
|
+
overrides: z
|
|
86
|
+
.record(z.string(), VIDEO_COVER_ENTRY.nullable())
|
|
87
|
+
.optional()
|
|
88
|
+
.describe("Per-platform overrides keyed by platform id (instagram, facebook, linkedin, linkedin_page, tiktok, pinterest, youtube). An override wins over the base cover for that platform."),
|
|
89
|
+
})
|
|
90
|
+
.optional()
|
|
91
|
+
.describe("Video thumbnail for a post whose media is ONE video. Applied on Instagram (feed videos and reels), Facebook (feed videos and reels), LinkedIn Profile and Page, TikTok, Pinterest and YouTube Shorts. TikTok only takes a frame: a 'custom' cover is skipped there, so add a tiktok override with type 'frame' when the user wants a specific TikTok frame. The per-platform reel fields (instagram.thumb_offset / cover_url, tiktok.video_cover_timestamp_ms, pinterest.video_cover) keep working and win over the base cover for their platform. get_post reads it back as `video_cover`.");
|
|
66
92
|
const LINKEDIN_OPTIONS = z
|
|
67
93
|
.object({
|
|
68
94
|
first_comment: z
|
|
@@ -601,6 +627,36 @@ export function registerPostTools(server, getClient) {
|
|
|
601
627
|
optionRows.push(["Pinterest title", pin.title]);
|
|
602
628
|
if (typeof pin.link === "string" && pin.link)
|
|
603
629
|
optionRows.push(["Pinterest link", pin.link]);
|
|
630
|
+
// Cross-platform video cover (base + per-platform overrides).
|
|
631
|
+
const describeCover = (c) => {
|
|
632
|
+
if (!c || typeof c !== "object")
|
|
633
|
+
return null;
|
|
634
|
+
const entry = c;
|
|
635
|
+
if (entry.type === "frame") {
|
|
636
|
+
const ms = Number(entry.thumb_offset) || 0;
|
|
637
|
+
return `video frame at ${ms} ms (${(ms / 1000).toFixed(1)}s into the video)`;
|
|
638
|
+
}
|
|
639
|
+
if (entry.type === "custom" && typeof entry.cover_url === "string")
|
|
640
|
+
return `custom image ${entry.cover_url}`;
|
|
641
|
+
return null;
|
|
642
|
+
};
|
|
643
|
+
const vc = p.video_cover;
|
|
644
|
+
const baseCover = describeCover(vc);
|
|
645
|
+
if (baseCover)
|
|
646
|
+
optionRows.push(["Video cover", baseCover]);
|
|
647
|
+
const coverOverrides = vc && typeof vc === "object" ? vc.overrides : null;
|
|
648
|
+
if (coverOverrides && typeof coverOverrides === "object") {
|
|
649
|
+
for (const [platform, entry] of Object.entries(coverOverrides)) {
|
|
650
|
+
const text = describeCover(entry);
|
|
651
|
+
if (text)
|
|
652
|
+
optionRows.push([`Video cover (${platform})`, text]);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
const fbCoverMs = Number(p.facebook?.thumb_offset) || 0;
|
|
656
|
+
const fbCoverUrl = p.facebook?.cover_url;
|
|
657
|
+
if (!coverOverrides?.facebook && (fbCoverMs > 0 || typeof fbCoverUrl === "string")) {
|
|
658
|
+
optionRows.push(["Facebook cover", typeof fbCoverUrl === "string" ? `custom image ${fbCoverUrl}` : `video frame at ${fbCoverMs} ms`]);
|
|
659
|
+
}
|
|
604
660
|
if (optionRows.length) {
|
|
605
661
|
md += `\n\n### Platform Options\n\n| Option | Value |\n|--------|-------|\n`;
|
|
606
662
|
for (const [k, v] of optionRows)
|
|
@@ -714,6 +770,7 @@ Do NOT call this tool without media when creating stories, reels, Instagram post
|
|
|
714
770
|
hashtag_set: z.string().optional().describe("Name of a saved hashtag set (from list_hashtag_sets, matched case-insensitively) to apply. The set's tags are merged in ONCE at create time — tags already in a caption are skipped, and Instagram's 30-hashtag cap returns a clear hashtag_limit_exceeded error. When the user says 'add my usual hashtags', check list_hashtag_sets first."),
|
|
715
771
|
hashtag_placement: z.enum(["caption_append", "first_comment"]).optional().describe("Where the set's tags land. caption_append (default): appended to each target caption after a blank line. first_comment: posted as the auto first comment on Instagram/Facebook/LinkedIn/LinkedIn Page/YouTube/TikTok (TikTok only when the workspace enabled TikTok comments) (appended after any explicit first_comment); platforms without a comment API fall back to caption_append. Stories always use captions."),
|
|
716
772
|
hashtag_platforms: z.array(z.string()).optional().describe("Optional subset of the post's channels to apply the hashtag set to (e.g. [\"instagram\", \"tiktok\"]). Defaults to all selected channels."),
|
|
773
|
+
video_cover: VIDEO_COVER_OPTIONS,
|
|
717
774
|
pinterest: PINTEREST_OPTIONS,
|
|
718
775
|
youtube: z.object({
|
|
719
776
|
title: z.string().optional().describe("Short title shown on YouTube. Falls back to \"YouTube Short\" when omitted."),
|
|
@@ -871,6 +928,7 @@ Do NOT call without required media — it will fail.`, {
|
|
|
871
928
|
hashtag_set: z.string().optional().describe("Name of a saved hashtag set (from list_hashtag_sets, matched case-insensitively) to apply. Tags are merged in once at create time; tags already in a caption are skipped."),
|
|
872
929
|
hashtag_placement: z.enum(["caption_append", "first_comment"]).optional().describe("caption_append (default) appends tags to the captions; first_comment posts them as the auto first comment on comment-capable platforms (others fall back to caption)."),
|
|
873
930
|
hashtag_platforms: z.array(z.string()).optional().describe("Optional subset of the post's channels to apply the hashtag set to. Defaults to all selected channels."),
|
|
931
|
+
video_cover: VIDEO_COVER_OPTIONS,
|
|
874
932
|
pinterest: PINTEREST_OPTIONS,
|
|
875
933
|
youtube: z.object({
|
|
876
934
|
title: z.string().optional().describe("Short title shown on YouTube."),
|
|
@@ -985,6 +1043,7 @@ Do NOT call without required media — it will fail.`, {
|
|
|
985
1043
|
location_id: z.string().optional().describe("Instagram only. Facebook Place/Page ID to tag the post's location with. Send an empty string to clear an existing location tag. Ignored by other platforms."),
|
|
986
1044
|
collaborators: z.array(z.string()).max(3).optional().describe("Instagram only. Up to 3 public Instagram usernames to invite as co-authors. Replaces the existing collaborator list. Send an empty array to clear collaborators. Works on image, carousel, and reel posts — NOT Stories. Ignored by other platforms."),
|
|
987
1045
|
user_tags: z.array(USER_TAG_ENTRY).optional().describe("Instagram only. Tag public accounts at x/y positions on a PHOTO (not video/reels/stories). Replaces the existing tag list. Send an empty array to clear. For carousels set image_index per tag. Ignored by other platforms."),
|
|
1046
|
+
video_cover: VIDEO_COVER_OPTIONS.nullable().describe("Replaces the stored video cover wholesale; pass null to remove it; omit to leave it untouched. facebook.thumbnail_type / thumb_offset / cover_url merge into overrides.facebook on their own."),
|
|
988
1047
|
youtube: z.object({
|
|
989
1048
|
title: z.string().optional().describe("Short title shown on YouTube. To change the Short's title on an existing draft, set this — do NOT use `content` (which is the description)."),
|
|
990
1049
|
tags: z.array(z.string()).optional(),
|
package/build/types.d.ts
CHANGED
|
@@ -138,6 +138,21 @@ export interface ApiInstagramOptionsOut extends ApiPlatformOptionsOut {
|
|
|
138
138
|
thumb_offset?: number | string;
|
|
139
139
|
cover_url?: string;
|
|
140
140
|
}
|
|
141
|
+
/** One video-cover choice: a frame offset or a custom image. */
|
|
142
|
+
export interface ApiVideoCoverEntry {
|
|
143
|
+
type: "frame" | "custom";
|
|
144
|
+
/** Milliseconds into the video (type "frame"). */
|
|
145
|
+
thumb_offset?: number;
|
|
146
|
+
/** Public image URL (type "custom"). */
|
|
147
|
+
cover_url?: string;
|
|
148
|
+
}
|
|
149
|
+
/** Cross-platform video cover as stored on the post (`video_cover`). On
|
|
150
|
+
* input, a `null` override entry drops that platform's override; the API
|
|
151
|
+
* never echoes nulls back. */
|
|
152
|
+
export interface ApiVideoCover extends ApiVideoCoverEntry {
|
|
153
|
+
/** Per-platform overrides keyed by platform id; an override wins for that platform. */
|
|
154
|
+
overrides?: Record<string, ApiVideoCoverEntry | null>;
|
|
155
|
+
}
|
|
141
156
|
export interface ApiStorySlide {
|
|
142
157
|
/** Zero-based slide position. */
|
|
143
158
|
index?: number;
|
|
@@ -179,6 +194,8 @@ export interface ApiPost {
|
|
|
179
194
|
/** Stories only, after publishing: every slide per platform in publish
|
|
180
195
|
* order. `published_urls` keeps only the first slide's URL. */
|
|
181
196
|
story_slides?: Record<string, ApiStorySlide[]>;
|
|
197
|
+
/** Video thumbnail (base + per-platform overrides); null when none was chosen. */
|
|
198
|
+
video_cover?: ApiVideoCover | null;
|
|
182
199
|
/** Set on a retry post: the original failed post's id. */
|
|
183
200
|
retry_of?: string;
|
|
184
201
|
/** Set on the original post: ids of its retry posts. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@omnisocials/mcp-server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.35.0",
|
|
4
4
|
"mcpName": "io.github.omnisocials/mcp-server",
|
|
5
5
|
"description": "MCP server for OmniSocials API - manage social media posts, media, accounts, analytics, and webhooks",
|
|
6
6
|
"type": "module",
|