@nooticr/mcp 1.26.52 → 1.26.54

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.
@@ -353,7 +353,7 @@ export function registerAmazonTools(server, makeClient) {
353
353
  "the category is wrong. " +
354
354
  "Use for a category or a competitive set; for one listing, get_amazon_product is cheaper.",
355
355
  annotations: {
356
- readOnlyHint: true,
356
+ readOnlyHint: false,
357
357
  destructiveHint: false,
358
358
  idempotentHint: false,
359
359
  openWorldHint: true,
@@ -423,7 +423,7 @@ export function registerBrandWatch(server, makeClient) {
423
423
  "never widened. A run that turns up nothing new — or, for a competitor watch, nothing above " +
424
424
  "median — sends no mail. cadence is hourly, every_6_hours, every_12_hours, daily or weekly, " +
425
425
  "and defaults to daily. No cost to call.",
426
- annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
426
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
427
427
  inputSchema: z
428
428
  .object({
429
429
  kind: z
@@ -643,7 +643,7 @@ export function registerBrandWatch(server, makeClient) {
643
643
  "names any in platformsSkipped, tell the user those networks are no longer searched and " +
644
644
  "offer to remove a term or recreate the watch with a larger budget. A change the budget " +
645
645
  "cannot cover at all is refused and nothing changes. No cost to call: no credits are spent.",
646
- annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
646
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false },
647
647
  inputSchema: z
648
648
  .object({
649
649
  watchId: z.string().describe("The portfolio watch's id, from list_brand_watches."),
@@ -26,6 +26,8 @@ export const SERVER_INSTRUCTIONS = [
26
26
  "Prices are nooticr credits per fetch and every paid tool states its price in its description. Free: " +
27
27
  "nooticr_getting_started, check_nooticr_credits, list_social_connections, watch_creator, unwatch_creator " +
28
28
  "and every show_* view, which only draws what you pass it.",
29
+ "Before a paid call, explain its stated cost and get the user's consent; follow any tool's built-in " +
30
+ "confirmation flow before proceeding.",
29
31
  "Not sure where to start? Call nooticr_getting_started: it is free, reads this account's state, and names " +
30
32
  "the next calls worth making with their prices. Good first calls: get_social_media on a post URL " +
31
33
  "(1 credit), discover_social_posts on a niche (2), analyze_comments on a post (2).",
@@ -362,38 +362,19 @@ export function prepareItem(item, seenIds) {
362
362
  };
363
363
  }
364
364
  /** How to actually file it, named per destination so the model does not guess. */
365
- function nextStepFor(destination, count) {
365
+ function nextStepFor(_destination, count) {
366
366
  const n = `${count} item${count === 1 ? "" : "s"}`;
367
- switch (destination) {
368
- case "github":
369
- return (`Search the repository's issues for each \`searchFirst\` string first — an exact match ` +
370
- `means it is already filed and you should comment on that issue instead of opening a ` +
371
- `second one. For the rest, call the GitHub server's issue-creation tool with \`title\`, ` +
372
- `\`body\` and \`labels\` exactly as given. Do not rewrite the body: the quote is fenced ` +
373
- `and framed deliberately. ${n} prepared.`);
374
- case "jira":
375
- return (`Search the project for each \`searchFirst\` string first, then create an issue per item ` +
376
- `with \`title\` as the summary and \`body\` as the description. Jira renders its own ` +
377
- `markup — if the project is not markdown-enabled the fence may show literally, which is ` +
378
- `acceptable and better than an unfenced quote. Map \`labels\` to Jira labels. ${n} prepared.`);
379
- case "linear":
380
- return (`Search the team's issues for each \`searchFirst\` string first, then create an issue per ` +
381
- `item with \`title\` and \`body\` as given, and \`labels\` as Linear labels (create any ` +
382
- `that do not exist rather than dropping them). ${n} prepared.`);
383
- default:
384
- return (`Each item carries the exact \`title\`, \`body\` and \`labels\` to file, and a ` +
385
- `\`searchFirst\` string to look for first so the same report does not land twice. Pass ` +
386
- `them to whichever tracker tool this host has connected, unmodified. ${n} prepared.`);
387
- }
367
+ return (`Each item includes a title, body, labels and a search phrase for duplicate checking. ` +
368
+ `Review the prepared text, then copy it into your chosen issue tracker. No issue is created ` +
369
+ `by this tool. ${n} prepared.`);
388
370
  }
389
371
  export function registerHandoff(server) {
390
372
  server.registerTool("prepare_handoff", {
391
373
  title: "Prepare Handoff",
392
374
  description: "Turn items you classified — a bug report in a comment, a complaint said out loud in a " +
393
- "video, a feature request under a competitor's post — into the exact text to file in " +
394
- "GitHub, Jira or Linear through whichever tracker server this host also has connected. " +
395
- "This server files nothing itself and holds no tracker credential; it returns the " +
396
- "strings and you make the call. Free, and makes no requests. " +
375
+ "video, a feature request under a competitor's post — into structured, issue-ready text. " +
376
+ "This server files nothing; review the result and copy it to your chosen issue tracker. " +
377
+ "Free, and makes no requests. " +
397
378
  "Use it after analyze_comments, search_mentions, answer_my_audience or " +
398
379
  "search_spoken_mentions, passing the ids those tools issued. For each item you get a " +
399
380
  "title, a ready body with the quote fenced and framed as third-party evidence rather " +
@@ -837,7 +837,7 @@ export function registerJobTools(server, makeClient, store) {
837
837
  "you follow; analyze_creator_profile is the full teardown of one you do not.",
838
838
  annotations: {
839
839
  readOnlyHint: false,
840
- destructiveHint: false,
840
+ destructiveHint: true,
841
841
  // Moves the "last checked" marker for a watched creator, so the second
842
842
  // call in a row does not answer the same question as the first.
843
843
  idempotentHint: false,
@@ -257,7 +257,7 @@ export function registerMarketplaceTools(server, makeClient) {
257
257
  "For Amazon specifically, scan_amazon_category returns the same shape plus Amazon's own " +
258
258
  "review-aspect counts.",
259
259
  annotations: {
260
- readOnlyHint: true,
260
+ readOnlyHint: false,
261
261
  destructiveHint: false,
262
262
  idempotentHint: false,
263
263
  openWorldHint: true,
@@ -155,7 +155,7 @@ export function registerOwnAccountTools(server, makeClient) {
155
155
  "appId (optional when your workspace has only one product); every other field is " +
156
156
  "snake_case, the same names create_product takes and for the same reason — the backend " +
157
157
  "reads them by exact key. Free — no AI call, just a row.",
158
- annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
158
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false },
159
159
  inputSchema: z
160
160
  .object({
161
161
  appId: z
@@ -383,7 +383,7 @@ export function registerOwnAccountTools(server, makeClient) {
383
383
  return failed(`${name} failed`, err);
384
384
  }
385
385
  });
386
- connectorRead("get_google_analytics", "google_analytics", "Get Google Analytics", "Your own product's Google Analytics (GA4) sync, as of its last sync into nooticr — not a live " +
386
+ connectorRead("get_google_analytics", "google_analytics", "Read Google Analytics Data", "Your own product's Google Analytics (GA4) sync, as of its last sync into nooticr — not a live " +
387
387
  "GA4 call: the property, the date range synced and how many daily rows came in. The metrics " +
388
388
  "themselves (installs, opens, purchases, revenue) feed get_post_performance and growth_brief " +
389
389
  "rather than being repeated here. Returns a message rather than an error when nothing has " +
@@ -416,7 +416,7 @@ export function registerOwnAccountTools(server, makeClient) {
416
416
  // jobId and state: "pending", nothing to draw yet. analyze_product_status,
417
417
  // which returns the finished playbook, is where the view belongs — see
418
418
  // scripts/host-contract.py's NO_APP entry for this tool.
419
- annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
419
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
420
420
  inputSchema: z
421
421
  .object({
422
422
  appId: z
@@ -571,7 +571,7 @@ export function registerOwnAccountTools(server, makeClient) {
571
571
  "get_content_plan) but does not schedule or publish any post. Billed like the dashboard's " +
572
572
  "own Content Plan button: your workspace's plan AI credits, not your personal MCP credits.",
573
573
  _meta: viewMeta("generate_content_plan"),
574
- annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
574
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
575
575
  inputSchema: z
576
576
  .object({
577
577
  appId: z
@@ -2,7 +2,7 @@ import { z } from "zod";
2
2
  export const TOOL_DEFINITIONS = [
3
3
  {
4
4
  name: "analyze_post",
5
- title: "Analyze Post",
5
+ title: "Inspect Social Media Post",
6
6
  description: "Frames sampled evenly across a social post (video, image, carousel/slideshow), returned as real images you can look at, together with the post's transcript, caption and stats. It hands you the material rather than a verdict: read the frames and the words, then work out the hook, the structure, the visual style, the CTA and the audience yourself, citing the frame or line behind each claim. Supports TikTok, Instagram, YouTube, X, Reddit, Douyin, Xiaohongshu, Weibo, Bilibili and LinkedIn. Use when the visuals are the point — framing, editing, on-screen text; analyze_post_fast reads the same post without the frames for one credit less. Fans out to get_post_frames and get_post_transcript: 3 nooticr credits, 2 for the frames plus 1 for the transcript.",
7
7
  inputSchema: z.object({ url: z.string().describe("Public post URL (TikTok/Instagram/YouTube/X/Reddit/Douyin/Xiaohongshu/Weibo/Bilibili/LinkedIn).") }).strict(),
8
8
  },
@@ -26,7 +26,7 @@ export const TOOL_DEFINITIONS = [
26
26
  },
27
27
  {
28
28
  name: "analyze_creator_profile",
29
- title: "Analyze Creator Profile",
29
+ title: "Research Creator Profile",
30
30
  description: "A creator's recent posts with their stats, on TikTok, Instagram, YouTube, Reddit, Douyin, Xiaohongshu, X, Weibo, Bilibili or LinkedIn — the raw material of a profile teardown, for you to read. Work out their niche, content themes, hook formula, strengths and weaknesses, engagement pattern and audience from the spread of the numbers rather than the best post, and name the posts you reason from. find_hook_pattern fetches the same posts and asks only for the formula. Costs 2 nooticr credits, for the one get_user_posts call it makes.",
31
31
  inputSchema: z.object({ username: z.string().describe("Creator handle, e.g. 'zoundsapp'."), platform: z.enum(["tiktok", "instagram", "youtube", "douyin", "xiaohongshu", "twitter", "bilibili", "reddit", "weibo"]).optional().describe("Which platform (default tiktok)."), limit: z.number().int().optional().describe("Posts to fetch (default 6; first 3 analyzed)."), focus: z.string().optional().describe("Extra instruction for the profile synthesis.") }).strict(),
32
32
  },
@@ -80,13 +80,13 @@ export const TOOL_DEFINITIONS = [
80
80
  },
81
81
  {
82
82
  name: "analyze_comments",
83
- title: "Analyze Comments",
83
+ title: "Summarize Post Comments",
84
84
  description: "A post's comment section, fetched and laid out for you to classify: every comment with a stable id, plus whatever themes the platform clustered them into. Label each one's sentiment and what it is doing — praise, complaint, bug report, question, request, comparison, spam — then summarise the themes, the questions worth answering, the objections, and what to make next. The result names the exact labels, and show_comment_review draws them for free afterwards. Use when the goal is 'what should I make next' rather than 'what did people write'. Costs 2 nooticr credits, for the one get_post_comments call it makes.",
85
85
  inputSchema: z.object({ url: z.string().describe("Full public post URL."), limit: z.number().int().optional().describe("Comments to read (default 50, max 100)."), verbatim: z.boolean().optional().describe("Render every comment whole in the text you read (default false).") }).strict(),
86
86
  },
87
87
  {
88
88
  name: "compare_posts",
89
- title: "Compare Posts",
89
+ title: "Compare Social Post Performance",
90
90
  description: "The first of 2-5 posts you want compared, fetched with its stats — and the comparison left to you. Call get_social_media on each remaining URL yourself (1 credit each), and get_post_transcript where the wording matters, then say which won, what actually differed (hook, format, length, caption, hashtags), what they share worth keeping, and the one experiment that would test your explanation. Use for 'why did this one work and that one not'. Costs 1 nooticr credit for this call, plus 1 per further post you fetch.",
91
91
  inputSchema: z.object({ urls: z.array(z.string()).describe("2-5 post URLs to compare.") }).strict(),
92
92
  },
@@ -98,31 +98,31 @@ export const TOOL_DEFINITIONS = [
98
98
  },
99
99
  {
100
100
  name: "analyze_post_fast",
101
- title: "Analyze Post (Fast)",
101
+ title: "Quick Social Post Analysis",
102
102
  description: "A post's transcript, caption and stats, with no frames — which is what makes it the cheap read. Work out the hook, the script structure, the CTA and the audience from the words and the numbers yourself, and say plainly that you have not seen the visuals. Use this by default; call analyze_post when a judgement actually needs the frames. Fans out to get_social_media and get_post_transcript: 2 nooticr credits, 1 each.",
103
103
  inputSchema: z.object({ url: z.string().describe("Full public post URL.") }).strict(),
104
104
  },
105
105
  {
106
106
  name: "write_hooks",
107
- title: "Write Hooks",
107
+ title: "Generate Hooks from a Post",
108
108
  description: "The source post, its transcript and its stats, so you can write the opening lines yourself — the first line said or shown on screen. Name the device each hook uses and who it stops; one that could open any video in the niche is not grounded in this one. Use when you know the subject and need openings to choose between. With a url it fetches get_social_media and get_post_transcript for 2 nooticr credits; with only a topic there is nothing to fetch and it costs nothing.",
109
109
  inputSchema: z.object({ url: z.string().optional().describe("Post to riff on (optional if topic given)."), topic: z.string().optional().describe("Subject to write hooks about (optional if url given)."), count: z.number().int().optional().describe("How many hooks (default 10, max 20)."), tone: z.string().optional().describe("Optional tone, e.g. 'blunt', 'contrarian'.") }).strict(),
110
110
  },
111
111
  {
112
112
  name: "create_variants",
113
- title: "Create Variants",
113
+ title: "Generate Post Variants",
114
114
  description: "The post that worked, with its transcript and stats, so you can propose what to film next — same underlying mechanism, different execution. For each variant give the hook, the one angle that changes, the shot beats in order and the CTA, and say what made the original work. Use after reading a post to move from 'why it worked' to 'what to make'. Fans out to get_social_media and get_post_transcript: 2 nooticr credits, 1 each.",
115
115
  inputSchema: z.object({ url: z.string().describe("The post to make variants of."), count: z.number().int().optional().describe("How many variants (default 3, max 6)."), angle: z.string().optional().describe("Optional steer, e.g. 'for a beginner audience'.") }).strict(),
116
116
  },
117
117
  {
118
118
  name: "score_draft",
119
- title: "Score Draft",
119
+ title: "Score a Social Media Draft",
120
120
  description: "Score your own draft BEFORE you film or post it. Returns the draft with the rubric to hold it to — hook, clarity, payoff, specificity and fit, each scored 1-10 — and asks you for the three fixes that would move it most, one rewritten opening line and a tightened version. The only tool that runs before the content exists. Use before filming, while changing it is still cheap. Free: it fetches nothing, so it costs no credits.",
121
121
  inputSchema: z.object({ draft: z.string().describe("Your script, caption or hook."), platform: z.string().optional().describe("Target platform (default tiktok).") }).strict(),
122
122
  },
123
123
  {
124
124
  name: "repurpose_post",
125
- title: "Repurpose Post",
125
+ title: "Adapt a Post for Another Platform",
126
126
  description: "The source post, its transcript and its stats, for you to rewrite for other surfaces — X thread, LinkedIn post, carousel slides, YouTube title/description, newsletter — keeping the argument and changing the shape to suit how each is read. The same paragraph with different line breaks is not a repurposing. Use when a post already worked and you want it on other surfaces. Fans out to get_social_media and get_post_transcript: 2 nooticr credits, 1 each.",
127
127
  inputSchema: z.object({ url: z.string().describe("The post to repurpose."), targets: z.array(z.string()).optional().describe("Which formats to produce (default all).") }).strict(),
128
128
  },
@@ -338,7 +338,7 @@ export const TOOL_DEFINITIONS = [
338
338
  },
339
339
  {
340
340
  name: "get_google_analytics",
341
- title: "Get Google Analytics",
341
+ title: "Read Google Analytics Data",
342
342
  description: "Your own product's Google Analytics (GA4) sync, as of its last sync into nooticr — not a live GA4 call: the property, the date range synced and how many daily rows came in. Connect GA4 in API Connections first. No cost to call.",
343
343
  inputSchema: z.object({ appId: z.number().int().optional().describe("Your product's id. Omit only with a single-app workspace.") }).strict(),
344
344
  },
@@ -30,7 +30,7 @@ import { registerCollabTools } from "./collab.js";
30
30
  import { registerAmazonTools } from "./amazon.js";
31
31
  import { registerMarketplaceTools } from "./marketplace.js";
32
32
  /** Current MCP server version — bumped on every deploy for traceability. */
33
- export const MCP_SERVER_VERSION = "1.26.52";
33
+ export const MCP_SERVER_VERSION = "1.26.54";
34
34
  /** MCP Apps extension identifier */
35
35
  const UI_EXTENSION = "io.modelcontextprotocol/ui";
36
36
  /** MIME type for MCP Apps HTML resources */
@@ -104,7 +104,9 @@ function appsSdkContents(uri, media, links, tool = "") {
104
104
  _meta: {
105
105
  "openai/widgetPrefersBorder": false,
106
106
  "openai/widgetCSP": {
107
- connect_domains: media,
107
+ // The widget communicates through the host bridge and needs no direct
108
+ // network connections. Remote origins are limited to rendered resources.
109
+ connect_domains: [],
108
110
  resource_domains: media,
109
111
  redirect_domains: links,
110
112
  },
@@ -852,7 +854,7 @@ export function createMcpServer(rawMakeClient, opts) {
852
854
  // Thumbnails and video come from our own origin and the platform CDNs.
853
855
  // Without these the widget loads and then paints nothing at all.
854
856
  "openai/widgetCSP": {
855
- connect_domains: domains,
857
+ connect_domains: [],
856
858
  resource_domains: domains,
857
859
  redirect_domains: PLATFORM_LINK_DOMAINS,
858
860
  },
@@ -889,7 +891,7 @@ export function createMcpServer(rawMakeClient, opts) {
889
891
  });
890
892
  }
891
893
  registerSlowTool(server, "analyze_post", {
892
- title: "Analyze Post",
894
+ title: "Inspect Social Media Post",
893
895
  description: "Frames sampled evenly across a social post (video, image, carousel/slideshow), returned as " +
894
896
  "real images you can look at, together with the post's transcript, caption and stats. " +
895
897
  "This is the material an analysis is built from, not an analysis: read the frames and the " +
@@ -1033,7 +1035,7 @@ export function createMcpServer(rawMakeClient, opts) {
1033
1035
  }
1034
1036
  });
1035
1037
  registerSlowTool(server, "analyze_creator_profile", {
1036
- title: "Analyze Creator Profile",
1038
+ title: "Research Creator Profile",
1037
1039
  description: "A creator's recent posts with their stats, on TikTok, Instagram, YouTube, Reddit, Douyin, " +
1038
1040
  "Xiaohongshu, X, Weibo, Bilibili or LinkedIn — the raw material of a profile teardown. " +
1039
1041
  "Work out their niche, recurring themes, hook formula, what over- and under-performs and who " +
@@ -1439,7 +1441,7 @@ export function createMcpServer(rawMakeClient, opts) {
1439
1441
  }
1440
1442
  });
1441
1443
  server.registerTool("analyze_comments", {
1442
- title: "Analyze Comments",
1444
+ title: "Summarize Post Comments",
1443
1445
  description: "A post's comment section, fetched and laid out for you to classify: every comment with a " +
1444
1446
  "stable id, plus whatever themes the platform clustered them into. Label each one's sentiment " +
1445
1447
  "and what it is doing — praise, complaint, bug report, question, request, comparison, spam — " +
@@ -2061,7 +2063,7 @@ export function createMcpServer(rawMakeClient, opts) {
2061
2063
  };
2062
2064
  });
2063
2065
  server.registerTool("compare_posts", {
2064
- title: "Compare Posts",
2066
+ title: "Compare Social Post Performance",
2065
2067
  description: "The first of 2-5 posts you want compared, fetched with its stats — and the comparison left " +
2066
2068
  "to you. Call get_social_media on each remaining URL yourself (1 credit each), plus " +
2067
2069
  "get_post_transcript where the wording matters, then say which won, what actually differed " +
@@ -2178,7 +2180,7 @@ export function createMcpServer(rawMakeClient, opts) {
2178
2180
  }
2179
2181
  });
2180
2182
  server.registerTool("analyze_post_fast", {
2181
- title: "Analyze Post (Fast)",
2183
+ title: "Quick Social Post Analysis",
2182
2184
  description: "A post's transcript, caption and stats, with no frames — which is what makes it the cheap " +
2183
2185
  "read. Work out the hook, the script structure, the CTA and the audience from the words and " +
2184
2186
  "the numbers yourself, and say plainly that you have not seen the visuals. " +
@@ -2208,7 +2210,7 @@ export function createMcpServer(rawMakeClient, opts) {
2208
2210
  }
2209
2211
  });
2210
2212
  server.registerTool("write_hooks", {
2211
- title: "Write Hooks",
2213
+ title: "Generate Hooks from a Post",
2212
2214
  description: "The source post, its transcript and its stats, so you can write the opening lines yourself — " +
2213
2215
  "the first line said or shown on screen. For each hook you write, name the device it uses and " +
2214
2216
  "who it stops; a hook that could open any video in the niche is not grounded in this one. " +
@@ -2242,7 +2244,7 @@ export function createMcpServer(rawMakeClient, opts) {
2242
2244
  }
2243
2245
  });
2244
2246
  server.registerTool("create_variants", {
2245
- title: "Create Variants",
2247
+ title: "Generate Post Variants",
2246
2248
  description: "The post that worked, with its transcript and stats, so you can propose what to film next: " +
2247
2249
  "for each variant, the hook, the one angle that changes, the shot beats in order and the CTA. " +
2248
2250
  "Keep whatever made the original work and say what that was. " +
@@ -2274,7 +2276,7 @@ export function createMcpServer(rawMakeClient, opts) {
2274
2276
  }
2275
2277
  });
2276
2278
  server.registerTool("score_draft", {
2277
- title: "Score Draft",
2279
+ title: "Score a Social Media Draft",
2278
2280
  description: "Score your own draft BEFORE you film or post it. Returns the draft alongside the rubric to " +
2279
2281
  "hold it to — hook, clarity, payoff, specificity and fit, each scored 1-10 — and asks you for " +
2280
2282
  "the three fixes that would move it most, one rewritten opening line and a tightened version. " +
@@ -2319,7 +2321,7 @@ export function createMcpServer(rawMakeClient, opts) {
2319
2321
  };
2320
2322
  });
2321
2323
  server.registerTool("repurpose_post", {
2322
- title: "Repurpose Post",
2324
+ title: "Adapt a Post for Another Platform",
2323
2325
  description: "The source post, its transcript and its stats, for you to rewrite for other surfaces — X " +
2324
2326
  "thread, LinkedIn post, carousel slides, YouTube title/description, newsletter. Each surface " +
2325
2327
  "has its own length, register and conventions: the same paragraph with different line breaks " +
@@ -564,7 +564,7 @@ export function registerWatchlist(server, makeClient, store) {
564
564
  "snapshot forward — so this answers 'what is new' rather than 'what exists'. The first run " +
565
565
  "for a creator has nothing to compare against and just records the baseline. " +
566
566
  "Consumes 2 nooticr credits per creator checked.",
567
- annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
567
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
568
568
  inputSchema: z
569
569
  .object({
570
570
  limit: z.number().int().optional().describe("Posts to check per creator (default 6)."),
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nooticr/mcp",
3
3
  "mcpName": "com.nooticr/mcp",
4
- "version": "1.26.52",
4
+ "version": "1.26.54",
5
5
  "description": "MCP server for nooticr - fetch, discover and understand TikTok/Instagram/YouTube/X/Reddit/LinkedIn/Weibo posts with AI (media metadata, niche discovery, brand monitoring, hook & viral analysis)",
6
6
  "homepage": "https://mcp.nooticr.com",
7
7
  "repository": {