@omnisocials/mcp-server 1.34.0 → 1.34.1

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 CHANGED
@@ -227,6 +227,10 @@ Full API docs: [docs.omnisocials.com](https://docs.omnisocials.com)
227
227
 
228
228
  ## Changelog
229
229
 
230
+ ### 1.34.1 (2026-09-14)
231
+
232
+ - **Changed: Threads threads and the Threads inbox are no longer "rolling out".** Meta approved the reply, read-replies and mentions permissions, so `threads.thread_parts` and the Threads inbox work for every Threads connection; accounts connected before 2026-09-14 need a one-time reconnect under Settings -> Organisation -> Workspaces (`400`/`401` errors name the reconnect). Threads location tagging stays rolling out (not approved yet). Same on the companion server.
233
+
230
234
  ### 1.34.0 (2026-09-06)
231
235
 
232
236
  - **Added: work through the inbox one item at a time.** New tool `get_next_unanswered` returns the next inbox item that still needs an answer, with the whole conversation and the post it is on, in one call: the customer's latest DM with no reply after it (Instagram/Facebook DMs within the 24-hour reply window), or a comment/mention nobody replied to and that is not hidden. Longest-waiting first; filter with `platform` and `type`; only unread items by default (`mark_inbox_read` is the durable skip, `include_read: true` widens it, `exclude` skips up to 100 conversation ids for one call); looks back 30 days and reports `remaining`. `reply_to_inbox` accepts `include_next: true` and returns the next item in the same response. `list_inbox_conversations` gains an `unanswered` filter. Replies typed in the native apps now appear as outgoing messages and count as answers. Same on the companion server (`mcp.omnisocials.com`).
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.34.0",
32
+ version: "1.34.1",
33
33
  });
34
34
  // Register all tools - pass getter function so tools always use the active workspace's client
35
35
  registerPostTools(server, getActiveClient);
@@ -295,16 +295,19 @@ export function registerInboxTools(server, getClient) {
295
295
  if (result.error)
296
296
  return errorResult(result.error);
297
297
  const m = result.data;
298
+ const text = result.note
299
+ ? `Comment \`#${m?.id ?? message_id}\` on ${m?.platform || "the platform"}: ${result.note} Marked as handled — no need to retry it.`
300
+ : `Comment \`#${m?.id ?? message_id}\` on ${m?.platform || "the platform"} is now ${m?.hidden ? "hidden" : "visible"}.`;
298
301
  return {
299
302
  content: [
300
303
  {
301
304
  type: "text",
302
- text: `Comment \`#${m?.id ?? message_id}\` on ${m?.platform || "the platform"} is now ${m?.hidden ? "hidden" : "visible"}.`,
305
+ text,
303
306
  },
304
307
  ],
305
308
  };
306
309
  };
307
- server.tool("hide_inbox_comment", "Hide (or unhide) a comment someone left on one of the workspace's posts, on the platform, as the post owner. Facebook, Instagram, TikTok, YouTube and Threads comments. On YouTube, hide sets the comment to 'rejected' (removed from public view together with its replies) and unhide publishes it again. Takes the message id (the #id shown by get_inbox_conversation and get_next_unanswered), not the conversation id. A hidden comment no longer counts as unanswered. A 403 reconnect_required means the account was connected without the comment-moderation permission and must be reconnected in the dashboard; relay that to the user rather than retrying.", hideCommentSchema, { title: "Hide Inbox Comment", readOnlyHint: false, destructiveHint: false, openWorldHint: true }, hideComment);
310
+ server.tool("hide_inbox_comment", "Hide (or unhide) a comment someone left on one of the workspace's posts, on the platform, as the post owner. Facebook, Instagram, TikTok, YouTube and Threads comments. On YouTube, hide sets the comment to 'rejected' (removed from public view together with its replies) and unhide publishes it again. Takes the message id (the #id shown by get_inbox_conversation and get_next_unanswered), not the conversation id. A hidden comment no longer counts as unanswered. If the comment was already deleted on the platform (its author removed it, etc.), this still succeeds — the response says so — and the item is marked handled; do not retry it. A 403 reconnect_required means the account was connected without the comment-moderation permission and must be reconnected in the dashboard; relay that to the user rather than retrying.", hideCommentSchema, { title: "Hide Inbox Comment", readOnlyHint: false, destructiveHint: false, openWorldHint: true }, hideComment);
308
311
  // Older name from 1.28.0 (when hiding was Threads-only). Kept so existing
309
312
  // setups keep working; same call, same platforms as hide_inbox_comment.
