@nooticr/mcp 1.26.19
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/LICENSE +21 -0
- package/README.md +521 -0
- package/dist/auth.js +170 -0
- package/dist/config.js +54 -0
- package/dist/idempotency.js +32 -0
- package/dist/index.js +417 -0
- package/dist/nooticr.js +6 -0
- package/dist/oauth.js +263 -0
- package/dist/shared/brand-watch.js +297 -0
- package/dist/shared/cimd.js +231 -0
- package/dist/shared/collab.js +447 -0
- package/dist/shared/comment-review.js +171 -0
- package/dist/shared/connections.js +80 -0
- package/dist/shared/evidence.js +410 -0
- package/dist/shared/handoff.js +526 -0
- package/dist/shared/jobs.js +1785 -0
- package/dist/shared/nooticr.js +249 -0
- package/dist/shared/oauth.js +132 -0
- package/dist/shared/output-schemas.js +1046 -0
- package/dist/shared/own-account.js +559 -0
- package/dist/shared/performance.js +156 -0
- package/dist/shared/prompts.js +383 -0
- package/dist/shared/spend.js +158 -0
- package/dist/shared/tasks.js +88 -0
- package/dist/shared/tools-def.js +387 -0
- package/dist/shared/tools.js +2027 -0
- package/dist/shared/ui-template.js +2856 -0
- package/dist/shared/video.js +152 -0
- package/dist/shared/view-meta.js +34 -0
- package/dist/shared/watchlist.js +603 -0
- package/dist/video.js +6 -0
- package/package.json +62 -0
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const TOOL_DEFINITIONS = [
|
|
3
|
+
{
|
|
4
|
+
name: "analyze_post",
|
|
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 Bilibili. 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
|
+
inputSchema: z.object({ url: z.string().describe("Public post URL (TikTok/Instagram/YouTube/X, Reddit, Douyin, Xiaohongshu, Weibo or Bilibili).") }).strict(),
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
name: "get_social_media",
|
|
11
|
+
title: "Get Social Media",
|
|
12
|
+
description: "Fetch a social post's media from a TikTok, Instagram, YouTube, X, Reddit, Douyin, Xiaohongshu, Weibo or Bilibili URL: contentType (video/image/carousel/slideshow), title, caption, author, stats and direct media URLs. Returns an inline thumbnail image. (20 free credits for new users). Use when you need the post's facts and media and nothing more; if you want it interpreted, use analyze_post_fast instead. Consumes 1 nooticr credit.",
|
|
13
|
+
inputSchema: z.object({ url: z.string().describe("Full public post URL.") }).strict(),
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
name: "discover_social_posts",
|
|
17
|
+
title: "Discover Social Posts",
|
|
18
|
+
description: "Discover recent posts (video, image, carousel, slideshow) for a niche on YouTube, TikTok, Instagram, Reddit, Douyin, Xiaohongshu, X, Weibo or Bilibili. Reddit and Weibo are mostly text rather than video, so a post from either may have no videoUrl and no duration. Each post includes title/caption, thumbnailUrl, externalUrl, views/likes/comments and inline thumbnails (up to 4) so they show in chat. Say \"next\" to paginate (offset), or \"analyze the 2nd one\" / \"analyze all\" for batch analysis. (20 free credits for new users). Use to find individual posts to look at; use niche_report when you want the pattern across them rather than the posts themselves. Consumes 2 nooticr credits.",
|
|
19
|
+
inputSchema: z.object({ niche: z.string().describe("Niche/topic, e.g. 'fitness'."), keywords: z.string().optional().describe("Optional extra keywords."), limit: z.number().int().optional().describe("Max results (default 6)."), offset: z.number().int().optional().describe("Skip first N results — for 'next' pagination."), platform: z.enum(["youtube", "tiktok", "instagram", "douyin", "xiaohongshu", "twitter", "bilibili", "reddit", "weibo", "any"]).optional().describe("Platform to search (default youtube).") }).strict(),
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: "get_user_posts",
|
|
23
|
+
title: "Get User Posts",
|
|
24
|
+
description: "List recent posts by a creator handle (e.g. @zoundsapp) on TikTok, Instagram, YouTube, Reddit, Douyin, Xiaohongshu, X, Weibo, Bilibili or LinkedIn. Each post includes title/caption, thumbnailUrl, externalUrl, views/likes/comments and inline thumbnails (up to 4) so they show in chat. Use this when Claude needs to pull more posts from the same account to spot a pattern, or to scan a whole profile. (20 free credits for new users). Use to scan one creator's output; use find_hook_pattern when you want their formula extracted rather than the raw list. Consumes 2 nooticr credits.",
|
|
25
|
+
inputSchema: z.object({ username: z.string().describe("Creator handle, e.g. 'zoundsapp' or '@zoundsapp'."), platform: z.enum(["tiktok", "instagram", "youtube", "douyin", "xiaohongshu", "twitter", "bilibili", "reddit", "weibo"]).optional().describe("Which platform (default tiktok)."), limit: z.number().int().optional().describe("Max posts (default 6).") }).strict(),
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: "analyze_creator_profile",
|
|
29
|
+
title: "Analyze Creator Profile",
|
|
30
|
+
description: "A creator's recent posts with their stats, on TikTok, Instagram, YouTube, Reddit, Douyin, Xiaohongshu, X, Weibo, Bilibili or LinkedIn — the raw material of a profile teardown, for you to read. Work out their niche, content themes, hook formula, strengths and weaknesses, engagement pattern and audience from the spread of the numbers rather than the best post, and name the posts you reason from. find_hook_pattern fetches the same posts and asks only for the formula. Costs 2 nooticr credits, for the one get_user_posts call it makes.",
|
|
31
|
+
inputSchema: z.object({ username: z.string().describe("Creator handle, e.g. 'zoundsapp'."), platform: z.enum(["tiktok", "instagram", "youtube", "douyin", "xiaohongshu", "twitter", "bilibili", "reddit", "weibo"]).optional().describe("Which platform (default tiktok)."), limit: z.number().int().optional().describe("Posts to fetch (default 6; first 3 analyzed)."), focus: z.string().optional().describe("Extra instruction for the profile synthesis.") }).strict(),
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: "get_post_comments",
|
|
35
|
+
title: "Get Post Comments",
|
|
36
|
+
description: "Fetch top comments for a post URL on TikTok, Instagram, YouTube, Reddit, Douyin, X, Weibo, Bilibili or LinkedIn (plus keyword clusters from TikTok Analytics when available) — audience sentiment/audience-signal analysis. (20 free credits for new users). Use when you want to read what people actually wrote; use analyze_comments when you want it synthesised into what to do next. Consumes 2 nooticr credits.",
|
|
37
|
+
inputSchema: z.object({ url: z.string().describe("Full public post URL (TikTok/Instagram/YouTube/Douyin/X/Bilibili)."), limit: z.number().int().optional().describe("Max comments (default 20).") }).strict(),
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: "search_creators",
|
|
41
|
+
title: "Search Creators",
|
|
42
|
+
description: "Search creators by niche/keyword on TikTok, Instagram, Xiaohongshu, YouTube or Douyin — username, nickname, follower count, signature, verified status. Use to find influencers to vet or analyze. (20 free credits for new users). Use when you know the niche but not the names; use get_similar_creators when you already have one creator that works. Consumes 2 nooticr credits.",
|
|
43
|
+
inputSchema: z.object({ keyword: z.string().describe("Niche/keyword, e.g. 'fitness' or a creator name."), platform: z.enum(["tiktok", "instagram", "xiaohongshu", "youtube", "douyin"]).optional().describe("Which platform (default tiktok)."), count: z.number().int().optional().describe("Max creators (default 8).") }).strict(),
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: "get_similar_creators",
|
|
47
|
+
title: "Get Similar Creators",
|
|
48
|
+
description: "Find lookalike creators for a given handle — TikTok similar-user recommendations or Instagram similar users. Useful for scaling: 'if this creator works, here are more like them'. (20 free credits for new users). Use when one creator already fits and you want more of the same. Consumes 2 nooticr credits.",
|
|
49
|
+
inputSchema: z.object({ username: z.string().describe("Seed creator handle, e.g. 'zoundsapp'."), platform: z.enum(["tiktok", "instagram"]).optional().describe("Which platform (default tiktok).") }).strict(),
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: "discover_sounds",
|
|
53
|
+
title: "Discover Sounds",
|
|
54
|
+
description: "Discover trending sounds/music for a keyword on TikTok or Instagram — the sound is a huge ranking signal for TikTok virality. Returns title, artist, duration, play/cover URLs. (20 free credits for new users). Use when picking audio for a post, or to spot a sound before it peaks. Consumes 2 nooticr credits.",
|
|
55
|
+
inputSchema: z.object({ keyword: z.string().describe("Niche/keyword, e.g. 'gym'."), platform: z.enum(["tiktok", "instagram"]).optional().describe("Which platform (default tiktok)."), count: z.number().int().optional().describe("Max sounds (default 6).") }).strict(),
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: "understand_social_post",
|
|
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 Bilibili. 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
|
+
inputSchema: z.object({ url: z.string().describe("Full public post URL (TikTok/Instagram/YouTube/X/Douyin/Xiaohongshu/Bilibili)."), focus: z.string().optional().describe("Extra instruction, e.g. 'focus on the CTA'.") }).strict(),
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: "get_post_transcript",
|
|
65
|
+
title: "Get Post Transcript",
|
|
66
|
+
description: "Get the words actually spoken in a TikTok or YouTube post by reading its caption track. Cheap and exact — use this before analyze_post when you need the script, hook wording or CTA verbatim rather than an interpretation. Returns plain text with a word count, or available:false with a reason when the post has no captions. Use before any analysis when the exact wording matters. Consumes 1 nooticr credit.",
|
|
67
|
+
inputSchema: z.object({ url: z.string().describe("Post URL (TikTok or YouTube)."), language: z.string().optional().describe("Preferred language code, e.g. 'en'.") }).strict(),
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: "analyze_comments",
|
|
71
|
+
title: "Analyze Comments",
|
|
72
|
+
description: "A post's comment section, fetched and laid out for you to classify: every comment with a stable id, plus whatever themes the platform clustered them into. Label each one's sentiment and what it is doing — praise, complaint, bug report, question, request, comparison, spam — then summarise the themes, the questions worth answering, the objections, and what to make next. The result names the exact labels, and show_comment_review draws them for free afterwards. Use when the goal is 'what should I make next' rather than 'what did people write'. Costs 2 nooticr credits, for the one get_post_comments call it makes.",
|
|
73
|
+
inputSchema: z.object({ url: z.string().describe("Full public post URL."), limit: z.number().int().optional().describe("Comments to read (default 50, max 100).") }).strict(),
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: "compare_posts",
|
|
77
|
+
title: "Compare Posts",
|
|
78
|
+
description: "The first of 2-5 posts you want compared, fetched with its stats — and the comparison left to you. Call get_social_media on each remaining URL yourself (1 credit each), and get_post_transcript where the wording matters, then say which won, what actually differed (hook, format, length, caption, hashtags), what they share worth keeping, and the one experiment that would test your explanation. Use for 'why did this one work and that one not'. Costs 1 nooticr credit for this call, plus 1 per further post you fetch.",
|
|
79
|
+
inputSchema: z.object({ urls: z.array(z.string()).describe("2-5 post URLs to compare.") }).strict(),
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: "discover_hashtags",
|
|
83
|
+
title: "Discover Hashtags",
|
|
84
|
+
description: "Trending TikTok hashtags from the Creative Center trend board, with post counts, view counts and whether each is rising, cooling or steady. Filter by country and time window. Use to find what to tag, or to spot a wave early. Consumes 2 nooticr credits.",
|
|
85
|
+
inputSchema: z.object({ country: z.string().optional().describe("2-letter country code (default US)."), days: z.number().int().optional().describe("Window in days: 7, 30 or 120 (default 7)."), count: z.number().int().optional().describe("Max hashtags (default 20)."), industryId: z.string().optional().describe("Optional TikTok industry id to filter by.") }).strict(),
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
name: "analyze_post_fast",
|
|
89
|
+
title: "Analyze Post (Fast)",
|
|
90
|
+
description: "A post's transcript, caption and stats, with no frames — which is what makes it the cheap read. Work out the hook, the script structure, the CTA and the audience from the words and the numbers yourself, and say plainly that you have not seen the visuals. Use this by default; call analyze_post when a judgement actually needs the frames. Fans out to get_social_media and get_post_transcript: 2 nooticr credits, 1 each.",
|
|
91
|
+
inputSchema: z.object({ url: z.string().describe("Full public post URL.") }).strict(),
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: "write_hooks",
|
|
95
|
+
title: "Write Hooks",
|
|
96
|
+
description: "The source post, its transcript and its stats, so you can write the opening lines yourself — the first line said or shown on screen. Name the device each hook uses and who it stops; one that could open any video in the niche is not grounded in this one. Use when you know the subject and need openings to choose between. With a url it fetches get_social_media and get_post_transcript for 2 nooticr credits; with only a topic there is nothing to fetch and it costs nothing.",
|
|
97
|
+
inputSchema: z.object({ url: z.string().optional().describe("Post to riff on (optional if topic given)."), topic: z.string().optional().describe("Subject to write hooks about (optional if url given)."), count: z.number().int().optional().describe("How many hooks (default 10, max 20)."), tone: z.string().optional().describe("Optional tone, e.g. 'blunt', 'contrarian'.") }).strict(),
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
name: "create_variants",
|
|
101
|
+
title: "Create Variants",
|
|
102
|
+
description: "The post that worked, with its transcript and stats, so you can propose what to film next — same underlying mechanism, different execution. For each variant give the hook, the one angle that changes, the shot beats in order and the CTA, and say what made the original work. Use after reading a post to move from 'why it worked' to 'what to make'. Fans out to get_social_media and get_post_transcript: 2 nooticr credits, 1 each.",
|
|
103
|
+
inputSchema: z.object({ url: z.string().describe("The post to make variants of."), count: z.number().int().optional().describe("How many variants (default 3, max 6)."), angle: z.string().optional().describe("Optional steer, e.g. 'for a beginner audience'.") }).strict(),
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
name: "score_draft",
|
|
107
|
+
title: "Score Draft",
|
|
108
|
+
description: "Score your own draft BEFORE you film or post it. Returns the draft with the rubric to hold it to — hook, clarity, payoff, specificity and fit, each scored 1-10 — and asks you for the three fixes that would move it most, one rewritten opening line and a tightened version. The only tool that runs before the content exists. Use before filming, while changing it is still cheap. Free: it fetches nothing, so it costs no credits.",
|
|
109
|
+
inputSchema: z.object({ draft: z.string().describe("Your script, caption or hook."), platform: z.string().optional().describe("Target platform (default tiktok).") }).strict(),
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
name: "repurpose_post",
|
|
113
|
+
title: "Repurpose Post",
|
|
114
|
+
description: "The source post, its transcript and its stats, for you to rewrite for other surfaces — X thread, LinkedIn post, carousel slides, YouTube title/description, newsletter — keeping the argument and changing the shape to suit how each is read. The same paragraph with different line breaks is not a repurposing. Use when a post already worked and you want it on other surfaces. Fans out to get_social_media and get_post_transcript: 2 nooticr credits, 1 each.",
|
|
115
|
+
inputSchema: z.object({ url: z.string().describe("The post to repurpose."), targets: z.array(z.string()).optional().describe("Which formats to produce (default all).") }).strict(),
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
name: "niche_report",
|
|
119
|
+
title: "Niche Report",
|
|
120
|
+
description: "Recent posts in a niche with their stats, so you can read what is working right now: dominant formats, recurring hook patterns, what over- and underperforms, and the gaps nobody is filling. Captions and performance, no video, which is what keeps it cheap — and only name a gap whose absence is visible in the set you were handed. Use when entering a niche or deciding what to make next, rather than judging one post. Costs 2 nooticr credits, for the one discover_social_posts call it makes.",
|
|
121
|
+
inputSchema: z.object({ niche: z.string().describe("Niche or topic, e.g. 'home fitness'."), platform: z.string().optional().describe("Platform to survey (default tiktok)."), count: z.number().int().optional().describe("Posts to survey (default 20, max 40).") }).strict(),
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
name: "find_hook_pattern",
|
|
125
|
+
title: "Find Hook Pattern",
|
|
126
|
+
description: "A creator's recent posts, fetched so their opening lines can be read as a set. Extract the repeatable formula yourself: the hook types they reuse, caption style, length pattern, and fill-in-the-blank templates another creator could adapt — each one saying how many posts it is drawn from, because a template that fits one post is not a pattern. Use to reverse-engineer a creator you want to learn from. Costs 2 nooticr credits, for the one get_user_posts call it makes.",
|
|
127
|
+
inputSchema: z.object({ username: z.string().describe("Creator handle, with or without @."), platform: z.string().optional().describe("Platform (default tiktok)."), limit: z.number().int().optional().describe("Posts to read (default 20, max 40).") }).strict(),
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: "search_mentions",
|
|
131
|
+
title: "Search Mentions",
|
|
132
|
+
description: "Brand monitoring: what people are actually saying about a term across every network at once. Searches TikTok, Instagram, YouTube, X, Reddit, Weibo, Douyin, Xiaohongshu and Bilibili in parallel, opens the posts it finds and reads their COMMENTS for the term \u2014 the mention is usually in the replies, not the caption. Returns the comments grouped under the post they were left on, each with an id you can pass to another tool, how many times it names the term, and whether the post itself is about the brand or merely where the audience raised it. Use `since` to monitor a past window and `offset` to page through. Costs 2 nooticr credits per platform searched, except Xiaohongshu at 5. Use to see what is said about a brand; discover_social_posts is for one platform's posts.",
|
|
133
|
+
inputSchema: z.object({ term: z.string().describe("Brand, product or person to look for, e.g. 'nooticr'."), platforms: z.array(z.enum(["youtube", "tiktok", "instagram", "douyin", "xiaohongshu", "twitter", "bilibili", "reddit", "weibo"])).optional().describe("Which networks to search (default: all). Fewer platforms costs less."), since: z.string().optional().describe("Only comments posted on or after this date, as YYYY-MM-DD. Omit for no window."), limit: z.number().int().optional().describe("Posts to open per platform (default 5, max 20). Each one is a comment fetch."), commentsPerPost: z.number().int().optional().describe("Comments to read per post (default 30, max 100)."), offset: z.number().int().optional().describe("Skip this many groups — pass nextOffset from the previous call to load more."), pageSize: z.number().int().optional().describe("Groups returned per call (default 6, max 30).") }).strict(),
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
name: "get_post_frames",
|
|
137
|
+
title: "Get Post Frames",
|
|
138
|
+
description: "Frames from a post's video, returned as images you can look at yourself rather than an analysis of them. They are chosen by scene change rather than by the clock: the video is decoded through and a frame kept whenever the picture actually changed, so every distinct shot is represented, where evenly spaced frames can all land inside one long take and miss a cutaway entirely. The result says how many shots were found, how many frames came back and whether the cap left any out, so you never have to guess what you have seen. A carousel or slideshow returns its own images unchanged. Pair it with get_post_transcript to have both what is shown and what is said, and judge them yourself. Each frame costs you roughly 1,200 tokens of context. Consumes 2 nooticr credits. Use when the frames are all you want; analyze_post pairs them with the transcript for 1 credit more.",
|
|
139
|
+
inputSchema: z.object({ url: z.string().describe("Public post URL."), count: z.number().int().optional().describe("The most frames to return (max 24). Omit it and scene mode returns one frame per shot."), mode: z.enum(["auto", "scene", "even"]).optional().describe("auto (default) picks per video; scene returns one frame per shot; even keeps the old fixed-interval sampling.") }).strict(),
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
name: "prepare_handoff",
|
|
143
|
+
title: "Prepare Handoff",
|
|
144
|
+
description: "Turn items you classified — a bug report in a comment, a complaint said out loud in a video, a feature request under a competitor's post — into the exact text to file in GitHub, Jira or Linear through whichever tracker server this host also has connected. This server files nothing itself and holds no tracker credential; it returns the strings and you make the call. Free, and makes no requests. For each item you get a title, a ready body with the quote fenced and framed as third-party evidence rather than as instructions, tracker-safe labels, and a searchFirst string to look for before filing so the same report does not become five issues. File the body unmodified.",
|
|
145
|
+
inputSchema: z.object({ destination: z.enum(["github", "jira", "linear", "generic"]).optional().describe("Where these are going. Default generic."), product: z.string().optional().describe("What the reports are about, for the title prefix."), items: z.array(z.object({ sourceId: z.string(), sourceUrl: z.string().optional(), kind: z.string(), title: z.string(), summary: z.string().optional(), quote: z.string().optional(), author: z.string().optional(), platform: z.string().optional(), occurredAt: z.string().optional(), severity: z.string().optional(), confidence: z.string().optional() })).min(1).max(50).describe("The items to prepare, one per report.") }).strict(),
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
name: "show_collab_shortlist",
|
|
149
|
+
title: "Show Collab Shortlist",
|
|
150
|
+
description: "Display the creators you scored after vetting them, ranked, so the user can pick who to approach. Free, and makes no requests — it only draws what you pass it, and the scores shown are attributed to you rather than presented as a nooticr rating. Use after who_should_i_work_with and after actually reading some of each candidate's links: pass the score, the reason, and what you read to reach it. A candidate you did not verify should say so in checked rather than carrying a confident number.",
|
|
151
|
+
inputSchema: z.object({ niche: z.string(), platform: z.string().optional(), summary: z.string().optional(), candidates: z.array(z.object({ id: z.string(), username: z.string(), nickname: z.string().optional(), platform: z.string().optional(), followers: z.number().optional(), verified: z.boolean().optional(), avatarUrl: z.string().optional(), signature: z.string().optional(), score: z.number().min(0).max(100).optional(), verdict: z.enum(["approach", "maybe", "pass"]).optional(), why: z.string().optional(), checked: z.array(z.string()).optional(), concerns: z.array(z.string()).optional() })).min(1).max(50), recommended: z.string().optional(), question: z.string().optional() }).strict(),
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
name: "show_comment_review",
|
|
155
|
+
title: "Show Comment Review",
|
|
156
|
+
description: "Display comment classifications you produced from analyze_comments. Free, and makes no requests — it only draws what you pass it. Renders each comment with its sentiment and category so a person can sort and act on them. Call this after you have classified the comments, not instead of classifying them.",
|
|
157
|
+
inputSchema: z.object({ url: z.string().describe("The post the comments came from."), summary: z.string().optional(), title: z.string().optional(), comments: z.array(z.object({ id: z.string(), text: z.string(), author: z.string().optional(), likes: z.number().optional(), sentiment: z.string().optional(), category: z.string().optional(), note: z.string().optional() })), themes: z.array(z.string()).optional(), nextSteps: z.array(z.string()).optional() }).strict(),
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
name: "show_comparison",
|
|
161
|
+
title: "Show Comparison",
|
|
162
|
+
description: "Display a comparison you wrote after compare_posts fetched the first post and you fetched the rest yourself. Free, and makes no requests — it only draws what you pass it: each post with a BEST badge on the winner, what differed, shared strengths and the next experiment worth running. Call this after you have done the comparing, not instead of it.",
|
|
163
|
+
inputSchema: z.object({ posts: z.array(z.record(z.unknown())).min(2).max(5), winner: z.number().int(), winnerReason: z.string().optional(), differences: z.array(z.object({ factor: z.string(), detail: z.string() })).optional(), lessons: z.array(z.string()).optional(), nextTest: z.string().optional() }).strict(),
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
name: "show_analysis",
|
|
167
|
+
title: "Show Analysis",
|
|
168
|
+
description: "Display an analysis you wrote after analyze_post, analyze_post_fast or understand_social_post handed you the material. Free, and makes no requests — it only draws what you pass it: hook strength, script structure, quotable lines, hashtags, target audience, viral triggers and more, whichever of these you actually produced. Call this after you have done the analysing, not instead of it.",
|
|
169
|
+
inputSchema: z.object({ url: z.string(), post: z.record(z.unknown()).optional(), analysis: z.record(z.unknown()) }).strict(),
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
name: "show_hooks",
|
|
173
|
+
title: "Show Hooks",
|
|
174
|
+
description: "Display the alternative opening hooks you wrote after write_hooks handed you a post's material (or just a topic). Free, and makes no requests — it only draws what you pass it: each hook with the device it uses and who it stops. Call this after you have written the hooks, not instead of writing them.",
|
|
175
|
+
inputSchema: z.object({ url: z.string().optional(), topic: z.string().optional(), hooks: z.array(z.object({ hook: z.string(), mechanism: z.string().optional(), why: z.string().optional() })).min(1) }).strict(),
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
name: "show_variants",
|
|
179
|
+
title: "Show Variants",
|
|
180
|
+
description: "Display the post variants you wrote after create_variants handed you the original post's material. Free, and makes no requests — it only draws what you pass it: each variant's hook, the angle that changes, its shot beats and its call to action. Call this after you have written the variants, not instead of writing them.",
|
|
181
|
+
inputSchema: z.object({ sourceUrl: z.string(), post: z.record(z.unknown()).optional(), variants: z.array(z.object({ title: z.string(), hook: z.string(), angle: z.string().optional(), beats: z.array(z.string()).optional(), cta: z.string().optional(), whyItCouldWork: z.string().optional() })).min(1) }).strict(),
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
name: "show_repurposed_post",
|
|
185
|
+
title: "Show Repurposed Post",
|
|
186
|
+
description: "Display the rewritten copy you produced after repurpose_post handed you the source post's material. Free, and makes no requests — it only draws what you pass it: one entry per surface you rewrote it for. Call this after you have done the rewriting, not instead of it.",
|
|
187
|
+
inputSchema: z.object({ sourceUrl: z.string(), versions: z.array(z.object({ surface: z.string(), content: z.string() })).min(1) }).strict(),
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
name: "check_nooticr_credits",
|
|
191
|
+
title: "Check Nooticr Credits",
|
|
192
|
+
description: "Check your MCP credit balance, billing URL and pack size. New users get 20 free credits. No cost — call anytime to see remaining credits before running other tools. Use before a run of paid calls to confirm the balance covers it.",
|
|
193
|
+
inputSchema: z.object({}).strict(),
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
name: "buy_nooticr_credits",
|
|
197
|
+
title: "Buy Nooticr Credits",
|
|
198
|
+
description: "Buy an MCP credit pack via Stripe Checkout. Returns a secure checkout URL — open it in your browser to pay. Credits are added automatically after payment. Use when the balance is short and the user has agreed to top up. No cost to call.",
|
|
199
|
+
inputSchema: z.object({}).strict(),
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
name: "nooticr_login",
|
|
203
|
+
title: "Nooticr Login",
|
|
204
|
+
description: "Get a fresh login URL to re-authenticate your MCP session. Call this tool when you need to reconnect or when the session has expired. Use when a call fails with an authentication error, to re-link the account. Free to call. No cost to call.",
|
|
205
|
+
inputSchema: z.object({}).strict(),
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
name: "watch_creator",
|
|
209
|
+
title: "Watch Creator",
|
|
210
|
+
description: "Add a creator to your watchlist so you can ask later what they have posted since. Stores the handle only \u2014 nothing is fetched here. Use catch_up_watchlist to see what changed. Use when you want to follow someone over time rather than look at them once. No cost to call.",
|
|
211
|
+
inputSchema: z.object({ username: z.string().describe("Creator handle, with or without @."), platform: z.string().optional().describe("Platform (default tiktok)."), note: z.string().optional().describe("Why you are watching them \u2014 shown back to you later.") }).strict(),
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
name: "unwatch_creator",
|
|
215
|
+
title: "Unwatch Creator",
|
|
216
|
+
description: "Remove a creator from your watchlist. Nothing is fetched. Use when you no longer want them in the catch-up. No cost to call.",
|
|
217
|
+
inputSchema: z.object({ username: z.string().describe("Creator handle, with or without @."), platform: z.string().optional().describe("Platform (default tiktok).") }).strict(),
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
name: "catch_up_watchlist",
|
|
221
|
+
title: "Catch Up On Watchlist",
|
|
222
|
+
description: "What the creators you watch have posted since you last checked. Fetches each one's recent posts and compares them against the snapshot taken at your last catch-up, then moves the snapshot forward \u2014 so it answers 'what is new' rather than 'what exists'. The first run for a creator records the baseline and has nothing to compare against. Use to follow a set of creators over time instead of re-reading each one. Consumes 2 nooticr credits per creator checked.",
|
|
223
|
+
inputSchema: z.object({ limit: z.number().int().optional().describe("Posts to check per creator (default 6)."), platform: z.string().optional().describe("Only check creators on this platform.") }).strict(),
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
name: "create_brand_watch",
|
|
227
|
+
title: "Create Brand Watch",
|
|
228
|
+
description: "Run a sweep on a schedule and email what is new, instead of remembering to ask. kind: \"mentions\" (default) sweeps term across platforms; kind: \"competitor\" sweeps one creator's own posts (handle + platform) and reports only what beats their own recent median. Two calls by design: call it once with no confirmation to get back the cost per run and what it multiplies out to per day, then call it again with confirm: true and the confirmationToken to actually start it. The first call creates nothing and spends nothing. Bills 2 credits per network per run (5 for Xiaohongshu) for a mentions watch, or a flat 2 credits for a competitor watch, trimmed to fit budgetCredits. A run that finds nothing new (or, for a competitor watch, nothing above median) sends no mail. No cost to call.",
|
|
229
|
+
inputSchema: z.object({ kind: z.enum(["mentions", "competitor"]).optional().describe("\"mentions\" (default) or \"competitor\"."), term: z.string().optional().describe("kind: mentions only — what to watch for (max 120 chars)."), platforms: z.array(z.string()).optional().describe("kind: mentions only — networks to sweep (default: every searchable network)."), handle: z.string().optional().describe("kind: competitor only — the creator's handle."), platform: z.string().optional().describe("kind: competitor only — the platform that creator posts on."), cadence: z.enum(["hourly", "every_6_hours", "every_12_hours", "daily", "weekly"]).optional().describe("How often to run (default daily)."), budgetCredits: z.number().int().optional().describe("Hard per-run credit ceiling (default the full sweep's cost)."), deliverTo: z.string().optional().describe("Digest email (default the account's own)."), confirm: z.boolean().optional().describe("Set true only once the user has agreed to the quoted cost."), confirmationToken: z.string().optional().describe("The token the first call returned.") }).strict(),
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
name: "list_brand_watches",
|
|
233
|
+
title: "List Brand Watches",
|
|
234
|
+
description: "Every scheduled brand-monitoring watch this user has: term, networks, cadence, cost per run, credits spent so far, runs made, next run due, and whether it is stopped and why. Read before creating a watch — a second watch on the same term is a second recurring charge for the same answer. No cost to call.",
|
|
235
|
+
inputSchema: z.object({}).strict(),
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
name: "stop_brand_watch",
|
|
239
|
+
title: "Stop Brand Watch",
|
|
240
|
+
description: "Stop a scheduled brand-monitoring watch, by watchId or by term. Takes effect immediately — the run that was due does not happen and nothing further is charged. Works even at a zero balance, since a user out of credits is exactly the user who needs to turn off what is spending them. No cost to call.",
|
|
241
|
+
inputSchema: z.object({ watchId: z.string().optional().describe("The watch's id, from list_brand_watches."), term: z.string().optional().describe("Alternative to watchId — the term it was watching.") }).strict(),
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
name: "list_own_apps",
|
|
245
|
+
title: "List Own Apps",
|
|
246
|
+
description: "List every product (\"app\") in your own workspace — id, name, niche and product type. Call this first when your workspace has more than one product and another own-account tool asks for appId. Reads only your own workspace. No cost to call.",
|
|
247
|
+
inputSchema: z.object({}).strict(),
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
name: "create_product",
|
|
251
|
+
title: "Create Product",
|
|
252
|
+
description: "Create a new product (\"app\") in your own workspace — the row every other own-account tool needs before it has anything to work with. Takes no workspace argument: it always creates in the calling session's own workspace. Subject to your plan's product limit. Does not generate a brand playbook by itself; call analyze_product afterwards for that. name and slug are required; description, website_url, niche, product_type and the store-listing fields are optional and snake_case. Free — no AI call, just a row.",
|
|
253
|
+
inputSchema: z.object({ name: z.string().describe("Product name."), slug: z.string().describe("URL-safe slug, unique within your workspace."), description: z.string().optional(), website_url: z.string().optional().describe("The product's own site; analyze_product later fetches an excerpt of it."), niche: z.string().optional(), product_type: z.string().optional(), icon_url: z.string().optional(), primary_cta_label: z.string().optional(), primary_cta_url: z.string().optional(), external_listing_id: z.string().optional(), ios_bundle_id: z.string().optional(), android_package: z.string().optional() }).strict(),
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
name: "update_product",
|
|
257
|
+
title: "Update Product",
|
|
258
|
+
description: "Patch your own product's fields — omitted arguments leave their column unchanged. Takes appId (optional with a single-app workspace); every other field is snake_case, same as create_product. Free — no AI call, just a row.",
|
|
259
|
+
inputSchema: z.object({ appId: z.number().int().optional().describe("Your product's id. Omit only with a single-app workspace."), name: z.string().optional(), slug: z.string().optional(), description: z.string().optional(), website_url: z.string().optional(), niche: z.string().optional(), product_type: z.string().optional(), icon_url: z.string().optional(), primary_cta_label: z.string().optional(), primary_cta_url: z.string().optional(), external_listing_id: z.string().optional(), ios_bundle_id: z.string().optional(), android_package: z.string().optional() }).strict(),
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
name: "get_brand_playbook",
|
|
263
|
+
title: "Get Brand Playbook",
|
|
264
|
+
description: "Your own product's brand playbook — name, description and the playbook text — if one has been configured, in the dashboard or by analyze_product. Read-only. Returns available: false when none exists yet. Takes appId (optional with a single-app workspace). No cost to call.",
|
|
265
|
+
inputSchema: z.object({ appId: z.number().int().optional().describe("Your product's id. Omit only with a single-app workspace.") }).strict(),
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
name: "analyze_product",
|
|
269
|
+
title: "Analyze Product",
|
|
270
|
+
description: "Start an AI analysis of your own product: fetches an excerpt of the product's own website (website_url, if set — a real outbound fetch), reads its recent posts and fleet performance, and writes the result as the product's brand playbook. Takes appId (optional with a single-app workspace). Runs in the background; returns a jobId immediately, poll it with analyze_product_status. Billed like the dashboard's own analyze job: 10 of your workspace's plan AI credits (first analysis free per workspace), not your personal MCP credits.",
|
|
271
|
+
inputSchema: z.object({ appId: z.number().int().optional().describe("Your product's id. Omit only with a single-app workspace.") }).strict(),
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
name: "analyze_product_status",
|
|
275
|
+
title: "Analyze Product Status",
|
|
276
|
+
description: "Poll a job started by analyze_product. Takes jobId. Returns state (pending, thinking, done, error) and, once done, the generated analysis/brand playbook. Free to poll — the cost was already charged when analyze_product started the job.",
|
|
277
|
+
inputSchema: z.object({ jobId: z.string().describe("The jobId analyze_product returned.") }).strict(),
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
name: "get_scheduled_posts",
|
|
281
|
+
title: "Get Scheduled Posts",
|
|
282
|
+
description: "Your own scheduled and draft posts in the content pipeline — title, status, scheduled time, approval status. What is queued to publish, not what already has (see get_post_performance for that). Does not publish or change anything. No cost to call.",
|
|
283
|
+
inputSchema: z.object({ appId: z.number().int().optional().describe("Your product's id. Omit only with a single-app workspace."), limit: z.number().int().optional().describe("Max rows (default 20, capped at 50).") }).strict(),
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
name: "get_post_performance",
|
|
287
|
+
title: "Get Post Performance",
|
|
288
|
+
description: "Your own already-published posts with their engagement counters — views, likes, comments, shares, platform, post date. This is the raw performance history, not an interpretation of it; pair with growth_brief for that. No cost to call.",
|
|
289
|
+
inputSchema: z.object({ appId: z.number().int().optional().describe("Your product's id. Omit only with a single-app workspace."), limit: z.number().int().optional().describe("Max rows (default 15, capped at 50).") }).strict(),
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
name: "get_video_stats",
|
|
293
|
+
title: "Get Video Stats",
|
|
294
|
+
description: "Your own most recently synced video performance stats across every connected creator — views, likes, comments, shares, plus a running total. Reads the last sync; does not trigger a new one. No cost to call.",
|
|
295
|
+
inputSchema: z.object({ appId: z.number().int().optional().describe("Your product's id. Omit only with a single-app workspace."), limit: z.number().int().optional().describe("Max videos (default 20, capped at 50).") }).strict(),
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
name: "get_content_plan",
|
|
299
|
+
title: "Get Content Plan",
|
|
300
|
+
description: "The saved weekly content plan for your own product, if one has been generated (see generate_content_plan). Read-only and free even when a plan exists. Returns plan: null when none has been made yet. No cost to call.",
|
|
301
|
+
inputSchema: z.object({ appId: z.number().int().describe("Your product's id.") }).strict(),
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
name: "review_post",
|
|
305
|
+
title: "Review Post",
|
|
306
|
+
description: "Score a post before you publish it: hook strength, an optional A-vs-B hook comparison, aesthetic and storytelling notes, and rewritten hook/caption suggestions. Pass postId for something already in your pipeline, or appId plus draft fields otherwise. Nothing is published. Free — nothing is billed for this, same as the dashboard's own pre-publish review.",
|
|
307
|
+
inputSchema: z.object({ postId: z.number().int().optional(), appId: z.number().int().optional(), title: z.string().optional(), titleB: z.string().optional(), caption: z.string().optional(), contentType: z.string().optional(), thumbnailUrl: z.string().optional(), mediaItems: z.array(z.unknown()).optional(), platform: z.string().optional(), influencerId: z.number().int().optional() }).strict(),
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
name: "draft_post",
|
|
311
|
+
title: "Draft Post",
|
|
312
|
+
description: "Generate a ready-to-use post draft (title, caption, hashtags, and a per-slide script) for a topic on your own product. Does not save or schedule anything. Billed like the dashboard's own Draft Post button: consumes your workspace's plan AI credits, not your personal MCP credits.",
|
|
313
|
+
inputSchema: z.object({ appId: z.number().int().describe("Your product's id."), topic: z.string().describe("What the post should be about."), contentType: z.string().optional(), slideCount: z.number().int().optional() }).strict(),
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
name: "growth_brief",
|
|
317
|
+
title: "Growth Brief",
|
|
318
|
+
description: "A plain-language growth brief for your own product: the single most important insight, wins, risks and concrete next actions, grounded in your real post history and synced analytics. Read-only. Billed like the dashboard's own Growth Brief button: your workspace's plan AI credits, not your personal MCP credits.",
|
|
319
|
+
inputSchema: z.object({ appId: z.number().int().describe("Your product's id.") }).strict(),
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
name: "generate_content_plan",
|
|
323
|
+
title: "Generate Content Plan",
|
|
324
|
+
description: "Generate a one-week content plan for your own product's creators: day-by-day posts with a hook, caption, hashtags and a full production script, grounded in what already worked in your post history. Saves the plan (fetch it later with get_content_plan) but does not schedule or publish. Billed like the dashboard's own Content Plan button: your workspace's plan AI credits, not your personal MCP credits.",
|
|
325
|
+
inputSchema: z.object({ appId: z.number().int().describe("Your product's id."), weekStart: z.string().optional().describe("ISO date. Defaults to next Monday."), influencerIds: z.array(z.number().int()).optional().describe("Subset of your creators to plan for.") }).strict(),
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
name: "generate_captions",
|
|
329
|
+
title: "Generate Captions",
|
|
330
|
+
description: "Generate timed on-screen caption cues for a video on your own product — a transcript plus start/end-timed lines. Does not burn captions onto any video. Billed like the dashboard's own Generate Captions button: your workspace's plan AI credits, not your personal MCP credits.",
|
|
331
|
+
inputSchema: z.object({ appId: z.number().int().describe("Your product's id."), durationSec: z.number().optional(), title: z.string().optional(), caption: z.string().optional(), videoUrl: z.string().optional(), transcriptHint: z.string().optional() }).strict(),
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
name: "list_social_connections",
|
|
335
|
+
title: "List Social Connections",
|
|
336
|
+
description: "List the social accounts your workspace has connected and what each connection is allowed to do — read the account, publish a post, manage comments. Also returns which platforms can be connected at all. No cost to call.",
|
|
337
|
+
inputSchema: z.object({}).strict(),
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
name: "connect_social_account",
|
|
341
|
+
title: "Connect Social Account",
|
|
342
|
+
description: "Get a link to open so you can connect one social account. The user approves at the provider; nothing is connected until they do, and no credential ever passes through this tool. No cost to call.",
|
|
343
|
+
inputSchema: z.object({ platform: z.string().describe("Which network to connect."), influencerId: z.number().int().optional(), appId: z.number().int().optional() }).strict(),
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
name: "answer_my_audience",
|
|
347
|
+
title: "Answer My Audience",
|
|
348
|
+
description: "The questions waiting for you under your own posts. Fetches a creator's recent posts, reads the comments on each, and returns them grouped under the post they were left on \u2014 every comment with a stable id, and the ones that read like questions or requests flagged and sorted to the top. Finds and drafts; it cannot post a reply, because no nooticr connection carries comment-write permission \u2014 the drafts are for a person to paste in themselves. Use when the job is to answer your own audience rather than to read about strangers. Consumes 2 nooticr credits for the post list plus 2 per post opened \u2014 14 credits at the default of 6 posts.",
|
|
349
|
+
inputSchema: z.object({ username: z.string().describe("Your handle, with or without @."), platform: z.string().optional().describe("Platform (default tiktok)."), limit: z.number().int().optional().describe("Posts to open (default 6, max 12). Each one is a comment fetch, so this is the price."), commentsPerPost: z.number().int().optional().describe("Comments to read per post (default 20, max 50)."), since: z.string().optional().describe("Only posts published on or after this date, as YYYY-MM-DD. Windows the posts, not the comments.") }).strict(),
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
name: "show_audience_replies",
|
|
353
|
+
title: "Show Audience Replies",
|
|
354
|
+
description: "Lay out the replies you drafted from answer_my_audience so a person can read them, grouped under the post each comment was left on, with what you decided to do about each one. Sends nothing and fetches nothing \u2014 it only draws what you pass it. Use after drafting, not instead of drafting. No cost to call.",
|
|
355
|
+
inputSchema: z.object({ username: z.string().describe("Whose audience this is, with or without @."), summary: z.string().optional(), replies: z.array(z.object({ id: z.string(), comment: z.string(), author: z.string().optional(), likes: z.number().optional(), postUrl: z.string().optional(), postTitle: z.string().optional(), postedAt: z.string().optional(), draft: z.string().optional(), kind: z.string().optional(), why: z.string().optional() })), themes: z.array(z.string()).optional(), nextSteps: z.array(z.string()).optional() }).strict(),
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
name: "track_competitor",
|
|
359
|
+
title: "Track Competitor",
|
|
360
|
+
description: "What a creator shipped recently, and which of it beat their own baseline. Scores every post in the window against the median of that same window, because a raw view count mostly measures follower count \u2014 outperformance against themselves is the signal worth reporting. If they are on your watchlist it also marks which posts are new since your last track_competitor call and moves that marker forward. Use for a rival you follow over time. Consumes 2 nooticr credits, whatever the window size.",
|
|
361
|
+
inputSchema: z.object({ username: z.string().describe("Creator handle, with or without @."), platform: z.string().optional().describe("Platform (default tiktok)."), limit: z.number().int().optional().describe("Posts in the window (default 12, max 30)."), metric: z.enum(["views", "likes", "comments", "shares", "engagementRate"]).optional().describe("Which stat to rank on (default views)."), since: z.string().optional().describe("Only posts published on or after this date, as YYYY-MM-DD.") }).strict(),
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
name: "who_should_i_work_with",
|
|
365
|
+
title: "Who Should I Work With",
|
|
366
|
+
description: "A collaboration shortlist for a niche. Searches creators by keyword and, when you name a creator who already fits, merges in their lookalikes \u2014 marking which search found each one, since agreement between the two is the strongest signal in the result. It does not measure audience overlap: proving the same people comment under two accounts costs roughly nine credits per candidate, so the result says so and shows how to check a finalist instead of faking it. Use to build a list to vet. Consumes 2 nooticr credits, or 4 with a seed creator.",
|
|
367
|
+
inputSchema: z.object({ niche: z.string().describe("Niche or keyword, e.g. 'home fitness'."), platform: z.enum(["tiktok", "instagram", "xiaohongshu", "youtube", "douyin"]).optional().describe("Which platform (default tiktok)."), seed: z.string().optional().describe("A creator who already fits \u2014 their lookalikes are added. Costs 2 more credits."), count: z.number().int().optional().describe("Candidates from the keyword search (default 8, max 20).") }).strict(),
|
|
368
|
+
},
|
|
369
|
+
{
|
|
370
|
+
name: "why_did_this_underperform",
|
|
371
|
+
title: "Why Did This Underperform",
|
|
372
|
+
description: "One post measured against the creator's own recent median rather than against another post. Fetches the post and that creator's recent window, takes the post back out of its own baseline, and returns where it actually sits in the distribution \u2014 median, quartiles, ratio and percentile \u2014 so the answer can be 'this is an ordinary result, not a failure'. Use when you have one post and nothing to compare it with; compare_posts is for two URLs you already picked. Consumes 3 nooticr credits.",
|
|
373
|
+
inputSchema: z.object({ url: z.string().describe("The post to explain."), username: z.string().optional().describe("Whose baseline to use, when the post does not name its creator."), platform: z.string().optional().describe("Platform of the creator's feed."), window: z.number().int().optional().describe("Posts in the comparison window (default 12, max 30)."), metric: z.enum(["views", "likes", "comments", "shares", "engagementRate"]).optional().describe("Which stat to compare (default views).") }).strict(),
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
name: "what_should_i_make_next",
|
|
377
|
+
title: "What Should I Make Next",
|
|
378
|
+
description: "Demand and supply in one result. Reads the comments on a creator's own recent posts for what their audience explicitly asks for, sweeps the niche for what is already being made, and returns both with ids so an idea can be traced back to the comment that asked for it \u2014 a gap nobody asked for is noise, a request nobody serves is the opportunity. Names the niche from the creator's most-used hashtag when you do not give one. Use to decide what to film; niche_report covers the supply half alone. Consumes 2 nooticr credits for the post list, 2 per post read, and 2 for the sweep \u2014 12 at the default of 4 posts.",
|
|
379
|
+
inputSchema: z.object({ username: z.string().describe("Your handle, with or without @."), platform: z.string().optional().describe("Platform (default tiktok)."), niche: z.string().optional().describe("Niche for the supply sweep. Defaults to your most frequent hashtag."), limit: z.number().int().optional().describe("Your posts to read comments on (default 4, max 8). This is the price."), commentsPerPost: z.number().int().optional().describe("Comments per post (default 25, max 50)."), supplyLimit: z.number().int().optional().describe("Posts in the niche sweep (default 12, max 30).") }).strict(),
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
name: "search_spoken_mentions",
|
|
383
|
+
title: "Search Spoken Mentions",
|
|
384
|
+
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 YouTube only — the only two networks whose posts carry a caption track this cheaply — and even there a video with no 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.",
|
|
385
|
+
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(),
|
|
386
|
+
},
|
|
387
|
+
];
|