@omnisocials/mcp-server 1.21.0 → 1.22.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/README.md +1 -1
- package/build/client.d.ts +6 -1
- package/build/tools/inbox.js +7 -4
- package/build/tools/posts.js +21 -9
- package/build/types.d.ts +12 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -188,7 +188,7 @@ Apply a set when creating a post: `create_post` / `create_and_publish_post` acce
|
|
|
188
188
|
|
|
189
189
|
| Tool | Description |
|
|
190
190
|
|------|-------------|
|
|
191
|
-
| `list_inbox_conversations` | List conversations — Instagram/Facebook DMs, comments, mentions,
|
|
191
|
+
| `list_inbox_conversations` | List conversations — Instagram/Facebook DMs, comments, mentions, LinkedIn company-page comments/mentions, TikTok video comments, and X DMs (opt-in); filter by platform, type, or unread |
|
|
192
192
|
| `get_inbox_conversation` | Get a conversation's full message history |
|
|
193
193
|
| `mark_inbox_read` | Mark all incoming messages in a conversation as read |
|
|
194
194
|
| `reply_to_inbox` | Reply to a DM, comment, or mention (existing conversations only) |
|
package/build/client.d.ts
CHANGED
|
@@ -101,11 +101,16 @@ export interface MastodonPostOptionsUpdate {
|
|
|
101
101
|
thread_parts?: MastodonThreadPartInput[] | null;
|
|
102
102
|
}
|
|
103
103
|
/** Non-sponsored LinkedIn poll: question + 2-4 options + duration. */
|
|
104
|
-
export interface
|
|
104
|
+
export interface LinkedInPollFields {
|
|
105
105
|
question: string;
|
|
106
106
|
options: string[];
|
|
107
107
|
duration: "ONE_DAY" | "THREE_DAYS" | "SEVEN_DAYS" | "FOURTEEN_DAYS";
|
|
108
108
|
}
|
|
109
|
+
/** LinkedIn poll(s), independent per channel. A channel's poll is omitted/null when it isn't a poll. */
|
|
110
|
+
export interface LinkedInPollInput {
|
|
111
|
+
linkedin?: LinkedInPollFields | null;
|
|
112
|
+
linkedin_page?: LinkedInPollFields | null;
|
|
113
|
+
}
|
|
109
114
|
/**
|
|
110
115
|
* Request body for POST /posts/create. `createAndPublishPost` accepts the
|
|
111
116
|
* same shape minus `scheduled_at` (publishing is immediate).
|
package/build/tools/inbox.js
CHANGED
|
@@ -4,12 +4,13 @@ const PLATFORM_EMOJI = {
|
|
|
4
4
|
instagram: "📸",
|
|
5
5
|
facebook: "📘",
|
|
6
6
|
linkedin: "💼",
|
|
7
|
+
tiktok: "🎵",
|
|
7
8
|
x: "🐦",
|
|
8
9
|
};
|
|
9
10
|
export function registerInboxTools(server, getClient) {
|
|
10
|
-
server.tool("list_inbox_conversations", "List 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.", {
|
|
11
|
+
server.tool("list_inbox_conversations", "List social inbox conversations (Instagram/Facebook DMs, comments, mentions, LinkedIn company-page comments/mentions, TikTok video comments, 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.", {
|
|
11
12
|
platform: z
|
|
12
|
-
.enum(["instagram", "facebook", "linkedin", "x"])
|
|
13
|
+
.enum(["instagram", "facebook", "linkedin", "tiktok", "x"])
|
|
13
14
|
.optional()
|
|
14
15
|
.describe("Filter to one platform"),
|
|
15
16
|
type: z
|
|
@@ -137,9 +138,11 @@ export function registerInboxTools(server, getClient) {
|
|
|
137
138
|
],
|
|
138
139
|
};
|
|
139
140
|
});
|
|
140
|
-
server.tool("reply_to_inbox", "Reply 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. Each workspace can send up to 1,000 replies per day.", {
|
|
141
|
+
server.tool("reply_to_inbox", "Reply to an existing inbox conversation (DM, comment, or mention) on Instagram, Facebook, LinkedIn, TikTok, 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. TikTok replies are comments only, text-only, and capped at 150 characters; they can take a few minutes to appear on TikTok while they pass spam review. 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. Each workspace can send up to 1,000 replies per day.", {
|
|
141
142
|
conversation_id: z.string().describe("The conversation ID to reply to"),
|
|
142
|
-
text: z
|
|
143
|
+
text: z
|
|
144
|
+
.string()
|
|
145
|
+
.describe("The reply text (max 2000 characters; TikTok comments max 150)"),
|
|
143
146
|
}, async ({ conversation_id, text }) => {
|
|
144
147
|
const result = await getClient().replyToInboxConversation(conversation_id, {
|
|
145
148
|
text,
|
package/build/tools/posts.js
CHANGED
|
@@ -83,8 +83,7 @@ const LINKEDIN_PAGE_OPTIONS = z
|
|
|
83
83
|
})
|
|
84
84
|
.optional()
|
|
85
85
|
.describe("LinkedIn Company Page options");
|
|
86
|
-
const
|
|
87
|
-
.object({
|
|
86
|
+
const LINKEDIN_POLL_FIELDS = z.object({
|
|
88
87
|
question: z.string().max(140).describe("The poll question (max 140 characters)."),
|
|
89
88
|
options: z
|
|
90
89
|
.array(z.string().max(30))
|
|
@@ -94,10 +93,19 @@ const LINKEDIN_POLL_OPTIONS = z
|
|
|
94
93
|
duration: z
|
|
95
94
|
.enum(["ONE_DAY", "THREE_DAYS", "SEVEN_DAYS", "FOURTEEN_DAYS"])
|
|
96
95
|
.describe("How long the poll stays open for votes."),
|
|
96
|
+
});
|
|
97
|
+
const LINKEDIN_POLL_OPTIONS = z
|
|
98
|
+
.object({
|
|
99
|
+
linkedin: LINKEDIN_POLL_FIELDS.nullable()
|
|
100
|
+
.optional()
|
|
101
|
+
.describe("Poll for the personal profile post. Omit or null = not a poll."),
|
|
102
|
+
linkedin_page: LINKEDIN_POLL_FIELDS.nullable()
|
|
103
|
+
.optional()
|
|
104
|
+
.describe("Poll for the company page post. Omit or null = not a poll."),
|
|
97
105
|
})
|
|
98
106
|
.nullable()
|
|
99
107
|
.optional()
|
|
100
|
-
.describe("Non-sponsored LinkedIn poll
|
|
108
|
+
.describe("Non-sponsored LinkedIn poll(s) — independent per channel, keyed by `linkedin` (personal profile) / `linkedin_page` (company page). A poll is mutually exclusive with media and a link share on that channel's post — a poll takes priority over both at publish time. Still requires `content.linkedin` (or `content.default`) as that channel's caption; the poll itself only carries the question/options/duration. On update_post, set a channel's key to `null` to clear that channel's poll and revert it to a normal post — send the full desired state for both channels, since the whole object replaces wholesale.");
|
|
101
109
|
// Per-platform option schemas shared verbatim by create_post,
|
|
102
110
|
// create_and_publish_post and update_post (they were byte-identical in all
|
|
103
111
|
// three). Platform blocks that genuinely differ per tool (instagram, youtube,
|
|
@@ -274,7 +282,7 @@ export function registerPostTools(server, getClient) {
|
|
|
274
282
|
content: [{ type: "text", text: md }],
|
|
275
283
|
};
|
|
276
284
|
});
|
|
277
|
-
server.tool("get_post", "Get details of a specific post by ID — content, channels, media (with URLs + any per-media alt text), first comment, Instagram collaborators/user tags/location/Trial Reel state/Reel cover (thumbnail_type + thumb_offset in ms), dates and live URLs, 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 Poll` with the question, options, and duration. After publishing, includes `published_urls` — a map of platform → live URL for each platform that successfully posted (e.g. facebook, instagram, linkedin, x). Useful for polling: when a post's status is `published`, read `published_urls` to surface the live links.", {
|
|
285
|
+
server.tool("get_post", "Get details of a specific post by ID — content, channels, media (with URLs + any per-media alt text), first comment, Instagram collaborators/user tags/location/Trial Reel state/Reel cover (thumbnail_type + thumb_offset in ms), dates and live URLs, 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 publishing, includes `published_urls` — a map of platform → live URL for each platform that successfully posted (e.g. facebook, instagram, linkedin, x). Useful for polling: when a post's status is `published`, read `published_urls` to surface the live links.", {
|
|
278
286
|
id: z.string().describe("The post ID"),
|
|
279
287
|
}, async ({ id }) => {
|
|
280
288
|
const result = await getClient().getPost(id);
|
|
@@ -407,11 +415,15 @@ export function registerPostTools(server, getClient) {
|
|
|
407
415
|
md += `\n`;
|
|
408
416
|
}
|
|
409
417
|
}
|
|
410
|
-
// LinkedIn poll: mutually exclusive with
|
|
411
|
-
//
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
418
|
+
// LinkedIn poll(s): independent per channel, mutually exclusive with
|
|
419
|
+
// media/link-share on that channel, so surface each prominently
|
|
420
|
+
// rather than leaving callers to infer it's a poll.
|
|
421
|
+
for (const channel of ["linkedin", "linkedin_page"]) {
|
|
422
|
+
const poll = p.linkedin_poll?.[channel];
|
|
423
|
+
if (!poll?.question)
|
|
424
|
+
continue;
|
|
425
|
+
const label = channel === "linkedin_page" ? "LinkedIn Page" : "LinkedIn Profile";
|
|
426
|
+
md += `\n\n### ${label} Poll\n\n`;
|
|
415
427
|
md += `**${poll.question}**\n\n`;
|
|
416
428
|
for (const option of poll.options || []) {
|
|
417
429
|
md += `- ${option}\n`;
|
package/build/types.d.ts
CHANGED
|
@@ -28,7 +28,7 @@ export interface InboxParticipant {
|
|
|
28
28
|
}
|
|
29
29
|
export interface InboxConversation {
|
|
30
30
|
conversation_id: string;
|
|
31
|
-
platform: "instagram" | "facebook" | "linkedin";
|
|
31
|
+
platform: "instagram" | "facebook" | "linkedin" | "tiktok" | "x";
|
|
32
32
|
type: "dm" | "comment" | "mention";
|
|
33
33
|
participant: InboxParticipant;
|
|
34
34
|
unread_count: number;
|
|
@@ -152,11 +152,18 @@ export interface ApiPost {
|
|
|
152
152
|
facebook?: ApiPlatformOptionsOut;
|
|
153
153
|
linkedin?: ApiPlatformOptionsOut;
|
|
154
154
|
linkedin_page?: ApiPlatformOptionsOut;
|
|
155
|
-
/** Non-sponsored LinkedIn poll echoed back after create/update/publish. */
|
|
155
|
+
/** Non-sponsored LinkedIn poll(s), independent per channel — echoed back after create/update/publish. */
|
|
156
156
|
linkedin_poll?: {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
157
|
+
linkedin?: {
|
|
158
|
+
question: string;
|
|
159
|
+
options: string[];
|
|
160
|
+
duration: string;
|
|
161
|
+
} | null;
|
|
162
|
+
linkedin_page?: {
|
|
163
|
+
question: string;
|
|
164
|
+
options: string[];
|
|
165
|
+
duration: string;
|
|
166
|
+
} | null;
|
|
160
167
|
} | null;
|
|
161
168
|
tiktok?: ApiPlatformOptionsOut;
|
|
162
169
|
google_business?: ApiPlatformOptionsOut;
|
package/package.json
CHANGED