@nooticr/mcp 1.26.79 → 1.26.80

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.
@@ -154,7 +154,7 @@ export function registerOwnAccountTools(server, makeClient) {
154
154
  description: "Patch your own product's fields — omitted arguments leave their column unchanged. Takes " +
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
- "reads them by exact key. Free — no AI call, just a row.",
157
+ "reads them by exact key. Free — no AI call, just a row. Use when the user wants to change details of a product that already exists.",
158
158
  annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false },
159
159
  inputSchema: z
160
160
  .object({
@@ -191,7 +191,7 @@ export function registerOwnAccountTools(server, makeClient) {
191
191
  title: "Get Scheduled Posts",
192
192
  description: "Your own scheduled and draft posts in the content pipeline — title, status, scheduled " +
193
193
  "time, approval status. What is queued to publish, not what already has (see " +
194
- "get_post_performance for that). Does not publish or change anything. No cost to call.",
194
+ "get_post_performance for that). Does not publish or change anything. No cost to call. Use when the user asks what is queued or drafted in their content pipeline.",
195
195
  _meta: viewMeta("get_scheduled_posts"),
196
196
  annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
197
197
  inputSchema: z
@@ -218,7 +218,7 @@ export function registerOwnAccountTools(server, makeClient) {
218
218
  title: "Get Post Performance",
219
219
  description: "Your own already-published posts with their engagement counters — views, likes, " +
220
220
  "comments, shares, platform, post date. This is the raw performance history, not an " +
221
- "interpretation of it; pair with growth_brief for that. No cost to call.",
221
+ "interpretation of it; pair with growth_brief for that. No cost to call. Use when the user asks how their own published posts performed.",
222
222
  _meta: viewMeta("get_post_performance"),
223
223
  annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
224
224
  inputSchema: z
@@ -245,7 +245,7 @@ export function registerOwnAccountTools(server, makeClient) {
245
245
  title: "Get Video Stats",
246
246
  description: "Your own most recently synced video performance stats across every connected creator — " +
247
247
  "views, likes, comments, shares, plus a running total. Reads the last sync; does not " +
248
- "trigger a new one. No cost to call.",
248
+ "trigger a new one. No cost to call. Use when the user asks for their own synced video numbers.",
249
249
  _meta: viewMeta("get_video_stats"),
250
250
  annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
251
251
  inputSchema: z
@@ -285,7 +285,7 @@ export function registerOwnAccountTools(server, makeClient) {
285
285
  title: "Get Content Plan",
286
286
  description: "The saved weekly content plan for your own product, if one has been generated (see " +
287
287
  "generate_content_plan). Read-only and free even when a plan exists — generating a new " +
288
- "one is the paid step. Returns plan: null when none has been made yet. No cost to call.",
288
+ "one is the paid step. Returns plan: null when none has been made yet. No cost to call. Use when the user asks to see the content plan already generated for their product.",
289
289
  _meta: viewMeta("get_content_plan"),
290
290
  annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
291
291
  inputSchema: z
@@ -316,7 +316,7 @@ export function registerOwnAccountTools(server, makeClient) {
316
316
  description: "Your own product's brand playbook — name, description and the playbook text — if one has " +
317
317
  "been configured, in the dashboard or by a previous analyze_product run. Read-only: never " +
318
318
  "creates or edits a playbook itself. Returns available: false when none exists yet. Takes " +
319
- "appId (optional when your workspace has only one product). No cost to call.",
319
+ "appId (optional when your workspace has only one product). No cost to call. Use when a task needs the product's stored brand voice or positioning.",
320
320
  _meta: viewMeta("get_brand_playbook"),
321
321
  annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
322
322
  inputSchema: z
@@ -388,7 +388,8 @@ export function registerOwnAccountTools(server, makeClient) {
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 " +
390
390
  "ever been synced; connect GA4 in API Connections first. Takes appId (optional when your " +
391
- "workspace has only one product). No cost to call.");
391
+ "workspace has only one product). No cost to call. Use to check whether GA4 data is synced " +
392
+ "and for which dates.");
392
393
  connectorRead("get_search_console_data", "search_console", "Get Search Console Data", "Your own product's Google Search Console data — total clicks and impressions and the top " +
393
394
  "search queries — as of its last sync into nooticr, not a live Search Console call. Use it " +
394
395
  "to see whether people search for what your social content is about. Returns a message " +
@@ -411,7 +412,7 @@ export function registerOwnAccountTools(server, makeClient) {
411
412
  "background: returns a jobId immediately rather than the finished analysis — poll it with " +
412
413
  "analyze_product_status. Billed like the dashboard's own analyze job: 10 of your " +
413
414
  "workspace's plan AI credits (first analysis free per workspace), a different balance from " +
414
- "your personal MCP credits and not tracked by check_nooticr_credits.",
415
+ "your personal MCP credits and not tracked by check_nooticr_credits. Use when the user wants a brand playbook generated or refreshed for their product.",
415
416
  // Not view-less for lack of trying: the immediate reply is only a
416
417
  // jobId and state: "pending", nothing to draw yet. analyze_product_status,
417
418
  // which returns the finished playbook, is where the view belongs — see
@@ -440,7 +441,7 @@ export function registerOwnAccountTools(server, makeClient) {
440
441
  title: "Analyze Product Status",
441
442
  description: "Poll a job started by analyze_product. Takes jobId. Returns state (pending, thinking, " +
442
443
  "done, error) and, once done, the generated analysis/brand playbook. Free to poll — the " +
443
- "cost was already charged when analyze_product started the job.",
444
+ "cost was already charged when analyze_product started the job. Use after analyze_product, until the job reports done or error.",
444
445
  _meta: viewMeta("analyze_product_status"),
445
446
  annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
446
447
  inputSchema: z
@@ -467,13 +468,13 @@ export function registerOwnAccountTools(server, makeClient) {
467
468
  "appId plus the draft fields to review something that only exists as arguments. Nothing is " +
468
469
  "published. If the AI reviewer is unavailable the result carries degraded: true and a " +
469
470
  "warning — treat those scores as generic placeholders, never as real feedback. Free — " +
470
- "nothing is billed for this, same as the dashboard's own pre-publish review.",
471
+ "nothing is billed for this, same as the dashboard's own pre-publish review. Use when the user wants feedback on a post before publishing it.",
471
472
  _meta: viewMeta("review_post"),
472
473
  // Not read-only: given a postId the backend saves the review and score
473
474
  // onto that scheduled post (`save_ai_review`), overwriting whatever was
474
475
  // there. Free, which is not the same thing — a host that auto-approves
475
476
  // read-only tools would let this rewrite pipeline data unattended.
476
- annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
477
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
477
478
  inputSchema: z
478
479
  .object({
479
480
  postId: z.number().int().optional().describe("A post already in your pipeline. Alternative to appId + draft fields."),
@@ -505,9 +506,9 @@ export function registerOwnAccountTools(server, makeClient) {
505
506
  "schedule anything — this only returns the draft text for you to present, refine, or hand " +
506
507
  "to a tool that persists it. Billed like the dashboard's own Draft Post button: consumes " +
507
508
  "your workspace's plan AI credits, a different balance from your personal MCP credits and " +
508
- "not tracked by check_nooticr_credits.",
509
+ "not tracked by check_nooticr_credits. Use when the user asks for a post written for their own product.",
509
510
  _meta: viewMeta("draft_post"),
510
- annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
511
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: false, openWorldHint: false },
511
512
  inputSchema: z
512
513
  .object({
513
514
  appId: z
@@ -537,13 +538,13 @@ export function registerOwnAccountTools(server, makeClient) {
537
538
  "plus whatever analytics (GA4, Search Console, PostHog) and fleet analysis you have " +
538
539
  "synced. It changes nothing of yours, but it is not free: billed like the dashboard's own " +
539
540
  "Growth Brief button, from your workspace's plan AI credits rather than your personal " +
540
- "MCP credits.",
541
+ "MCP credits. Use when the user asks how their product's social presence is doing and what to do next.",
541
542
  _meta: viewMeta("growth_brief"),
542
543
  // Spending the workspace's plan AI credits (`CREDIT_GROWTH_BRIEF`) is
543
544
  // what puts this outside read-only, by the same rule that already
544
545
  // covers draft_post and generate_content_plan. It writes no content of
545
546
  // the user's, which is why the description says so in those words.
546
- annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
547
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: false, openWorldHint: false },
547
548
  inputSchema: z
548
549
  .object({
549
550
  appId: z
@@ -569,7 +570,7 @@ export function registerOwnAccountTools(server, makeClient) {
569
570
  "a hook, caption, hashtags and a full production script for each, grounded in what already " +
570
571
  "worked in your post history. Saves the generated plan (fetch it later with the free " +
571
572
  "get_content_plan) but does not schedule or publish any post. Billed like the dashboard's " +
572
- "own Content Plan button: your workspace's plan AI credits, not your personal MCP credits.",
573
+ "own Content Plan button: your workspace's plan AI credits, not your personal MCP credits. Use when the user asks for a week of planned posts for their product.",
573
574
  _meta: viewMeta("generate_content_plan"),
574
575
  annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
575
576
  inputSchema: z
@@ -601,11 +602,11 @@ export function registerOwnAccountTools(server, makeClient) {
601
602
  description: "Generate timed on-screen caption cues for a video on your own product — a transcript " +
602
603
  "plus start/end-timed lines. Does not burn captions onto any video or touch stored media; " +
603
604
  "only returns the cue data. If no AI provider is configured the result carries " +
604
- "provider: \"mock\" placeholder captions instead of failing — check that field before " +
605
+ "provider: \"mock\" placeholder captions rather than an error — check that field before " +
605
606
  "treating the captions as real. Billed like the dashboard's own Generate Captions button: " +
606
- "your workspace's plan AI credits, not your personal MCP credits.",
607
+ "your workspace's plan AI credits, not your personal MCP credits. Use when the user needs timed on-screen captions for one of their videos.",
607
608
  _meta: viewMeta("generate_captions"),
608
- annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
609
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: false, openWorldHint: false },
609
610
  inputSchema: z
610
611
  .object({
611
612
  appId: z
@@ -35,7 +35,7 @@ export function registerReplyTools(server, makeClient) {
35
35
  "connected with capabilities: [\"manage_comments\"] (see connect_social_account); networks " +
36
36
  "that cannot be answered come back under `unsupported` with the reason. Optional platform, " +
37
37
  "influencerId, postId, sinceDays (1-30, default 7), limit (1-100, default 30), " +
38
- "includeAnswered. Read-only: it sends nothing. No cost to call.",
38
+ "includeAnswered. Read-only: it sends nothing. No cost to call. Use when the user wants to reply to comments on their own connected account.",
39
39
  annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
40
40
  outputSchema: OUTPUT_SCHEMAS.list_comments_to_answer,
41
41
  inputSchema: z
@@ -64,7 +64,7 @@ export function registerReplyTools(server, makeClient) {
64
64
  "posted. Pass `text` to use the user's own wording (no cost), or omit it and nooticr " +
65
65
  "writes one (costs 1 nooticr credit); `guidance` steers the writing. Replies may not contain links, " +
66
66
  "emails, phone numbers or @-mentions of anyone but the commenter. Returns a draftId and a " +
67
- "confirmationToken: show the reply to the user first, then post it with post_comment_reply.",
67
+ "confirmationToken: show the reply to the user first, then post it with post_comment_reply. Use when the user wants to answer one comment from list_comments_to_answer.",
68
68
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
69
69
  outputSchema: OUTPUT_SCHEMAS.draft_comment_reply,
70
70
  inputSchema: z
@@ -93,7 +93,7 @@ export function registerReplyTools(server, makeClient) {
93
93
  "has seen and agreed to the exact text: pass the draftId, confirm: true and the " +
94
94
  "confirmationToken from draft_comment_reply. Each comment is answered at most once and " +
95
95
  "daily limits apply. It writes to a third-party network and cannot be undone from here. " +
96
- "No cost to call.",
96
+ "No cost to call. Use only once the user has approved a draft's exact text.",
97
97
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
98
98
  outputSchema: OUTPUT_SCHEMAS.post_comment_reply,
99
99
  inputSchema: z
@@ -29,6 +29,8 @@ const cell = z
29
29
  const rows = z.array(z.array(cell));
30
30
  const READ = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true };
31
31
  const WRITE = { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true };
32
+ // Overwriting a range replaces whatever the cells held, which append and add-sheet never do.
33
+ const OVERWRITE = { ...WRITE, destructiveHint: true };
32
34
  export function registerSheetsTools(server, makeClient) {
33
35
  const passthrough = (name) => async (args, extra) => {
34
36
  const client = await makeClient({ ...extra, arguments: args });
@@ -43,7 +45,7 @@ export function registerSheetsTools(server, makeClient) {
43
45
  title: "Create Spreadsheet",
44
46
  description: "Create a Google spreadsheet in the connected Google account, with the named sheets and a header row on each. " +
45
47
  "Returns its id and URL; Nooticr owns the file, so later writes need no approval. " +
46
- "Requires the product's Google Sheets connection. Free.",
48
+ "Requires the product's Google Sheets connection. Free. Use when the user wants results written to a new Google spreadsheet.",
47
49
  annotations: WRITE,
48
50
  inputSchema: z
49
51
  .object({
@@ -60,7 +62,7 @@ export function registerSheetsTools(server, makeClient) {
60
62
  server.registerTool("sheets_list", {
61
63
  title: "List Spreadsheets",
62
64
  description: "List the spreadsheets this connection can see: the ones Nooticr created and the ones the customer picked. " +
63
- "Each says whether it was created or picked and whether the employee may edit it without approval. Free.",
65
+ "Each says whether it was created or picked and whether the employee may edit it without approval. Free. Use to find a spreadsheet's id before reading or writing it.",
64
66
  annotations: READ,
65
67
  inputSchema: z.object({ appId, limit: z.number().int().min(1).max(100).optional() }).strict(),
66
68
  outputSchema: OUTPUT_SCHEMAS.sheets_list,
@@ -72,7 +74,7 @@ export function registerSheetsTools(server, makeClient) {
72
74
  'and a range open at the bottom ("A:F", "Ledger!$A:$F") or longer than the cap is cut to the rows that fit, ' +
73
75
  "with truncated=true when more may remain: read further down with a range that starts after the last row. " +
74
76
  'A bare name without quotes ("Expenses") is passed to Google as written, since it may be a named range, ' +
75
- 'so name a tab as "Ledger!A:F" or "\'My Tab\'" to have it bounded. Free.',
77
+ 'so name a tab as "Ledger!A:F" or "\'My Tab\'" to have it bounded. Free. Use when the user asks about data held in one of their connected spreadsheets.',
76
78
  annotations: READ,
77
79
  inputSchema: z.object({ appId, id: spreadsheetId, range: z.string() }).strict(),
78
80
  outputSchema: OUTPUT_SCHEMAS.sheets_read,
@@ -81,7 +83,7 @@ export function registerSheetsTools(server, makeClient) {
81
83
  title: "Append Spreadsheet Rows",
82
84
  description: "Append rows after the last row of a sheet (tab). Values are typed: numbers stay numbers and ISO dates become sheet dates. " +
83
85
  "At most 10000 cells per call. Free on a file Nooticr created or one the customer allowed you to edit; " +
84
- "on any other file the customer picked it waits for their approval.",
86
+ "on any other file the customer picked it waits for their approval. Use to add new rows below the existing data in a sheet.",
85
87
  annotations: WRITE,
86
88
  inputSchema: z.object({ appId, id: spreadsheetId, sheet: z.string().describe("Tab name."), rows }).strict(),
87
89
  outputSchema: OUTPUT_SCHEMAS.sheets_append_rows,
@@ -89,14 +91,14 @@ export function registerSheetsTools(server, makeClient) {
89
91
  server.registerTool("sheets_update_range", {
90
92
  title: "Update Spreadsheet Range",
91
93
  description: 'Overwrite a range (A1 notation, e.g. "Ledger!B2:C3") with values. Typed like sheets_append_rows; ' +
92
- "at most 10000 cells per call. Same approval rule as sheets_append_rows: free on a file Nooticr created or one the customer allowed you to edit, otherwise it waits for their approval.",
93
- annotations: WRITE,
94
+ "at most 10000 cells per call. Same approval rule as sheets_append_rows: free on a file Nooticr created or one the customer allowed you to edit, otherwise it waits for their approval. Use to change values that are already in a sheet.",
95
+ annotations: OVERWRITE,
94
96
  inputSchema: z.object({ appId, id: spreadsheetId, range: z.string(), values: rows }).strict(),
95
97
  outputSchema: OUTPUT_SCHEMAS.sheets_update_range,
96
98
  }, passthrough("sheets_update_range"));
97
99
  server.registerTool("sheets_add_sheet", {
98
100
  title: "Add Spreadsheet Tab",
99
- description: "Add an empty sheet (tab) to a spreadsheet. Same approval rule as sheets_append_rows: free on a file Nooticr created or one the customer allowed you to edit, otherwise it waits for their approval.",
101
+ description: "Add an empty sheet (tab) to a spreadsheet. Same approval rule as sheets_append_rows: free on a file Nooticr created or one the customer allowed you to edit, otherwise it waits for their approval. Use when a spreadsheet needs a new tab.",
100
102
  annotations: WRITE,
101
103
  inputSchema: z.object({ appId, id: spreadsheetId, name: z.string() }).strict(),
102
104
  outputSchema: OUTPUT_SCHEMAS.sheets_add_sheet,
@@ -40,7 +40,7 @@ export function registerStoreReadTools(server, makeClient) {
40
40
  "shop \"your-store.myshopify.com\"). Optionally appId ties the store to one product. The " +
41
41
  "owner approves at Amazon or Shopify; nothing is connected until they do, and no " +
42
42
  "credential passes through this tool. Nooticr only asks for access to listing copy and " +
43
- "prices. Each call mints a fresh link. No cost to call.",
43
+ "prices. Each call mints a fresh link. No cost to call. Use when the user wants to connect their Amazon or Shopify store.",
44
44
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
45
45
  inputSchema: z
46
46
  .object({
@@ -60,7 +60,7 @@ export function registerStoreReadTools(server, makeClient) {
60
60
  "exact SKU; Shopify: product search), limit (up to 50), cursor from nextCursor, " +
61
61
  "productRef to read one product by SKU or GID, includeIssues, includePricing (Amazon). " +
62
62
  "For Amazon, source \"report\" reads the whole catalogue through a report: it answers " +
63
- "status in_progress with a reportId and poll arguments until done. Read-only. No cost to call.",
63
+ "status in_progress with a reportId and poll arguments until done. Read-only. No cost to call. Use when the user asks about the products in their connected store.",
64
64
  annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
65
65
  inputSchema: z
66
66
  .object({