310
313
  server.tool("hide_inbox_reply", "Older name for hide_inbox_comment, kept so existing setups keep working; prefer hide_inbox_comment. Same behaviour: hide (or unhide with hide: false) a comment someone left on one of the workspace's posts, as the post owner, on Facebook, Instagram, TikTok, YouTube or Threads. Takes the message id (the #id in the thread), not the conversation id. A 403 reconnect_required means the account must be reconnected in the dashboard to grant the comment-moderation permission.", hideCommentSchema, { title: "Hide Inbox Comment (older name)", readOnlyHint: false, destructiveHint: false, openWorldHint: true }, hideComment);
@@ -756,7 +756,7 @@ Do NOT call this tool without media when creating stories, reels, Instagram post
756
756
  thread_parts: z.array(MASTODON_THREAD_PART).min(2).max(25).optional().describe("Publish as a chained Mastodon thread instead of a single status. Provide 2–25 parts; each is posted in order as a native reply to the previous status (in_reply_to_id). Attach media to any part via media_ids (from upload_media) or media_urls — max 4 per part. For a single status, omit thread_parts and use content."),
757
757
  }).optional().describe("Mastodon options"),
758
758
  threads: z.object({
759
- location_id: z.string().nullable().optional().describe("Tag a location on the Threads post. Use an `id` from search_locations with platform \"threads\" (Threads location ids are NOT Facebook Place IDs, so never reuse the Instagram location_id here). On a multi-post thread the tag goes on the first post. Needs the workspace's Threads connection to have the threads_location_tagging permission; the API returns a clear 400 asking to reconnect Threads when it is missing, and a clear 400 while location tagging is still rolling out (disabled in this environment). On update_post, pass null to remove the tag."),
759
+ location_id: z.string().nullable().optional().describe("Tag a location on the Threads post. Use an `id` from search_locations with platform \"threads\" (Threads location ids are NOT Facebook Place IDs, so never reuse the Instagram location_id here). On a multi-post thread the tag goes on the first post. Needs the workspace's Threads connection to have the threads_location_tagging permission; the API returns a clear 400 asking to reconnect Threads when it is missing, and a clear 400 while location tagging is still rolling out (Meta has not approved the permission yet). On update_post, pass null to remove the tag."),
760
760
  thread_parts: z.array(THREADS_THREAD_PART).min(2).max(25).optional().describe("Publish as a chained Threads (Meta) thread instead of a single post. Provide 2 to 25 parts; parts after the first are posted as replies to the previous part. Each part holds up to 500 characters and up to 10 media (images and videos can be mixed) via media_ids (from upload_media) or media_urls. When set, the Threads caption is taken from part 1 and content is ignored for Threads. For a single post, omit thread_parts and use content."),
761
761
  }).optional().describe("Threads (Meta) options: thread mode via `thread_parts`, location tag via `location_id`."),
762
762
  google_business: GOOGLE_BUSINESS_OPTIONS,
@@ -913,7 +913,7 @@ Do NOT call without required media — it will fail.`, {
913
913
  thread_parts: z.array(MASTODON_THREAD_PART).min(2).max(25).optional().describe("Publish as a chained Mastodon thread (2–25 parts). Each is posted in order as a native reply (in_reply_to_id). Attach media to any part via media_ids or media_urls — max 4 per part."),
914
914
  }).optional().describe("Mastodon options"),
915
915
  threads: z.object({
916
- location_id: z.string().nullable().optional().describe("Tag a location on the Threads post. Use an `id` from search_locations with platform \"threads\" (Threads location ids are NOT Facebook Place IDs, so never reuse the Instagram location_id here). On a multi-post thread the tag goes on the first post. Needs the workspace's Threads connection to have the threads_location_tagging permission; the API returns a clear 400 asking to reconnect Threads when it is missing, and a clear 400 while location tagging is still rolling out (disabled in this environment). On update_post, pass null to remove the tag."),
916
+ location_id: z.string().nullable().optional().describe("Tag a location on the Threads post. Use an `id` from search_locations with platform \"threads\" (Threads location ids are NOT Facebook Place IDs, so never reuse the Instagram location_id here). On a multi-post thread the tag goes on the first post. Needs the workspace's Threads connection to have the threads_location_tagging permission; the API returns a clear 400 asking to reconnect Threads when it is missing, and a clear 400 while location tagging is still rolling out (Meta has not approved the permission yet). On update_post, pass null to remove the tag."),
917
917
  thread_parts: z.array(THREADS_THREAD_PART).min(2).max(25).optional().describe("Publish as a chained Threads (Meta) thread (2 to 25 parts). Parts after the first are posted as replies to the previous part. Each part holds up to 500 characters and up to 10 media (images and videos can be mixed) via media_ids or media_urls. The Threads caption is taken from part 1."),
918
918
  }).optional().describe("Threads (Meta) options: thread mode via `thread_parts`, location tag via `location_id`."),
919
919
  google_business: GOOGLE_BUSINESS_OPTIONS,
@@ -1027,7 +1027,7 @@ Do NOT call without required media — it will fail.`, {
1027
1027
  thread_parts: z.array(MASTODON_THREAD_PART).min(2).max(25).nullable().optional().describe("Replace the Mastodon thread shape on this post. Pass an array (2–25 parts) to update/create the thread (attach media to any part via media_ids or media_urls; max 4 per part), or `null` to revert to single-status mode."),
1028
1028
  }).optional().describe("Mastodon options"),
