@nooticr/mcp 1.26.31 → 1.26.32
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.
|
@@ -94,7 +94,20 @@ function postLine(row) {
|
|
|
94
94
|
.join(" · ");
|
|
95
95
|
const text = clamp(str(row.caption) || str(row.title) || str(row.text));
|
|
96
96
|
const url = str(row.externalUrl) || str(row.url);
|
|
97
|
-
|
|
97
|
+
// Replies under the post, when something opened it. Nested one level down,
|
|
98
|
+
// which is exactly where #59 could come back without the chain-map gate
|
|
99
|
+
// noticing: `posts` is rendered, so the check is satisfied while the thing
|
|
100
|
+
// that was paid for — the comment that says "same here, this is my problem"
|
|
101
|
+
// — stays in the payload only.
|
|
102
|
+
const replies = Array.isArray(row.commentSample) ? row.commentSample : [];
|
|
103
|
+
const shown = replies.slice(0, 4).map((c) => ` - ${clamp(commentLine(c), 240)}`);
|
|
104
|
+
if (replies.length > shown.length) {
|
|
105
|
+
shown.push(` - (${replies.length - shown.length} more under this post)`);
|
|
106
|
+
}
|
|
107
|
+
const readNothing = row.commentsRead === 0 ? " - (this thread would not open — not that nobody replied)" : "";
|
|
108
|
+
return [head && `[${head}]`, text, url, ...shown, readNothing]
|
|
109
|
+
.filter(Boolean)
|
|
110
|
+
.join("\n ");
|
|
98
111
|
}
|
|
99
112
|
/**
|
|
100
113
|
* One comment, with its id.
|
package/dist/shared/jobs.js
CHANGED
|
@@ -2112,6 +2112,15 @@ export function registerJobTools(server, makeClient, store) {
|
|
|
2112
2112
|
.optional()
|
|
2113
2113
|
.describe("Where to look (default reddit). Each one is a separate paid search."),
|
|
2114
2114
|
limit: z.number().int().optional().describe("Posts per query shape (default 6, max 15)."),
|
|
2115
|
+
readComments: z
|
|
2116
|
+
.boolean()
|
|
2117
|
+
.optional()
|
|
2118
|
+
.describe("Open the most promising results and read their comments too (default false). " +
|
|
2119
|
+
"On Reddit especially, the person with the problem is at least as often replying " +
|
|
2120
|
+
"under someone else's thread as posting their own, and a post-only search cannot " +
|
|
2121
|
+
"see any of them. Costs 2 nooticr credits per post opened, up to 5 — and they are " +
|
|
2122
|
+
"opened on the MERGED, deduped results, so it is five extra calls in total rather " +
|
|
2123
|
+
"than five per query shape per platform."),
|
|
2115
2124
|
queries: z
|
|
2116
2125
|
.array(z.string())
|
|
2117
2126
|
.optional()
|
|
@@ -2181,6 +2190,41 @@ export function registerJobTools(server, makeClient, store) {
|
|
|
2181
2190
|
}
|
|
2182
2191
|
}
|
|
2183
2192
|
}
|
|
2193
|
+
// Read the comments under the most promising results, on the MERGED,
|
|
2194
|
+
// deduped list rather than per search.
|
|
2195
|
+
//
|
|
2196
|
+
// That distinction is the whole reason this lives here and not in the
|
|
2197
|
+
// backend's own `include_comments`: three query shapes across two
|
|
2198
|
+
// platforms is six searches, and opening five posts under each would be
|
|
2199
|
+
// thirty paid calls for a tool whose description promises a wide CHEAP
|
|
2200
|
+
// net. On the merged list it is five, once.
|
|
2201
|
+
//
|
|
2202
|
+
// Why at all: on Reddit the person with the problem is at least as often
|
|
2203
|
+
// replying under someone else's thread as posting their own, and a
|
|
2204
|
+
// post-only search cannot see any of them (Nooticr/nooticr-server#63).
|
|
2205
|
+
if (args.readComments && posts.length) {
|
|
2206
|
+
const OPEN = 5;
|
|
2207
|
+
for (const post of posts.slice(0, OPEN)) {
|
|
2208
|
+
const url = String(post.externalUrl ?? post.url ?? "");
|
|
2209
|
+
if (!url)
|
|
2210
|
+
continue;
|
|
2211
|
+
try {
|
|
2212
|
+
const res = await client.callTool("get_post_comments", { url, limit: 8 });
|
|
2213
|
+
const structured = (res.structured ?? {});
|
|
2214
|
+
spend.record("get_post_comments", structured);
|
|
2215
|
+
const comments = Array.isArray(structured.comments) ? structured.comments : [];
|
|
2216
|
+
post.commentsRead = comments.length;
|
|
2217
|
+
post.commentSample = comments;
|
|
2218
|
+
}
|
|
2219
|
+
catch {
|
|
2220
|
+
// A thread that will not open is not a failed sweep — the post's
|
|
2221
|
+
// own text may carry the complaint, and this is the cheap net.
|
|
2222
|
+
// Stated rather than left absent, so a reader can tell "no
|
|
2223
|
+
// comments" from "not looked at".
|
|
2224
|
+
post.commentsRead = 0;
|
|
2225
|
+
}
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2184
2228
|
if (!posts.length) {
|
|
2185
2229
|
return evidence([
|
|
2186
2230
|
`Nothing came back for "${problem}" on ${platforms.join(", ")}.`,
|
|
@@ -2223,6 +2267,17 @@ export function registerJobTools(server, makeClient, store) {
|
|
|
2223
2267
|
"and say how many you rejected and why — a shortlist with no reject count reads as if " +
|
|
2224
2268
|
"the search was precise.",
|
|
2225
2269
|
"",
|
|
2270
|
+
args.readComments
|
|
2271
|
+
? "The first few posts carry `commentSample`: the replies underneath them. Judge those " +
|
|
2272
|
+
"the same way — a person saying \"same here, this is exactly my problem\" under " +
|
|
2273
|
+
"someone else's thread is a better find than the thread itself, and the permalink " +
|
|
2274
|
+
"to quote is the post's. `commentsRead: 0` means the thread would not open, not " +
|
|
2275
|
+
"that nobody replied."
|
|
2276
|
+
: "Only the posts themselves were searched. On Reddit the person with the problem is " +
|
|
2277
|
+
"at least as often replying under someone else's thread as posting their own — if " +
|
|
2278
|
+
"these results look thin, call again with `readComments: true` to open the top few " +
|
|
2279
|
+
"and read what is underneath.",
|
|
2280
|
+
"",
|
|
2226
2281
|
args.queries?.length
|
|
2227
2282
|
? "`foundBy` says which of your own phrasings surfaced each post, in the order you " +
|
|
2228
2283
|
"gave them."
|
package/dist/shared/tools-def.js
CHANGED
|
@@ -394,7 +394,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
394
394
|
name: "find_people_with_problem",
|
|
395
395
|
title: "Find People With A Problem",
|
|
396
396
|
description: "Find people describing a problem in their own words — prospects, early users, anyone whose complaint your product answers. Say the problem the way a person would say it (\"I spend hours checking competitors by hand\") and this searches for posts shaped like that complaint, not for the topic it is about. That difference is the point: searching the TOPIC finds marketing about it, which is what discover_social_posts is for. Widens the query into the forms a complaint takes — the plain phrasing, the does-anyone-else question, the is-there-a-tool ask — and merges what each returns. It is a wide cheap net and NOT a filter: the posts come back for you to judge, and many will be off-target. Consumes 2 nooticr credits per platform searched. Defaults to reddit, where people describe workflow pain in sentences; add twitter for volume.",
|
|
397
|
-
inputSchema: z.object({ problem: z.string().describe("The problem in a person's own words, not a topic. \"I waste an hour a day checking what competitors posted\" finds people; \"competitor analysis\" finds marketing about competitor analysis."), platforms: z.array(z.enum(["reddit", "twitter", "youtube", "tiktok", "instagram", "linkedin"])).optional().describe("Where to look (default reddit). Each one is a separate paid search."), limit: z.number().int().optional().describe("Posts per query shape (default 6, max 15)."), queries: z.array(z.string()).optional().describe("Search these exact phrasings instead of the ones derived from `problem` (max 4). You are better at this than a string template is: write how a person would actually word the complaint on the network being searched \u2014 a Reddit title, an X post \u2014 and keep each one short, because this is a keyword search and a long sentence matches on its commonest words.") }).strict(),
|
|
397
|
+
inputSchema: z.object({ problem: z.string().describe("The problem in a person's own words, not a topic. \"I waste an hour a day checking what competitors posted\" finds people; \"competitor analysis\" finds marketing about competitor analysis."), platforms: z.array(z.enum(["reddit", "twitter", "youtube", "tiktok", "instagram", "linkedin"])).optional().describe("Where to look (default reddit). Each one is a separate paid search."), limit: z.number().int().optional().describe("Posts per query shape (default 6, max 15)."), readComments: z.boolean().optional().describe("Open the most promising results and read their comments too (default false). On Reddit especially, the person with the problem is at least as often replying under someone else's thread as posting their own, and a post-only search cannot see any of them. Costs 2 nooticr credits per post opened, up to 5 \u2014 and they are opened on the MERGED, deduped results, so it is five extra calls in total rather than five per query shape per platform."), queries: z.array(z.string()).optional().describe("Search these exact phrasings instead of the ones derived from `problem` (max 4). You are better at this than a string template is: write how a person would actually word the complaint on the network being searched \u2014 a Reddit title, an X post \u2014 and keep each one short, because this is a keyword search and a long sentence matches on its commonest words.") }).strict(),
|
|
398
398
|
},
|
|
399
399
|
{
|
|
400
400
|
name: "search_spoken_mentions",
|
package/dist/shared/tools.js
CHANGED
|
@@ -26,7 +26,7 @@ import { loadingPlansJson } from "./loading-plans.js";
|
|
|
26
26
|
import { registerHandoff } from "./handoff.js";
|
|
27
27
|
import { registerCollabTools } from "./collab.js";
|
|
28
28
|
/** Current MCP server version — bumped on every deploy for traceability. */
|
|
29
|
-
export const MCP_SERVER_VERSION = "1.26.
|
|
29
|
+
export const MCP_SERVER_VERSION = "1.26.32";
|
|
30
30
|
/** MCP Apps extension identifier */
|
|
31
31
|
const UI_EXTENSION = "io.modelcontextprotocol/ui";
|
|
32
32
|
/** MIME type for MCP Apps HTML resources */
|
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.32",
|
|
5
5
|
"description": "MCP server for nooticr - fetch, discover and understand TikTok/Instagram/YouTube/X/Reddit/LinkedIn/Weibo posts with AI (media metadata, niche discovery, brand monitoring, hook & viral analysis)",
|
|
6
6
|
"homepage": "https://mcp.nooticr.com",
|
|
7
7
|
"repository": {
|