@nooticr/mcp 1.26.79 → 1.26.81
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 +11 -7
- package/dist/shared/amazon.js +3 -3
- package/dist/shared/brand-watch.js +9 -10
- package/dist/shared/connections.js +1 -1
- package/dist/shared/creative.js +10 -10
- package/dist/shared/crm.js +125 -0
- package/dist/shared/employees.js +8 -8
- package/dist/shared/getting-started.js +7 -7
- package/dist/shared/jobs.js +26 -28
- package/dist/shared/loading-plans.js +5 -0
- package/dist/shared/marketplace.js +5 -5
- package/dist/shared/output-schemas.js +5 -0
- package/dist/shared/own-account.js +20 -19
- package/dist/shared/replies.js +3 -3
- package/dist/shared/sheets.js +9 -7
- package/dist/shared/store-read.js +2 -2
- package/dist/shared/tools-def.js +60 -36
- package/dist/shared/tools.js +84 -83
- package/dist/shared/watchlist.js +2 -2
- package/package.json +1 -1
package/dist/shared/jobs.js
CHANGED
|
@@ -675,7 +675,7 @@ export function registerJobTools(server, makeClient, store) {
|
|
|
675
675
|
"Costs 2 nooticr credits for the post list plus 2 per post opened — 14 credits at the " +
|
|
676
676
|
"default of 6 posts. Use when the job is to answer people; search_mentions is for what " +
|
|
677
677
|
"strangers say elsewhere.",
|
|
678
|
-
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint:
|
|
678
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: false, openWorldHint: true },
|
|
679
679
|
outputSchema: OUTPUT_SCHEMAS.answer_my_audience,
|
|
680
680
|
inputSchema: z
|
|
681
681
|
.object({
|
|
@@ -861,15 +861,14 @@ export function registerJobTools(server, makeClient, store) {
|
|
|
861
861
|
server.registerTool("track_creator", {
|
|
862
862
|
title: "Track Competitor",
|
|
863
863
|
_meta: viewMeta("track_creator"),
|
|
864
|
-
description: "
|
|
865
|
-
"they shipped since you last looked — this is the tool for all of those. What a creator " +
|
|
866
|
-
"shipped recently and which of it beat THEIR OWN baseline: fetches their recent posts " +
|
|
864
|
+
description: "What a creator shipped recently and which of it beat THEIR OWN baseline: fetches their recent posts " +
|
|
867
865
|
"once and scores each against the median of that same window, because a raw view count " +
|
|
868
866
|
"mostly measures follower count — outperformance against themselves is the signal. If " +
|
|
869
867
|
"they are on your watchlist it also marks what is new since your last track_creator " +
|
|
870
868
|
"call and moves that marker forward. " +
|
|
871
|
-
"Consumes 2 nooticr credits — one post list, whatever the window size. Use
|
|
872
|
-
"
|
|
869
|
+
"Consumes 2 nooticr credits — one post list, whatever the window size. Use to track a " +
|
|
870
|
+
"competitor, keep an eye on a rival, see how someone is doing lately, or catch what they " +
|
|
871
|
+
"shipped since you last looked; analyze_creator_profile is for a one-off profile teardown.",
|
|
873
872
|
annotations: {
|
|
874
873
|
readOnlyHint: false,
|
|
875
874
|
destructiveHint: true,
|
|
@@ -1059,15 +1058,15 @@ export function registerJobTools(server, makeClient, store) {
|
|
|
1059
1058
|
title: "Compare Creators",
|
|
1060
1059
|
_meta: viewMeta("compare_creators"),
|
|
1061
1060
|
description: "Two to five creators side by side, each scored against THEIR OWN recent median rather " +
|
|
1062
|
-
"than against each other's raw numbers
|
|
1063
|
-
"
|
|
1061
|
+
"than against each other's raw numbers, because a raw view count mostly measures follower " +
|
|
1062
|
+
"count. Returns per creator: " +
|
|
1064
1063
|
"how many posts were scored, their own median, the share of the window that beat it, how " +
|
|
1065
|
-
"hard they beat it when they did, every post's ratio, and their
|
|
1064
|
+
"hard they beat it when they did, every post's ratio, and their highest and lowest post. The " +
|
|
1066
1065
|
"ranking is yours to make: 'how often' and 'how big' usually disagree, and a hit rate " +
|
|
1067
1066
|
"over a short window is one post either way, so the window size travels with every " +
|
|
1068
1067
|
"number. Fetches one post list per creator: 2 nooticr credits each, 4-10 in total. " +
|
|
1069
|
-
"Use
|
|
1070
|
-
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint:
|
|
1068
|
+
"Use to compare hit rates between two to five creators, yourself included; track_creator covers one creator.",
|
|
1069
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: false, openWorldHint: true },
|
|
1071
1070
|
outputSchema: OUTPUT_SCHEMAS.compare_creators,
|
|
1072
1071
|
inputSchema: z
|
|
1073
1072
|
.object({
|
|
@@ -1131,13 +1130,13 @@ export function registerJobTools(server, makeClient, store) {
|
|
|
1131
1130
|
title: "Watchlist Standings",
|
|
1132
1131
|
_meta: viewMeta("watchlist_standings"),
|
|
1133
1132
|
description: "The same comparison across everyone on your watchlist, answering 'who moved' in one " +
|
|
1134
|
-
"call
|
|
1133
|
+
"call. Each is scored against their own recent median, so the " +
|
|
1135
1134
|
"numbers are comparable between accounts of different sizes; the window size travels " +
|
|
1136
1135
|
"with each so a short one is not ranked as if it were a season. Reads the watchlist " +
|
|
1137
1136
|
"itself for free and fetches one post list per creator on it: 2 nooticr credits each, so " +
|
|
1138
1137
|
"the price is the size of your watchlist — it confirms before spending. Use for the " +
|
|
1139
|
-
"weekly 'who is accelerating'; catch_up_watchlist
|
|
1140
|
-
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint:
|
|
1138
|
+
"weekly 'who is accelerating'; catch_up_watchlist lists what is new since the last check.",
|
|
1139
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: false, openWorldHint: true },
|
|
1141
1140
|
outputSchema: OUTPUT_SCHEMAS.watchlist_standings,
|
|
1142
1141
|
inputSchema: z
|
|
1143
1142
|
.object({
|
|
@@ -1215,7 +1214,7 @@ export function registerJobTools(server, makeClient, store) {
|
|
|
1215
1214
|
"per candidate, so the result says so and tells you how to check a finalist yourself. " +
|
|
1216
1215
|
"Consumes 2 nooticr credits, or 4 with a seed creator. Use to build a list to vet; " +
|
|
1217
1216
|
"get_similar_creators is the raw lookalike call.",
|
|
1218
|
-
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint:
|
|
1217
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: false, openWorldHint: true },
|
|
1219
1218
|
outputSchema: OUTPUT_SCHEMAS.who_should_i_work_with,
|
|
1220
1219
|
inputSchema: z
|
|
1221
1220
|
.object({
|
|
@@ -1364,7 +1363,7 @@ export function registerJobTools(server, makeClient, store) {
|
|
|
1364
1363
|
"quartiles, ratio and percentile — so the answer can be 'this is a normal result, not a " +
|
|
1365
1364
|
"failure'. Consumes 3 nooticr credits (1 for the post, 2 for the window). Use when you " +
|
|
1366
1365
|
"have one post and no comparison; compare_social_posts is for two URLs you already picked.",
|
|
1367
|
-
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint:
|
|
1366
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: false, openWorldHint: true },
|
|
1368
1367
|
outputSchema: OUTPUT_SCHEMAS.why_did_this_underperform,
|
|
1369
1368
|
inputSchema: z
|
|
1370
1369
|
.object({
|
|
@@ -1485,7 +1484,7 @@ export function registerJobTools(server, makeClient, store) {
|
|
|
1485
1484
|
"Consumes 2 nooticr credits for the post list, 2 per post read for comments, and 2 for the " +
|
|
1486
1485
|
"niche sweep — 12 at the default of 4 posts. Use to decide what to film; niche_report " +
|
|
1487
1486
|
"covers the supply half alone.",
|
|
1488
|
-
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint:
|
|
1487
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: false, openWorldHint: true },
|
|
1489
1488
|
outputSchema: OUTPUT_SCHEMAS.what_should_i_make_next,
|
|
1490
1489
|
inputSchema: z
|
|
1491
1490
|
.object({
|
|
@@ -1669,8 +1668,7 @@ export function registerJobTools(server, makeClient, store) {
|
|
|
1669
1668
|
"anything: each row is a draft, and a reply is posted only after the creator confirms its " +
|
|
1670
1669
|
"exact text. Each comment shows with what you decided to do " +
|
|
1671
1670
|
"about it and your draft underneath, grouped under its post, so they can work through " +
|
|
1672
|
-
"them one at a time and skip the rest. Call this after you have drafted the replies,
|
|
1673
|
-
"instead of drafting them.",
|
|
1671
|
+
"them one at a time and skip the rest. Call this after you have drafted the replies.",
|
|
1674
1672
|
annotations: {
|
|
1675
1673
|
readOnlyHint: true,
|
|
1676
1674
|
destructiveHint: false,
|
|
@@ -1810,14 +1808,14 @@ export function registerJobTools(server, makeClient, store) {
|
|
|
1810
1808
|
title: "Search Spoken Mentions",
|
|
1811
1809
|
_meta: viewMeta("search_spoken_mentions"),
|
|
1812
1810
|
description: "Brand mentions people SAY but never type. search_mentions reads captions, post bodies " +
|
|
1813
|
-
"and comments — text — so
|
|
1811
|
+
"and comments — text — so it does not see a brand named only out loud in a video. " +
|
|
1814
1812
|
"This reads the words actually spoken, from the platform's own caption track (not our own " +
|
|
1815
1813
|
"speech recognition — see get_post_transcript), and searches them for a term. Narrows to " +
|
|
1816
1814
|
"candidate posts first (a niche/keyword sweep, named creator handles, and/or your " +
|
|
1817
1815
|
"watchlist), transcribes only the most-viewed survivors up to a hard ceiling you set, and " +
|
|
1818
1816
|
"returns the matched line with surrounding context so tone can be judged. TikTok, YouTube " +
|
|
1819
|
-
"and Douyin — the networks whose post detail already carries a caption track,
|
|
1820
|
-
"
|
|
1817
|
+
"and Douyin — the networks whose post detail already carries a caption track, so a " +
|
|
1818
|
+
"transcript is read directly rather than waited for. Every other network's audio " +
|
|
1821
1819
|
"can be transcribed too, at the same price, but not yet from here: see " +
|
|
1822
1820
|
"get_post_transcript for that route. Even on these three a video whose creator disabled " +
|
|
1823
1821
|
"captions is invisible to this tool; coverage is real but " +
|
|
@@ -1828,7 +1826,7 @@ export function registerJobTools(server, makeClient, store) {
|
|
|
1828
1826
|
"count. Above 6 credits worst-case it asks first, like search_mentions. Use when a term " +
|
|
1829
1827
|
"might be spoken on camera but not written anywhere; search_mentions is for what people " +
|
|
1830
1828
|
"typed.",
|
|
1831
|
-
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint:
|
|
1829
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: false, openWorldHint: true },
|
|
1832
1830
|
outputSchema: OUTPUT_SCHEMAS.search_spoken_mentions,
|
|
1833
1831
|
inputSchema: z
|
|
1834
1832
|
.object({
|
|
@@ -2130,14 +2128,14 @@ export function registerJobTools(server, makeClient, store) {
|
|
|
2130
2128
|
description: "Find people describing a problem in their own words — prospects, early users, anyone " +
|
|
2131
2129
|
"whose complaint your product answers. Say the problem the way a person would say it " +
|
|
2132
2130
|
"(\"I spend hours checking competitors by hand\") and this searches for posts shaped like " +
|
|
2133
|
-
"that complaint, not for the topic it is about.
|
|
2134
|
-
"
|
|
2131
|
+
"that complaint, not for the topic it is about. Searching the TOPIC returns marketing " +
|
|
2132
|
+
"about it; discover_social_posts covers that. " +
|
|
2135
2133
|
"Widens the query into the forms a complaint takes — the plain phrasing, the " +
|
|
2136
2134
|
"does-anyone-else question, the is-there-a-tool ask — and merges what each returns. " +
|
|
2137
|
-
"It is a
|
|
2135
|
+
"It is a broad search and NOT a filter: the posts come back for you to judge, and " +
|
|
2138
2136
|
"many will be off-target. Consumes 2 nooticr credits per search, and each platform is searched once per query shape — three by default, so 6 credits per platform. Defaults to " +
|
|
2139
|
-
"reddit, where people describe workflow pain in sentences; add twitter for volume. Not searchable here: linkedin.",
|
|
2140
|
-
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint:
|
|
2137
|
+
"reddit, where people describe workflow pain in sentences; add twitter for volume. Not searchable here: linkedin. Use to find prospects or early users by the way they describe a problem.",
|
|
2138
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: false, openWorldHint: true },
|
|
2141
2139
|
outputSchema: OUTPUT_SCHEMAS.find_people_with_problem,
|
|
2142
2140
|
inputSchema: z
|
|
2143
2141
|
.object({
|
|
@@ -503,6 +503,11 @@ export const LOADING_PLANS = {
|
|
|
503
503
|
list_employees: { label: "Reading your employees", kind: "list", n: 3, steps: [], free: true },
|
|
504
504
|
pause_employee: { label: "Updating the employee", kind: "text", n: 1, steps: [], free: true },
|
|
505
505
|
fire_employee: { label: "Letting the employee go", kind: "text", n: 1, steps: [], free: true },
|
|
506
|
+
// Creator CRM (crm.ts): free, and none of it fetches.
|
|
507
|
+
crm_upsert_creator: { label: "Saving the creator", kind: "text", n: 1, steps: [], free: true },
|
|
508
|
+
crm_list_creators: { label: "Reading your creator pipeline", kind: "list", n: 3, steps: [], free: true },
|
|
509
|
+
crm_update_status: { label: "Updating the creator's status", kind: "text", n: 1, steps: [], free: true },
|
|
510
|
+
crm_log_interaction: { label: "Logging the interaction", kind: "text", n: 1, steps: [], free: true },
|
|
506
511
|
list_pending_actions: { label: "Reading what awaits approval", kind: "list", n: 3, steps: [], free: true },
|
|
507
512
|
get_employee_audit: { label: "Reading the audit trail", kind: "list", n: 3, steps: [], free: true },
|
|
508
513
|
approve_action: { label: "Approving the action", kind: "text", n: 1, steps: [], free: true },
|
|
@@ -274,7 +274,7 @@ export function registerMarketplaceTools(server, makeClient) {
|
|
|
274
274
|
"repeating until the scan is done. Never conclude, or end your turn, on an incomplete " +
|
|
275
275
|
"scan: a read of 4 of 10 listings presented as the category is wrong. " +
|
|
276
276
|
"For Amazon specifically, scan_amazon_category returns the same shape plus Amazon's own " +
|
|
277
|
-
"review-aspect counts.",
|
|
277
|
+
"review-aspect counts. Use for a category or a competitive set on one of these marketplaces; get_marketplace_product reads one product.",
|
|
278
278
|
annotations: {
|
|
279
279
|
readOnlyHint: false,
|
|
280
280
|
destructiveHint: false,
|
|
@@ -407,7 +407,7 @@ export function registerMarketplaceTools(server, makeClient) {
|
|
|
407
407
|
"of the same marketplace and `query`, so it fetches nothing and never re-scans — run " +
|
|
408
408
|
"scan_marketplace_category first, and again later, to have two runs to compare. `new` and " +
|
|
409
409
|
"`gone` mean entered or left the results, not listed or delisted. A category scanned only " +
|
|
410
|
-
"once has no earlier run and says so. Free.",
|
|
410
|
+
"once has no earlier run and says so. Free. Use when the user asks what changed in a category since an earlier scan.",
|
|
411
411
|
annotations: {
|
|
412
412
|
readOnlyHint: true,
|
|
413
413
|
destructiveHint: false,
|
|
@@ -442,7 +442,7 @@ export function registerMarketplaceTools(server, makeClient) {
|
|
|
442
442
|
server.registerTool("get_price_history", {
|
|
443
443
|
title: "Get Price History",
|
|
444
444
|
description: "One product's price over time — or its rating, review count or availability with `metric` — " +
|
|
445
|
-
"from the observations stored each time the product was scanned. `marketplace` and `id` name " +
|
|
445
|
+
"from the observations stored each time the product was scanned. Use when someone asks how a product's price, rating, review count or stock moved over time. `marketplace` and `id` name " +
|
|
446
446
|
"the product the same way get_marketplace_product does; `from` and `to` take dates (default: " +
|
|
447
447
|
"the last 30 days). Returns the points oldest first with min, max, first, last and the " +
|
|
448
448
|
"percentage change from first to last. A point is stored only when something changed, so the " +
|
|
@@ -498,7 +498,7 @@ export function registerMarketplaceTools(server, makeClient) {
|
|
|
498
498
|
annotations: {
|
|
499
499
|
readOnlyHint: true,
|
|
500
500
|
destructiveHint: false,
|
|
501
|
-
idempotentHint:
|
|
501
|
+
idempotentHint: false,
|
|
502
502
|
openWorldHint: true,
|
|
503
503
|
},
|
|
504
504
|
outputSchema: OUTPUT_SCHEMAS.get_marketplace_product,
|
|
@@ -549,7 +549,7 @@ export function registerMarketplaceTools(server, makeClient) {
|
|
|
549
549
|
"cite that the scan does not contain is flagged. Without a scanId, listings you pass are " +
|
|
550
550
|
"drawn marked as not checked against a scan. Free. Your read is attributed to you, never " +
|
|
551
551
|
"presented as a nooticr rating of anyone's product. For Amazon scans use " +
|
|
552
|
-
"show_amazon_category_insights. Call this after you have read the reviews
|
|
552
|
+
"show_amazon_category_insights. Call this after you have read the reviews and written the read.",
|
|
553
553
|
annotations: {
|
|
554
554
|
readOnlyHint: true,
|
|
555
555
|
destructiveHint: false,
|
|
@@ -1785,6 +1785,11 @@ export const OUTPUT_SCHEMAS = {
|
|
|
1785
1785
|
action: anyObject().nullish(),
|
|
1786
1786
|
}),
|
|
1787
1787
|
reject_action: open({ rejected: scalar(), action: anyObject().nullish() }),
|
|
1788
|
+
// Creator CRM (nooticr-server E1): the creator/interaction views are the server's own shapes.
|
|
1789
|
+
crm_upsert_creator: open({ creator: anyObject().nullish() }),
|
|
1790
|
+
crm_list_creators: open({ creators: listOf(open({ id: scalar(), platform: scalar(), handle: scalar(), status: scalar() })) }),
|
|
1791
|
+
crm_update_status: open({ creator: anyObject().nullish() }),
|
|
1792
|
+
crm_log_interaction: open({ interaction: anyObject().nullish() }),
|
|
1788
1793
|
list_employees: open({ employees: listOf(open({ id: scalar(), name: scalar(), status: scalar() })) }),
|
|
1789
1794
|
list_pending_actions: open({ actions: listOf(open({ id: scalar(), employeeId: scalar() })) }),
|
|
1790
1795
|
// AU1 (nooticr-server employees/mcp.rs get_employee_audit; event shape from employee_audit_view::event_view).
|
|
@@ -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:
|
|
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:
|
|
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:
|
|
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
|
|
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:
|
|
609
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: false, openWorldHint: false },
|
|
609
610
|
inputSchema: z
|
|
610
611
|
.object({
|
|
611
612
|
appId: z
|
package/dist/shared/replies.js
CHANGED
|
@@ -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
|
package/dist/shared/sheets.js
CHANGED
|
@@ -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:
|
|
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({
|