1029
1029
  threads: z.object({
1030
- location_id: z.string().nullable().optional().describe("Tag a location on the Threads post. Use an `id` from search_locations with platform \"threads\" (Threads location ids are NOT Facebook Place IDs, so never reuse the Instagram location_id here). On a multi-post thread the tag goes on the first post. Needs the workspace's Threads connection to have the threads_location_tagging permission; the API returns a clear 400 asking to reconnect Threads when it is missing, and a clear 400 while location tagging is still rolling out (disabled in this environment). On update_post, pass null to remove the tag."),
1030
+ location_id: z.string().nullable().optional().describe("Tag a location on the Threads post. Use an `id` from search_locations with platform \"threads\" (Threads location ids are NOT Facebook Place IDs, so never reuse the Instagram location_id here). On a multi-post thread the tag goes on the first post. Needs the workspace's Threads connection to have the threads_location_tagging permission; the API returns a clear 400 asking to reconnect Threads when it is missing, and a clear 400 while location tagging is still rolling out (Meta has not approved the permission yet). On update_post, pass null to remove the tag."),
1031
1031
  thread_parts: z.array(THREADS_THREAD_PART).min(2).max(25).nullable().optional().describe("Replace the Threads (Meta) thread shape on this post. Pass an array (2 to 25 parts, 500 characters per part) to update/create the thread (attach up to 10 media to any part via media_ids or media_urls; images and videos can be mixed), or `null` to revert to single-post mode."),
1032
1032
  }).optional().describe("Threads (Meta) options: thread mode via `thread_parts`, location tag via `location_id`."),
1033
1033
  google_business: GOOGLE_BUSINESS_OPTIONS,
@@ -1059,7 +1059,7 @@ Do NOT call without required media — it will fail.`, {
1059
1059
  content: [{ type: "text", text: md }],
1060
1060
  };
1061
1061
  });
1062
- server.tool("delete_post", "Delete a post by ID. This action cannot be undone.", {
1062
+ server.tool("delete_post", "Delete a post by ID from OmniSocials only: it leaves the calendar, the posts list and the analytics. This never deletes the live post on a platform, so a published post stays live everywhere it went; the user must delete it in the platform's own app or via \"Delete from [platform]\" in the OmniSocials dashboard. This action cannot be undone.", {
1063
1063
  id: z.string().describe("The post ID to delete"),
1064
1064
  }, { title: "Delete Post", readOnlyHint: false, destructiveHint: true, openWorldHint: false }, async ({ id }) => {
1065
1065
  const result = await getClient().deletePost(id);
@@ -1223,7 +1223,7 @@ Flow (Threads): same, but pass the \`id\` as \`threads.location_id\`. Threads ca
1223
1223
  Notes:
1224
1224
  - Use a SPECIFIC venue name. Searching a broad brand ("Starbucks") returns individual store locations, not the brand page.
1225
1225
  - Instagram: if the user already gives you a numeric Facebook Place ID, you can pass it straight to create_post as \`location_id\`; it's validated at publish time and an invalid one returns a clear error. If results come back empty with a permission note, the workspace's Facebook app can't search arbitrary places; tell the user to use their own business Page's ID.
1226
- - Threads: needs the workspace's Threads connection to have the location permission; if the result says to reconnect Threads, tell the user to reconnect it under Settings → Organisation → Workspaces. Threads location tagging is currently rolling out, so the search can also answer that it is not available yet.`, {
1226
+ - Threads: needs the workspace's Threads connection to have the location permission; if the result says to reconnect Threads, tell the user to reconnect it under Settings → Organisation → Workspaces. Threads location tagging is currently rolling out (Meta has not approved the permission yet), so the search can also answer that it is not available yet.`, {
1227
1227
  query: z
1228
1228
  .string()
1229
1229
  .optional()
package/build/types.d.ts CHANGED
@@ -21,6 +21,7 @@ export interface ApiResponse<T = unknown> {
21
21
  marked_read?: number;
22
22
  next?: InboxNextUnanswered | null;
23
23
  remaining?: number;
24
+ note?: string;
24
25
  }
25
26
  export interface InboxParticipant {
26
27
  id: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omnisocials/mcp-server",
3
- "version": "1.34.0",
3
+ "version": "1.34.1",
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",