@nooticr/mcp 1.26.22 → 1.26.23
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 +28 -14
- package/dist/shared/evidence.js +38 -12
- package/dist/shared/jobs.js +7 -11
- package/dist/shared/prompts.js +1 -1
- package/dist/shared/spend.js +23 -9
- package/dist/shared/tools-def.js +3 -3
- package/dist/shared/tools.js +6 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,8 +14,8 @@ model of ours for an opinion first. You pay for the fetch and nothing else.
|
|
|
14
14
|
|
|
15
15
|
It also **monitors a name**: `search_mentions` sweeps nine of those networks for
|
|
16
16
|
every comment that says your brand, inside a date window you choose, and
|
|
17
|
-
`search_spoken_mentions` reads the words actually said out loud in TikTok
|
|
18
|
-
YouTube videos for the mentions that were never typed anywhere.
|
|
17
|
+
`search_spoken_mentions` reads the words actually said out loud in TikTok,
|
|
18
|
+
YouTube and Douyin videos for the mentions that were never typed anywhere.
|
|
19
19
|
|
|
20
20
|
Runs over stdio locally or as a hosted connector at `https://mcp.nooticr.com/mcp`.
|
|
21
21
|
Billed against your nooticr credits; new accounts get 20 free.
|
|
@@ -52,7 +52,7 @@ npx @nooticr/mcp login # one-time sign-in (Google)
|
|
|
52
52
|
|
|
53
53
|
## Tools
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
64 tools, grouped by what you are trying to do. Prices are in nooticr credits and
|
|
56
56
|
match what the server actually charges.
|
|
57
57
|
|
|
58
58
|
Seven of them — the ones under **Answer a question you actually have** — are not
|
|
@@ -67,7 +67,7 @@ them caps that fan-out with an argument.
|
|
|
67
67
|
| Tool | Credits | What it is for |
|
|
68
68
|
|------|---------|----------------|
|
|
69
69
|
| `get_social_media` | 1 | The post's facts and media — contentType, title, caption, author, stats, direct media URLs, plus an inline thumbnail. Use when you want the post itself and nothing interpreted. |
|
|
70
|
-
| `get_post_transcript` | 1 | The words actually spoken
|
|
70
|
+
| `get_post_transcript` | 1 | The words actually spoken. Where the platform publishes a caption track (TikTok, Douyin, YouTube) it is read as-is — exact rather than inferred, and far cheaper than watching the video; everywhere else the post's own audio is transcribed, which needs speech-to-text configured on the server. Use before any analysis when the wording matters. |
|
|
71
71
|
| `get_post_frames` | 2 | Frames sampled evenly across a post's video, returned as **images you can actually look at** — not a description of them. ffmpeg opens the stream directly rather than downloading it, so HLS works and an expired link is re-resolved on the spot. Verified live at 3/3 on TikTok, YouTube, Instagram, Douyin and X; Reddit works on video posts. A carousel or slideshow returns its own images unchanged. Each frame costs roughly 1,200 tokens of your context. |
|
|
72
72
|
| `get_post_comments` | 2 | Top comments plus the themes the platform clusters them into, with which ones the creator pinned or liked. Use when you want to read what people wrote. |
|
|
73
73
|
|
|
@@ -95,7 +95,7 @@ them caps that fan-out with an argument.
|
|
|
95
95
|
| `discover_sounds` | 2 | Trending audio with playable previews. Sound is a major ranking signal on TikTok. |
|
|
96
96
|
| `discover_hashtags` | 2 | Trending hashtags with volumes and whether each is rising, cooling or steady. |
|
|
97
97
|
| `find_hook_pattern` | 2 | A creator's recent posts, so their opening lines can be read as a set and turned into fill-in-the-blank templates. One `get_user_posts` call. |
|
|
98
|
-
| `search_mentions` | 2 per network (5 for Xiaohongshu) | **Brand monitoring.** Every *comment* that names a term, across nine networks at once, grouped under the post it was left on. A brand is named far more often in the replies than in a caption, so the comment is the unit — not the post. Takes a `since` date to read a past window, and pages with `offset`/`pageSize` so a nine-network sweep does not arrive all at once. Does not read speech inside a video — `search_spoken_mentions` does, on TikTok and
|
|
98
|
+
| `search_mentions` | 2 per network (5 for Xiaohongshu) | **Brand monitoring.** Every *comment* that names a term, across nine networks at once, grouped under the post it was left on. A brand is named far more often in the replies than in a caption, so the comment is the unit — not the post. Takes a `since` date to read a past window, and pages with `offset`/`pageSize` so a nine-network sweep does not arrive all at once. Does not read speech inside a video — `search_spoken_mentions` does, on TikTok, YouTube and Douyin. |
|
|
99
99
|
| `watch_creator` | free | Add a creator to your watchlist. Stores the handle only — nothing is fetched. |
|
|
100
100
|
| `unwatch_creator` | free | Drop a creator from the watchlist. |
|
|
101
101
|
| `catch_up_watchlist` | 2 per creator | What everyone you watch has posted since your last catch-up. Compares against the snapshot taken last time and moves it forward, so it answers "what is new" rather than "what exists". |
|
|
@@ -117,7 +117,7 @@ them caps that fan-out with an argument.
|
|
|
117
117
|
| `show_collab_shortlist` | free | Draws the candidates you scored, ranked, and asks the user which to approach. The scores are attributed to your model, not presented as a nooticr rating, and a candidate scored without anything having been opened is marked unverified. Makes no requests. |
|
|
118
118
|
| `why_did_this_underperform` | 3 | One post against the creator's own recent distribution, with the post taken back out of its own baseline. Returns median, quartiles, ratio and percentile, so the answer can be "this is an ordinary result, not a failure". Different question from `compare_posts`, which weighs two URLs you already picked. |
|
|
119
119
|
| `what_should_i_make_next` | 2 + 2 per post read + 2 (12 by default) | Demand against supply: what your commenters explicitly ask for, set beside what a niche sweep shows is already being made. A gap nobody asked for is noise; a request nobody serves is the opportunity. Falls back to your most-used hashtag when you name no niche. |
|
|
120
|
-
| `search_spoken_mentions` | 2 per platform narrowed by niche + 2 per creator handle + 1 per transcript, up to `maxTranscripts` | **The mirror of `search_mentions`, for what was said rather than typed.** Narrows to candidate posts (a niche sweep, named handles, and/or your watchlist), transcribes only the most-viewed survivors up to a hard ceiling, and searches the words for the term — TikTok and
|
|
120
|
+
| `search_spoken_mentions` | 2 per platform narrowed by niche + 2 per creator handle + 1 per transcript, up to `maxTranscripts` | **The mirror of `search_mentions`, for what was said rather than typed.** Narrows to candidate posts (a niche sweep, named handles, and/or your watchlist), transcribes only the most-viewed survivors up to a hard ceiling, and searches the words for the term — TikTok, YouTube and Douyin, and only where the creator actually enabled captions. Reports how many candidates were found, transcribed and matched, so the spend is legible. |
|
|
121
121
|
|
|
122
122
|
### Make something
|
|
123
123
|
|
|
@@ -254,17 +254,31 @@ selectable. Free, and it makes no requests.
|
|
|
254
254
|
## Before an expensive call, it asks
|
|
255
255
|
|
|
256
256
|
Most tools print their price in their own description, so a call costs what you
|
|
257
|
-
already read.
|
|
257
|
+
already read. Six do not, because their price is set by an argument — or by a
|
|
258
|
+
list the request never mentions:
|
|
258
259
|
|
|
259
260
|
- `search_mentions` bills **per network swept**, so a bare "monitor my brand"
|
|
260
261
|
sweeps all nine for 21 credits.
|
|
261
|
-
- `catch_up_watchlist` bills **per creator**, so the price is the length of
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
262
|
+
- `catch_up_watchlist` bills **per creator**, so the price is the length of your
|
|
263
|
+
watchlist.
|
|
264
|
+
- `answer_my_audience` and `what_should_i_make_next` bill **per post opened**
|
|
265
|
+
(14 and 12 credits at their defaults), so they ask once the real post count is
|
|
266
|
+
known rather than on a worst case.
|
|
267
|
+
- `search_spoken_mentions` bills per network, per handle and per transcript, so
|
|
268
|
+
it asks on the worst case it could reach.
|
|
269
|
+
- `create_brand_watch` asks **however small the number is**, because what is
|
|
270
|
+
being agreed to is a recurring charge rather than one run.
|
|
271
|
+
|
|
272
|
+
Above 6 credits those first five ask first, over MCP `elicitation` — the client
|
|
273
|
+
shows the number and you accept or decline. Declining spends nothing and is not
|
|
274
|
+
an error. A client that does not support elicitation is not blocked; the call
|
|
275
|
+
runs as it always did.
|
|
276
|
+
|
|
277
|
+
Two rules go further than the threshold. A scheduled watch always asks, because
|
|
278
|
+
the standing arrangement is the thing being approved. And a watch that emails a
|
|
279
|
+
digest refuses to accept a `deliverTo` address unless a person actually saw the
|
|
280
|
+
dialog and said yes — "nobody could be asked" is a fine answer for a spend and
|
|
281
|
+
the wrong one for an address.
|
|
268
282
|
|
|
269
283
|
## Prerequisites
|
|
270
284
|
|
package/dist/shared/evidence.js
CHANGED
|
@@ -35,6 +35,23 @@
|
|
|
35
35
|
* documentation; it is the steering, and it lands in the model's context.
|
|
36
36
|
*/
|
|
37
37
|
import { BACKEND_CALL_CREDITS, costOf } from "./spend.js";
|
|
38
|
+
import { numberOf } from "./performance.js";
|
|
39
|
+
/**
|
|
40
|
+
* Every fan-out is capped; the cap is an argument, and the argument is clamped.
|
|
41
|
+
*
|
|
42
|
+
* Lives here rather than beside its first caller in jobs.ts because the
|
|
43
|
+
* evidence plans need it too, and for a while did not have it: three of them
|
|
44
|
+
* documented a `max` in their zod schema and then passed the argument straight
|
|
45
|
+
* through, so the ceiling a host read was never enforced and `limit: 500`
|
|
46
|
+
* fetched 500. One clamp for both files, and the import already runs this way
|
|
47
|
+
* round — jobs.ts imports evidence.ts, never the reverse.
|
|
48
|
+
*/
|
|
49
|
+
export function clamp(value, fallback, min, max) {
|
|
50
|
+
const n = Math.floor(numberOf(value ?? fallback));
|
|
51
|
+
if (!n)
|
|
52
|
+
return fallback;
|
|
53
|
+
return Math.min(max, Math.max(min, n));
|
|
54
|
+
}
|
|
38
55
|
const url = (a) => String(a.url ?? "");
|
|
39
56
|
/**
|
|
40
57
|
* Platforms whose accounts can be found by NAME. This is the backend's
|
|
@@ -217,24 +234,33 @@ export const EVIDENCE_PLANS = {
|
|
|
217
234
|
args: (a) => ({
|
|
218
235
|
username: String(a.username ?? ""),
|
|
219
236
|
platform: a.platform,
|
|
220
|
-
limit:
|
|
237
|
+
limit: clamp(a.limit, 12, 1, 30),
|
|
221
238
|
}),
|
|
222
|
-
guidance: (a) =>
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
239
|
+
guidance: (a) => {
|
|
240
|
+
const focus = String(a.focus ?? "").trim();
|
|
241
|
+
return [
|
|
242
|
+
`Recent posts by ${a.username}, with their stats.`,
|
|
243
|
+
"",
|
|
244
|
+
"Work out this creator's niche, recurring themes, hook formula, what",
|
|
245
|
+
"over- and under-performs for them, and who their audience is. Use the",
|
|
246
|
+
"spread of the numbers, not just the best post. Name the posts you are",
|
|
247
|
+
"reasoning from.",
|
|
248
|
+
...(focus
|
|
249
|
+
? [
|
|
250
|
+
"",
|
|
251
|
+
`Focus for this pass, as asked: ${focus}. Still cover the profile as a whole, but weight what you cover toward this.`,
|
|
252
|
+
]
|
|
253
|
+
: []),
|
|
254
|
+
ownIt,
|
|
255
|
+
].join("\n");
|
|
256
|
+
},
|
|
231
257
|
},
|
|
232
258
|
find_hook_pattern: {
|
|
233
259
|
via: "get_user_posts",
|
|
234
260
|
args: (a) => ({
|
|
235
261
|
username: String(a.username ?? ""),
|
|
236
262
|
platform: a.platform,
|
|
237
|
-
limit:
|
|
263
|
+
limit: clamp(a.limit, 12, 1, 40),
|
|
238
264
|
}),
|
|
239
265
|
guidance: (a) => [
|
|
240
266
|
`Recent posts by ${a.username}. Their opening lines are the material.`,
|
|
@@ -251,7 +277,7 @@ export const EVIDENCE_PLANS = {
|
|
|
251
277
|
args: (a) => ({
|
|
252
278
|
niche: String(a.niche ?? ""),
|
|
253
279
|
platform: a.platform,
|
|
254
|
-
limit:
|
|
280
|
+
limit: clamp(a.count, 12, 1, 40),
|
|
255
281
|
}),
|
|
256
282
|
guidance: (a) => [
|
|
257
283
|
`Recent posts in the "${a.niche}" niche, with their stats.`,
|
package/dist/shared/jobs.js
CHANGED
|
@@ -56,19 +56,12 @@
|
|
|
56
56
|
import { z } from "zod";
|
|
57
57
|
import { OUTPUT_SCHEMAS } from "./output-schemas.js";
|
|
58
58
|
import { platformFromUrl, postSlug } from "./comment-review.js";
|
|
59
|
-
import { handleMissGuidance, ownIt, PLATFORM_ARG } from "./evidence.js";
|
|
59
|
+
import { clamp, handleMissGuidance, ownIt, PLATFORM_ARG } from "./evidence.js";
|
|
60
60
|
import { confirmSpend, costOf, declinedResult, MAX_SPOKEN_HANDLE_CALLS, MAX_SPOKEN_TRANSCRIPTS, SPOKEN_PLATFORMS, } from "./spend.js";
|
|
61
61
|
import { distributionOf, excluding, numberOf, standing, } from "./performance.js";
|
|
62
62
|
import { normaliseHandle, watchEntryId, watchlistOwner } from "./watchlist.js";
|
|
63
63
|
import { viewMeta } from "./view-meta.js";
|
|
64
64
|
import { extractLinks, COLLAB_RUBRIC, vettingGuidance } from "./collab.js";
|
|
65
|
-
/** Every fan-out is capped; the cap is an argument, and the argument is clamped. */
|
|
66
|
-
function clamp(value, fallback, min, max) {
|
|
67
|
-
const n = Math.floor(numberOf(value ?? fallback));
|
|
68
|
-
if (!n)
|
|
69
|
-
return fallback;
|
|
70
|
-
return Math.min(max, Math.max(min, n));
|
|
71
|
-
}
|
|
72
65
|
const rowsOf = (value) => Array.isArray(value) ? value.filter((r) => r && typeof r === "object") : [];
|
|
73
66
|
const structuredOf = (res) => (res.structured ?? {});
|
|
74
67
|
const reason = (err) => (err instanceof Error ? err.message : String(err));
|
|
@@ -1572,9 +1565,12 @@ export function registerJobTools(server, makeClient, store) {
|
|
|
1572
1565
|
"speech recognition — see get_post_transcript), and searches them for a term. Narrows to " +
|
|
1573
1566
|
"candidate posts first (a niche/keyword sweep, named creator handles, and/or your " +
|
|
1574
1567
|
"watchlist), transcribes only the most-viewed survivors up to a hard ceiling you set, and " +
|
|
1575
|
-
"returns the matched line with surrounding context so tone can be judged. TikTok
|
|
1576
|
-
"
|
|
1577
|
-
"
|
|
1568
|
+
"returns the matched line with surrounding context so tone can be judged. TikTok, YouTube " +
|
|
1569
|
+
"and Douyin — the networks whose post detail already carries a caption track, which is " +
|
|
1570
|
+
"what makes a transcript one cheap read rather than a wait. Every other network's audio " +
|
|
1571
|
+
"can be transcribed too, at the same price, but not yet from here: see " +
|
|
1572
|
+
"get_post_transcript for that route. Even on these three a video whose creator disabled " +
|
|
1573
|
+
"captions is invisible to this tool; coverage is real but " +
|
|
1578
1574
|
"partial, and the result says how many candidates were found, transcribed and matched so " +
|
|
1579
1575
|
"the gap is never silent. Costs 2 nooticr credits per platform a niche is searched on, 2 " +
|
|
1580
1576
|
"per creator handle checked (including ones added by useWatchlist), and 1 per transcript " +
|
package/dist/shared/prompts.js
CHANGED
|
@@ -355,7 +355,7 @@ export function registerPrompts(server) {
|
|
|
355
355
|
`than the caption, because that is usually where a brand gets named. 2 credits per network ` +
|
|
356
356
|
`swept, 5 for Xiaohongshu — a bare call with no platforms sweeps all nine for 21 credits, so ` +
|
|
357
357
|
`say that number before you spend it if the user has not already narrowed it.\n` +
|
|
358
|
-
`2. search_spoken_mentions for "${brand}"${niche ? ` narrowed by "${niche}"` : ", narrowed by the niche you just found"} — the words said out loud on TikTok and ` +
|
|
358
|
+
`2. search_spoken_mentions for "${brand}"${niche ? ` narrowed by "${niche}"` : ", narrowed by the niche you just found"} — the words said out loud on TikTok, Douyin and ` +
|
|
359
359
|
`YouTube that search_mentions cannot see, because it only reads text. 2 credits per platform ` +
|
|
360
360
|
`the niche is searched on, 2 per named handle checked, 1 per transcript actually fetched up ` +
|
|
361
361
|
`to maxTranscripts — pricier than search_mentions, which is why it runs second.\n` +
|
package/dist/shared/spend.js
CHANGED
|
@@ -11,17 +11,31 @@ export const SEARCH_PLATFORMS = [
|
|
|
11
11
|
"weibo",
|
|
12
12
|
];
|
|
13
13
|
/**
|
|
14
|
-
* The
|
|
14
|
+
* The networks `search_spoken_mentions` can reach on the caption-track route.
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
16
|
+
* These three are the ones whose post detail already carries a caption track,
|
|
17
|
+
* so a transcript is one synchronous read at 1 credit —
|
|
18
|
+
* `TRANSCRIPT_CAPTION_PLATFORMS` in nooticr-server, mirrored here.
|
|
19
|
+
*
|
|
20
|
+
* This list is the *fast path*, not the boundary of what could be searched:
|
|
21
|
+
* `get_post_transcript`'s `_` arm transcribes any other platform's audio with
|
|
22
|
+
* whisper, behind the same tool at the same price. It stays a fast path rather
|
|
23
|
+
* than the whole story for two reasons that are about the deployment, not the
|
|
24
|
+
* network — speech-to-text is gated on a `WHISPER_MODEL` no environment sets
|
|
25
|
+
* yet, and the listening path byte-downloads the media, so an HLS-only
|
|
26
|
+
* platform (Reddit) and one that carries no media URL at all (Bilibili) cannot
|
|
27
|
+
* be reached even once it is configured. Widening past these three waits on
|
|
28
|
+
* nooticr-server#39 and #42; the earlier wording here claimed instead that
|
|
29
|
+
* listening was "a different tool at a different price", which was wrong on
|
|
30
|
+
* both halves and is why Douyin sat unreachable behind a two-network limit
|
|
31
|
+
* despite reading captions exactly like the other two.
|
|
32
|
+
*
|
|
33
|
+
* Named here rather than inline because three places have to agree on it — the
|
|
34
|
+
* tool's own enum, the filter it puts every requested platform through, and the
|
|
35
|
+
* wait that prices the sweep. A platform the filter drops but the price counts
|
|
36
|
+
* is a credit quoted for a call that never happens.
|
|
23
37
|
*/
|
|
24
|
-
export const SPOKEN_PLATFORMS = ["tiktok", "youtube"];
|
|
38
|
+
export const SPOKEN_PLATFORMS = ["tiktok", "youtube", "douyin"];
|
|
25
39
|
/** Two per network, except the one that costs an order of magnitude more upstream. */
|
|
26
40
|
export const CREDITS_PER_NETWORK = 2;
|
|
27
41
|
export const XIAOHONGSHU_CREDITS = 5;
|
package/dist/shared/tools-def.js
CHANGED
|
@@ -3,7 +3,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
3
3
|
{
|
|
4
4
|
name: "analyze_post",
|
|
5
5
|
title: "Analyze Post",
|
|
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 and
|
|
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
|
},
|
|
9
9
|
{
|
|
@@ -57,7 +57,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
57
57
|
{
|
|
58
58
|
name: "understand_social_post",
|
|
59
59
|
title: "Understand Social Post",
|
|
60
|
-
description: "The same frames and transcript analyze_post returns, asked a different question: describe what physically happens on screen, in order, with every observation anchored to a frame. Supports TikTok, Instagram, YouTube, X, Reddit, Douyin, Xiaohongshu, Weibo and
|
|
60
|
+
description: "The same frames and transcript analyze_post returns, asked a different question: describe what physically happens on screen, in order, with every observation anchored to a frame. Supports TikTok, Instagram, YouTube, X, Reddit, Douyin, Xiaohongshu, Weibo, Bilibili and LinkedIn. Use when you need the events rather than the strategy; analyze_post puts the strategic question to the same material. Fans out to get_post_frames and get_post_transcript: 3 nooticr credits, 2 for the frames plus 1 for the transcript.",
|
|
61
61
|
inputSchema: z.object({ url: z.string().describe("Full public post URL (TikTok/Instagram/YouTube/X/Reddit/Douyin/Xiaohongshu/Weibo/Bilibili/LinkedIn)."), focus: z.string().optional().describe("Extra instruction, e.g. 'focus on the CTA'.") }).strict(),
|
|
62
62
|
},
|
|
63
63
|
{
|
|
@@ -369,7 +369,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
369
369
|
{
|
|
370
370
|
name: "search_spoken_mentions",
|
|
371
371
|
title: "Search Spoken Mentions",
|
|
372
|
-
description: "Brand mentions people SAY but never type. search_mentions reads captions, post bodies and comments — text — so a video that names a brand only out loud is invisible to it. This reads the words actually spoken, from the platform's own caption track, and searches them for a term. Narrows to candidate posts first (a niche sweep, named creator handles, and/or your watchlist), transcribes only the most-viewed survivors up to a hard ceiling you set, and returns the matched line with surrounding context. TikTok and
|
|
372
|
+
description: "Brand mentions people SAY but never type. search_mentions reads captions, post bodies and comments — text — so a video that names a brand only out loud is invisible to it. This reads the words actually spoken, from the platform's own caption track, and searches them for a term. Narrows to candidate posts first (a niche sweep, named creator handles, and/or your watchlist), transcribes only the most-viewed survivors up to a hard ceiling you set, and returns the matched line with surrounding context. TikTok, YouTube and Douyin — the networks whose post detail already carries a caption track; every other network's audio can be transcribed at the same price but not yet from here, and even on these three a video whose creator disabled captions is invisible to this tool; the result says how many candidates were found, transcribed and matched. Use when a term might be spoken on camera but not written anywhere. Consumes 2 nooticr credits per platform a niche is searched on, 2 per creator handle checked, and 1 per transcript actually fetched, capped by maxTranscripts.",
|
|
373
373
|
inputSchema: z.object({ term: z.string().describe("Brand, product or person to listen for, e.g. 'nooticr'."), platforms: z.array(z.enum(["tiktok", "youtube"])).optional().describe("Which networks to check (default: both)."), niche: z.string().optional().describe("Niche or keyword for a candidate sweep. Provide this, usernames, useWatchlist, or any combination."), usernames: z.array(z.string()).optional().describe("Specific creator handles to check."), useWatchlist: z.boolean().optional().describe("Also check your watchlisted creators on tiktok or youtube."), candidateLimit: z.number().int().optional().describe("Posts pulled per narrowing call (default 15, max 25)."), maxTranscripts: z.number().int().optional().describe("Hard ceiling on transcripts fetched (default 8, max 20).") }).strict(),
|
|
374
374
|
},
|
|
375
375
|
{
|
package/dist/shared/tools.js
CHANGED
|
@@ -24,7 +24,7 @@ import { loadingPlansJson } from "./loading-plans.js";
|
|
|
24
24
|
import { registerHandoff } from "./handoff.js";
|
|
25
25
|
import { registerCollabTools } from "./collab.js";
|
|
26
26
|
/** Current MCP server version — bumped on every deploy for traceability. */
|
|
27
|
-
export const MCP_SERVER_VERSION = "1.26.
|
|
27
|
+
export const MCP_SERVER_VERSION = "1.26.23";
|
|
28
28
|
/** MCP Apps extension identifier */
|
|
29
29
|
const UI_EXTENSION = "io.modelcontextprotocol/ui";
|
|
30
30
|
/** MIME type for MCP Apps HTML resources */
|
|
@@ -737,7 +737,7 @@ export function createMcpServer(rawMakeClient, opts) {
|
|
|
737
737
|
"lands and who it is aimed at, citing the frame or line behind each claim. " +
|
|
738
738
|
"It fans out to two fetches and you pay for both. " +
|
|
739
739
|
`${costSentence("analyze_post")} Each frame costs roughly 1,200 tokens of your context. ` +
|
|
740
|
-
"Supports TikTok, Instagram, YouTube, X, Reddit, Douyin, Xiaohongshu, Weibo and
|
|
740
|
+
"Supports TikTok, Instagram, YouTube, X, Reddit, Douyin, Xiaohongshu, Weibo, Bilibili and LinkedIn. " +
|
|
741
741
|
"Use when the visuals are the point; analyze_post_fast reads the same post without the frames for one credit less.",
|
|
742
742
|
_meta: {
|
|
743
743
|
ui: { resourceUri: uiResource("analyze_post") },
|
|
@@ -896,7 +896,7 @@ export function createMcpServer(rawMakeClient, opts) {
|
|
|
896
896
|
.enum(["tiktok", "instagram", "youtube", "douyin", "xiaohongshu", "twitter", "bilibili", "linkedin", "reddit", "weibo"])
|
|
897
897
|
.optional()
|
|
898
898
|
.describe("Which platform (default tiktok)."),
|
|
899
|
-
limit: z.number().int().optional().describe("Posts to fetch (default
|
|
899
|
+
limit: z.number().int().optional().describe("Posts to fetch (default 12, max 30)."),
|
|
900
900
|
focus: z.string().optional().describe("Extra instruction for the profile synthesis."),
|
|
901
901
|
})
|
|
902
902
|
.strict(),
|
|
@@ -1775,7 +1775,7 @@ export function createMcpServer(rawMakeClient, opts) {
|
|
|
1775
1775
|
.object({
|
|
1776
1776
|
niche: z.string().describe("Niche or topic, e.g. 'home fitness'."),
|
|
1777
1777
|
platform: z.string().optional().describe("Platform to survey (default tiktok)."),
|
|
1778
|
-
count: z.number().int().optional().describe("Posts to survey (default
|
|
1778
|
+
count: z.number().int().optional().describe("Posts to survey (default 12, max 40)."),
|
|
1779
1779
|
})
|
|
1780
1780
|
.strict(),
|
|
1781
1781
|
}, async (args, extra) => {
|
|
@@ -1807,7 +1807,7 @@ export function createMcpServer(rawMakeClient, opts) {
|
|
|
1807
1807
|
.object({
|
|
1808
1808
|
username: z.string().describe("Creator handle, with or without @."),
|
|
1809
1809
|
platform: z.string().optional().describe("Platform (default tiktok)."),
|
|
1810
|
-
limit: z.number().int().optional().describe("Posts to read (default
|
|
1810
|
+
limit: z.number().int().optional().describe("Posts to read (default 12, max 40)."),
|
|
1811
1811
|
})
|
|
1812
1812
|
.strict(),
|
|
1813
1813
|
}, async (args, extra) => {
|
|
@@ -2011,7 +2011,7 @@ export function createMcpServer(rawMakeClient, opts) {
|
|
|
2011
2011
|
"what physically happens on screen, in order, with every observation anchored to a frame. " +
|
|
2012
2012
|
"It fans out to two fetches and you pay for both. " +
|
|
2013
2013
|
`${costSentence("understand_social_post")} Each frame costs roughly 1,200 tokens of your context. ` +
|
|
2014
|
-
"Supports TikTok, Instagram, YouTube, X, Reddit, Douyin, Xiaohongshu, Weibo and
|
|
2014
|
+
"Supports TikTok, Instagram, YouTube, X, Reddit, Douyin, Xiaohongshu, Weibo, Bilibili and LinkedIn. " +
|
|
2015
2015
|
"Use when you need the events rather than the strategy; analyze_post puts the strategic question to the same material.",
|
|
2016
2016
|
_meta: {
|
|
2017
2017
|
ui: { resourceUri: uiResource("understand_social_post") },
|
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.
|
|
4
|
+
"version": "1.26.23",
|
|
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
|
"type": "module",
|
|
7
7
|
"main": "dist/index.js",
|