@nooticr/mcp 1.26.40 → 1.26.42
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 +120 -15
- package/dist/auth.js +78 -7
- package/dist/config.js +11 -0
- package/dist/index.js +219 -9
- package/dist/shared/amazon.js +85 -14
- package/dist/shared/api-key.js +20 -0
- package/dist/shared/backend-only.js +27 -0
- package/dist/shared/connections.js +4 -2
- package/dist/shared/evidence-digest.js +83 -20
- package/dist/shared/getting-started.js +201 -0
- package/dist/shared/jobs.js +20 -10
- package/dist/shared/loading-plans.js +18 -0
- package/dist/shared/nooticr.js +74 -4
- package/dist/shared/oauth.js +3 -2
- package/dist/shared/output-schemas.js +104 -4
- package/dist/shared/prompts.js +5 -1
- package/dist/shared/spend.js +4 -0
- package/dist/shared/tools-def.js +47 -11
- package/dist/shared/tools.js +304 -8
- package/dist/shared/ui-template.js +159 -5
- package/dist/shared/watchlist.js +39 -0
- package/package.json +1 -1
|
@@ -39,9 +39,15 @@ export const NOOTICR_UI_TEMPLATE = `<!DOCTYPE html>
|
|
|
39
39
|
get_post_comments:"Get Post Comments",
|
|
40
40
|
search_creators:"Search Creators",
|
|
41
41
|
get_similar_creators:"Similar Creators",
|
|
42
|
+
suggest_creator_identity:"Same Person Elsewhere",
|
|
42
43
|
discover_sounds:"Discover Sounds",
|
|
43
44
|
understand_social_post:"Understand Social Post",
|
|
44
45
|
check_nooticr_credits:"Check Credits",
|
|
46
|
+
list_tool_runs:"Tool Runs",
|
|
47
|
+
get_tool_run:"Tool Run",
|
|
48
|
+
list_watchlist:"Watchlist",
|
|
49
|
+
detect_spoken_mentions:"Spoken Mentions",
|
|
50
|
+
nooticr_getting_started:"Getting Started",
|
|
45
51
|
compose_sequence:"Compose Sequence",
|
|
46
52
|
overlay_bake:"Overlay Bake",
|
|
47
53
|
spawn_variants:"Spawn Variants",
|
|
@@ -68,9 +74,15 @@ export const NOOTICR_UI_TEMPLATE = `<!DOCTYPE html>
|
|
|
68
74
|
get_post_comments:"Top comments on a post — sentiment, themes, viral threads.",
|
|
69
75
|
search_creators:"Find creators in a niche by engagement, followers, and content.",
|
|
70
76
|
get_similar_creators:"Find creators similar to a given handle.",
|
|
77
|
+
suggest_creator_identity:"Accounts on other networks that may be the same person, with the evidence.",
|
|
71
78
|
discover_sounds:"Trending sounds and music on TikTok/Instagram.",
|
|
72
79
|
understand_social_post:"The same frames and transcript, for a description of what happens on screen.",
|
|
73
80
|
check_nooticr_credits:"View your Nooticr credit balance and usage.",
|
|
81
|
+
list_tool_runs:"Where your credits went: every call, what it cost, and whether it worked.",
|
|
82
|
+
get_tool_run:"One call from your history, with its full error.",
|
|
83
|
+
list_watchlist:"The creators you are watching, and when you last caught up on each.",
|
|
84
|
+
detect_spoken_mentions:"Brands named out loud in a video, beside what its caption says.",
|
|
85
|
+
nooticr_getting_started:"Where your account stands and what to try first.",
|
|
74
86
|
compose_sequence:"AI-powered content composition for social posts.",
|
|
75
87
|
overlay_bake:"Bake text/image overlays onto video or image.",
|
|
76
88
|
spawn_variants:"Generate multiple content variants from a single seed.",
|
|
@@ -2997,6 +3009,85 @@ export const NOOTICR_UI_TEMPLATE = `<!DOCTYPE html>
|
|
|
2997
3009
|
+"</div>";
|
|
2998
3010
|
}
|
|
2999
3011
|
|
|
3012
|
+
// ─── Identity suggestions (suggest_creator_identity) ───
|
|
3013
|
+
//
|
|
3014
|
+
// Its own view rather than creatorCard: a candidate carries a points score
|
|
3015
|
+
// (50 for a shared bio link, 25 for a cross-link) that the vetting strip
|
|
3016
|
+
// would draw as "/100", and no follower total, because adding two accounts'
|
|
3017
|
+
// followers together on this evidence is exactly what the tool forbids.
|
|
3018
|
+
// Every card says "suggestion" on its face for the same reason.
|
|
3019
|
+
function renderIdentity(d){
|
|
3020
|
+
var seed=d.seed||{},list=d.suggestions||[];
|
|
3021
|
+
var searched=(d.searched||[]).join(", ");
|
|
3022
|
+
var head='<div style="margin-bottom:12px"><div style="font-size:15px;font-weight:700">Possibly the same person as @'
|
|
3023
|
+
+esc(seed.handle||"")+" on "+esc(seed.platform||"")+"</div>"
|
|
3024
|
+
+'<div style="font-size:12px;color:var(--muted);margin-top:3px">'
|
|
3025
|
+
+list.length+" suggestion"+(list.length===1?"":"s")+(searched?" · looked on "+esc(searched):"")
|
|
3026
|
+
+" · nothing merged, a human confirms</div>"
|
|
3027
|
+
+(d.note?'<div style="font-size:12px;color:var(--muted);margin-top:6px">'+esc(d.note)+"</div>":"")
|
|
3028
|
+
+"</div>";
|
|
3029
|
+
var cards=list.map(function(c){
|
|
3030
|
+
var high=c.confidence==="high",color=pColor(c.platform||"");
|
|
3031
|
+
var ev=(c.evidence||[]).map(function(e){
|
|
3032
|
+
var strong=e.strength==="strong";
|
|
3033
|
+
return '<li style="margin:3px 0"><span style="font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:'
|
|
3034
|
+
+(strong?"var(--green)":"var(--muted)")+'">'+esc(e.strength||"")+"</span> "
|
|
3035
|
+
+'<span style="font-weight:600">'+esc(String(e.signal||"").split("_").join(" "))+"</span>"
|
|
3036
|
+
+(e.detail?' <span style="color:var(--muted)">· '+esc(e.detail)+"</span>":"")+"</li>";
|
|
3037
|
+
}).join("");
|
|
3038
|
+
var link=c.profileUrl&&String(c.profileUrl).indexOf("http")===0
|
|
3039
|
+
?'<a href="'+esc(c.profileUrl)+'" style="font-size:12px;color:var(--fg)">Open profile</a>':"";
|
|
3040
|
+
return '<div class="creator-card" data-identity-candidate>'
|
|
3041
|
+
+'<div style="display:flex;align-items:center;gap:10px;margin-bottom:8px">'
|
|
3042
|
+
+'<div class="avatar-placeholder" style="flex-shrink:0;background:'+color+'20">'+pSvg(c.platform||"",22)+"</div>"
|
|
3043
|
+
+'<div style="min-width:0"><div style="font-size:14px;font-weight:700">'+esc(c.nickname||c.username||"")+"</div>"
|
|
3044
|
+
+'<div class="handle" style="overflow-wrap:anywhere">@'+esc(c.username||"")+" · "+esc(c.platform||"")+"</div></div></div>"
|
|
3045
|
+
+'<div style="font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;margin-bottom:6px;color:'
|
|
3046
|
+
+(high?"var(--green)":"var(--muted)")+'">'+(high?"likely":"possible")+" · suggestion</div>"
|
|
3047
|
+
+(c.followers!=null?'<div style="font-size:12px;color:var(--muted);margin-bottom:6px">'+fmtNum(c.followers)+" followers on this account alone</div>":"")
|
|
3048
|
+
+(ev?'<ul style="list-style:none;padding:0;margin:0;font-size:12px;line-height:1.4">'+ev+"</ul>":"")
|
|
3049
|
+
+(link?'<div style="margin-top:8px">'+link+"</div>":"")
|
|
3050
|
+
+"</div>";
|
|
3051
|
+
}).join("");
|
|
3052
|
+
var none=list.length?"":'<div class="empty-state"><div class="icon">👤</div><div class="text">No account cleared the bar. A creator with no links in their bio is hard to match this way; that is not proof they have no other accounts.</div></div>';
|
|
3053
|
+
var gaps=(d.unavailable||[]).map(function(u){return esc(u.platform||"")+" could not be searched";}).join(" · ");
|
|
3054
|
+
return '<div class="fade-in">'+head+none+(cards?'<div class="gallery">'+cards+"</div>":"")
|
|
3055
|
+
+(gaps?'<div style="font-size:12px;color:var(--muted);margin-top:10px">'+gaps+"</div>":"")+"</div>";
|
|
3056
|
+
}
|
|
3057
|
+
|
|
3058
|
+
// ─── Getting started (nooticr_getting_started) ───
|
|
3059
|
+
//
|
|
3060
|
+
// Checked before the credits card, which it would otherwise fall into: it
|
|
3061
|
+
// carries a balance too. A null is drawn as "could not read", never as 0 —
|
|
3062
|
+
// "no balance" and "no connections" are claims this view has not checked.
|
|
3063
|
+
function renderGettingStarted(d){
|
|
3064
|
+
function stat(label,val,unknown){
|
|
3065
|
+
return '<div style="flex:1;min-width:120px;padding:10px 12px;border:1px solid var(--border);border-radius:10px">'
|
|
3066
|
+
+'<div style="font-size:11px;color:var(--muted);text-transform:uppercase;letter-spacing:.05em">'+esc(label)+"</div>"
|
|
3067
|
+
+'<div style="font-size:16px;font-weight:700;margin-top:3px">'+(val==null?'<span style="color:var(--muted);font-weight:500;font-size:13px">'+esc(unknown)+"</span>":esc(String(val)))+"</div></div>";
|
|
3068
|
+
}
|
|
3069
|
+
var head='<div style="font-size:15px;font-weight:700;margin-bottom:10px">'
|
|
3070
|
+
+(d.signedIn?"Where your nooticr account stands":"Not signed in to nooticr yet")+"</div>";
|
|
3071
|
+
var stats=d.signedIn
|
|
3072
|
+
?'<div style="display:flex;gap:8px;flex-wrap:wrap;margin-bottom:14px">'
|
|
3073
|
+
+stat("Credits",d.balance,"could not read")
|
|
3074
|
+
+stat("Connected accounts",d.connectedCount,"could not read")
|
|
3075
|
+
+stat("Watching",d.watching,"could not read")+"</div>"
|
|
3076
|
+
:"";
|
|
3077
|
+
var steps=(d.nextSteps||[]).map(function(n){
|
|
3078
|
+
var price=Number(n.credits)>0?esc(String(n.credits))+" cr":"free";
|
|
3079
|
+
var ex=n.example&&typeof n.example==="object"?JSON.stringify(n.example):"";
|
|
3080
|
+
return '<div data-next-step style="padding:10px 0;border-top:1px solid var(--border)">'
|
|
3081
|
+
+'<div style="display:flex;align-items:baseline;gap:8px"><code style="font-weight:700">'+esc(n.tool||"")+"</code>"
|
|
3082
|
+
+'<span style="margin-left:auto;font-size:11px;font-weight:700;color:'+(price==="free"?"var(--green)":"var(--muted)")+'">'+price+"</span></div>"
|
|
3083
|
+
+'<div style="font-size:13px;margin-top:3px">'+esc(n.why||"")+"</div>"
|
|
3084
|
+
+(ex?'<div style="font-size:12px;color:var(--muted);margin-top:3px;font-family:ui-monospace,monospace">'+esc(ex)+"</div>":"")
|
|
3085
|
+
+"</div>";
|
|
3086
|
+
}).join("");
|
|
3087
|
+
return '<div class="fade-in">'+head+stats
|
|
3088
|
+
+'<div class="sec-label">Try next</div>'+steps+"</div>";
|
|
3089
|
+
}
|
|
3090
|
+
|
|
3000
3091
|
// ─── Vetting strip ───
|
|
3001
3092
|
//
|
|
3002
3093
|
// Only show_collab_shortlist sends these fields, so every other creator card
|
|
@@ -3414,8 +3505,8 @@ export const NOOTICR_UI_TEMPLATE = `<!DOCTYPE html>
|
|
|
3414
3505
|
+'<div class="sec-label">Read-only — nooticr can sweep these, no account to link</div>'
|
|
3415
3506
|
+'<div class="chiprow">'+readOnlyTags+"</div>"
|
|
3416
3507
|
+'<div class="section-text" style="margin-top:12px;color:var(--muted)">So "monitor my '
|
|
3417
|
-
+'brand on Weibo" works and "reply to that Weibo comment" cannot —
|
|
3418
|
-
+"
|
|
3508
|
+
+'brand on Weibo" works and "reply to that Weibo comment" cannot — nooticr does not '
|
|
3509
|
+
+"post replies on any network, whatever a connection is allowed to do.</div>"
|
|
3419
3510
|
+"</div></div>"
|
|
3420
3511
|
:"")
|
|
3421
3512
|
+"</div>";
|
|
@@ -4061,10 +4152,23 @@ export const NOOTICR_UI_TEMPLATE = `<!DOCTYPE html>
|
|
|
4061
4152
|
var drivers=(ins.drivers||[]).map(function(f){return amazonFinding(f,"driver");}).join("");
|
|
4062
4153
|
var barriers=(ins.barriers||[]).map(function(f){return amazonFinding(f,"barrier");}).join("");
|
|
4063
4154
|
var gaps=(ins.gaps||[]).map(function(f){return amazonFinding(f,"gap");}).join("");
|
|
4155
|
+
// #107: whether the listings on this card are the scan's own, re-read by
|
|
4156
|
+
// the server, or whatever the model re-sent. Only the first earns the
|
|
4157
|
+
// "check any claim" line at the bottom.
|
|
4158
|
+
var ver=ins.verification||null,verified=!!(ver&&ver.status==="verified");
|
|
4159
|
+
var unknown=(ver&&ver.unknownAsins)||[];
|
|
4064
4160
|
var strengths=(ins.strengths||[]).map(function(s){
|
|
4161
|
+
var off=s.asin&&unknown.indexOf(s.asin)!==-1;
|
|
4065
4162
|
return '<div class="amz-strength"><span class="amz-strength-brand">'+esc(s.brand||"")+"</span>"
|
|
4066
|
-
+'<span class="amz-strength-detail">'+esc(s.detail||"")+"</span
|
|
4163
|
+
+'<span class="amz-strength-detail">'+esc(s.detail||"")+"</span>"
|
|
4164
|
+
+(off?'<span data-not-in-scan style="margin-left:6px;font-size:10px;font-weight:700;text-transform:uppercase;color:var(--red,#b42318)">not in the scan</span>':"")
|
|
4165
|
+
+"</div>";
|
|
4067
4166
|
}).join("");
|
|
4167
|
+
var verBox=ver
|
|
4168
|
+
?'<div data-verification="'+(verified?"verified":"unverified")+'" style="font-size:12px;padding:8px 10px;margin-bottom:10px;border-radius:8px;border:1px solid var(--border);'
|
|
4169
|
+
+(verified?"":"background:rgba(180,35,24,.06);")+'">'
|
|
4170
|
+
+'<b>'+(verified?"Listings re-read from the scan":"Listings not checked against a scan")+"</b> · "+esc(ver.note||"")+"</div>"
|
|
4171
|
+
:"";
|
|
4068
4172
|
var angles=(ins.positioning||[]).map(function(a){
|
|
4069
4173
|
return '<div class="amz-angle"><div class="amz-angle-claim">'+esc(a.angle||"")+"</div>"
|
|
4070
4174
|
+(a.who?'<div class="amz-angle-row"><b>For</b> '+esc(a.who)+"</div>":"")
|
|
@@ -4073,6 +4177,7 @@ export const NOOTICR_UI_TEMPLATE = `<!DOCTYPE html>
|
|
|
4073
4177
|
+"</div>";
|
|
4074
4178
|
}).join("");
|
|
4075
4179
|
return '<div class="fade-in">'
|
|
4180
|
+
+verBox
|
|
4076
4181
|
+(ins.summary?'<div class="lede-box">'+esc(ins.summary)+"</div>":"")
|
|
4077
4182
|
+block("Purchase drivers","What makes someone buy. Click an id to read the review it rests on.",drivers)
|
|
4078
4183
|
+block("Purchase barriers","What stops them, or brings it back.",barriers)
|
|
@@ -4080,7 +4185,8 @@ export const NOOTICR_UI_TEMPLATE = `<!DOCTYPE html>
|
|
|
4080
4185
|
+block("Gaps in the category","Asked for repeatedly, served by nobody in this set.",gaps)
|
|
4081
4186
|
+block("Positioning angles","",angles)
|
|
4082
4187
|
+'<div class="ai-note">This read is your model’s, from the reviews on this screen — not a nooticr '
|
|
4083
|
-
+"rating of anyone’s product.
|
|
4188
|
+
+"rating of anyone’s product."
|
|
4189
|
+
+(verified||!ver?" Every claim above can be checked against the text it came from.":" The listings were not checked against a scan, so check them before relying on them.")+"</div>"
|
|
4084
4190
|
+"</div>";
|
|
4085
4191
|
}
|
|
4086
4192
|
|
|
@@ -4409,6 +4515,7 @@ export const NOOTICR_UI_TEMPLATE = `<!DOCTYPE html>
|
|
|
4409
4515
|
return;}
|
|
4410
4516
|
|
|
4411
4517
|
// Creators
|
|
4518
|
+
if(Array.isArray(d.suggestions)&&d.seed){app.innerHTML=renderIdentity(d);setTimeout(reportSize,50);return;}
|
|
4412
4519
|
if(d.creators&&Array.isArray(d.creators)){
|
|
4413
4520
|
if(!d.creators.length){app.innerHTML='<div class="empty-state fade-in"><div class="icon">👤</div><div class="text">No creators found</div></div>';return;}
|
|
4414
4521
|
app.innerHTML=galleryWrap(d.creators.map(function(c){return creatorCard(c);}).join(""),d.creators.length);return;}
|
|
@@ -4423,7 +4530,12 @@ export const NOOTICR_UI_TEMPLATE = `<!DOCTYPE html>
|
|
|
4423
4530
|
+'<div style="display:flex;align-items:center;gap:7px;font-size:16px;font-weight:700;margin-bottom:6px">'+mpIcon("note",16)+"<span>Transcript</span></div>"
|
|
4424
4531
|
+'<div class="muted" style="font-size:13px;color:var(--muted)">'+esc(d.reason||"No transcript available.")+"</div></div></div>";
|
|
4425
4532
|
return;}
|
|
4426
|
-
|
|
4533
|
+
// No regex escape here: this file is one template literal (and the
|
|
4534
|
+
// page is backslash-free by test), so the old whitespace class reached
|
|
4535
|
+
// the browser as the letter s ("I switched to Acme grinders" counted 3
|
|
4536
|
+
// words). Whitespace is spelled out by char code instead.
|
|
4537
|
+
var WS=new RegExp("["+String.fromCharCode(32,9,10,13)+"]+");
|
|
4538
|
+
var wc=d.wordCount||(d.transcript?String(d.transcript).trim().split(WS).filter(Boolean).length:0);
|
|
4427
4539
|
var meta=[wc+" words"];
|
|
4428
4540
|
if(d.language)meta.push(esc(d.language));
|
|
4429
4541
|
if(d.autoGenerated)meta.push("auto-generated");
|
|
@@ -4613,7 +4725,49 @@ export const NOOTICR_UI_TEMPLATE = `<!DOCTYPE html>
|
|
|
4613
4725
|
+(s.videoCount?'<div style="font-size:11px;color:var(--muted);margin-top:2px">'+fmtNum(s.videoCount)+" videos</div>":"")
|
|
4614
4726
|
+"</div></div>"+player+"</div>";
|
|
4615
4727
|
}).join(""),d.sounds.length);return;}
|
|
4728
|
+
// Run history (list_tool_runs / get_tool_run): where the credits went.
|
|
4729
|
+
// One row per call, the credits it actually took on the right, so the
|
|
4730
|
+
// expensive calls are the ones the eye lands on.
|
|
4731
|
+
if(Array.isArray(d.runs)||(d.run&&d.run.tool)){
|
|
4732
|
+
var runs=Array.isArray(d.runs)?d.runs:[d.run];
|
|
4733
|
+
if(!runs.length){app.innerHTML='<div class="empty-state fade-in"><div class="icon">🧾</div><div class="text">No runs in this window</div></div>';return;}
|
|
4734
|
+
var runRows=runs.map(function(r){
|
|
4735
|
+
var when=r.startedAt?String(r.startedAt).slice(0,16).split("T").join(" ")+" UTC":"";
|
|
4736
|
+
var secs=Number(r.durationMs)>0?(Number(r.durationMs)/1000).toFixed(1)+"s":"";
|
|
4737
|
+
var meta=[when,secs,r.userId?"user "+String(r.userId).slice(0,8):""].filter(Boolean).join(" • ");
|
|
4738
|
+
var cr=Number(r.credits)||0;
|
|
4739
|
+
return '<div style="display:flex;align-items:flex-start;gap:10px;padding:9px 0;border-bottom:1px solid var(--border)">'
|
|
4740
|
+
+'<div style="flex:1;min-width:0">'
|
|
4741
|
+
+'<div style="font-size:13.5px;font-weight:600">'+(r.ok===false?"⚠️ ":"")+esc(r.tool||"")+"</div>"
|
|
4742
|
+
+'<div style="font-size:11.5px;color:var(--muted)">'+esc(meta)+"</div>"
|
|
4743
|
+
+(r.error?'<div style="font-size:12px;color:var(--muted);margin-top:3px;word-break:break-word">'+esc(String(r.error))+"</div>":"")
|
|
4744
|
+
+"</div>"
|
|
4745
|
+
+'<div style="font-size:14px;font-weight:700;white-space:nowrap">'+(cr?cr+" cr":"free")+"</div></div>";
|
|
4746
|
+
}).join("");
|
|
4747
|
+
var total=d.creditsOnThisPage!=null?'<div class="section-label" style="margin-bottom:6px">'+esc(String(d.creditsOnThisPage))+" credits across "+runs.length+" call"+(runs.length===1?"":"s")+(d.scope==="workspace"?" in the workspace":"")+"</div>":"";
|
|
4748
|
+
app.innerHTML='<div class="card card-wide fade-in"><div class="card-body">'
|
|
4749
|
+
+'<div style="font-size:16px;font-weight:700;margin-bottom:8px">🧾 '+(d.run?"Tool run":"Where your credits went")+"</div>"
|
|
4750
|
+
+total+runRows
|
|
4751
|
+
+(d.nextBefore!=null?'<div style="font-size:11.5px;color:var(--muted);margin-top:8px">More runs are older than these.</div>':"")
|
|
4752
|
+
+"</div></div>";return;}
|
|
4753
|
+
// Watchlist (list_watchlist): who is being watched, and since when.
|
|
4754
|
+
if(Array.isArray(d.entries)&&d.watching!=null&&!d.posts&&!d.results){
|
|
4755
|
+
if(!d.entries.length){app.innerHTML='<div class="empty-state fade-in"><div class="icon">👀</div><div class="text">You are not watching anyone yet</div></div>';return;}
|
|
4756
|
+
var wlRows=d.entries.map(function(e){
|
|
4757
|
+
if(!e)return"";
|
|
4758
|
+
var since=e.lastCaughtUpAt?"caught up "+String(e.lastCaughtUpAt).slice(0,10):"never caught up";
|
|
4759
|
+
var added=e.addedAt?"added "+String(e.addedAt).slice(0,10):"";
|
|
4760
|
+
return '<div style="padding:9px 0;border-bottom:1px solid var(--border)">'
|
|
4761
|
+
+'<div style="font-size:13.5px;font-weight:600">@'+esc(e.handle||"")+' <span style="font-weight:400;color:var(--muted)">'+esc(e.platform||"")+"</span></div>"
|
|
4762
|
+
+'<div style="font-size:11.5px;color:var(--muted)">'+esc([added,since].filter(Boolean).join(" • "))+"</div>"
|
|
4763
|
+
+(e.note?'<div style="font-size:12px;margin-top:3px">'+esc(String(e.note))+"</div>":"")
|
|
4764
|
+
+"</div>";
|
|
4765
|
+
}).join("");
|
|
4766
|
+
app.innerHTML='<div class="card card-wide fade-in"><div class="card-body">'
|
|
4767
|
+
+'<div style="font-size:16px;font-weight:700;margin-bottom:8px">👀 Watching '+esc(String(d.watching))+"</div>"
|
|
4768
|
+
+wlRows+"</div></div>";return;}
|
|
4616
4769
|
// Credits
|
|
4770
|
+
if(d.gettingStarted===true){app.innerHTML=renderGettingStarted(d);setTimeout(reportSize,50);return;}
|
|
4617
4771
|
if(d.balance!=null||d.tier){
|
|
4618
4772
|
var bal=Number(d.balance)||0,tier=d.tier||"",ff=d.firstFreeTools||[];
|
|
4619
4773
|
var freeHtml="";
|
package/dist/shared/watchlist.js
CHANGED
|
@@ -513,6 +513,45 @@ export function registerWatchlist(server, makeClient, store) {
|
|
|
513
513
|
const entries = await store.list(owner);
|
|
514
514
|
return text({ removed, id, watching: entries.length, entries });
|
|
515
515
|
});
|
|
516
|
+
// Reading the list back (#101). The resource above says the same thing, but
|
|
517
|
+
// most hosts never surface a resource to the model and ChatGPT does not read
|
|
518
|
+
// one at all, so "who am I watching?" had no free answer: the only tools
|
|
519
|
+
// that returned the list were the paid catch-up and standings runs. Built on
|
|
520
|
+
// `store`, not the backend's own twin, for the reason the header gives.
|
|
521
|
+
server.registerTool("list_watchlist", {
|
|
522
|
+
title: "List Watchlist",
|
|
523
|
+
_meta: {
|
|
524
|
+
ui: { resourceUri: "ui://nooticr/list_watchlist" },
|
|
525
|
+
"ui/resourceUri": "ui://nooticr/list_watchlist",
|
|
526
|
+
"openai/outputTemplate": "ui://nooticr/list_watchlist.html",
|
|
527
|
+
},
|
|
528
|
+
description: "The creators on your watchlist: handle, platform, your note, when you added them and when " +
|
|
529
|
+
"you last caught up on them. Reads the stored list only — nothing is fetched, so there is no " +
|
|
530
|
+
"cost. Use it to answer 'who am I watching?', or to find the exact handle and platform " +
|
|
531
|
+
"unwatch_creator needs, before paying for catch_up_watchlist. No cost to call.",
|
|
532
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
533
|
+
inputSchema: z.object({}).strict(),
|
|
534
|
+
outputSchema: z
|
|
535
|
+
.object({
|
|
536
|
+
watching: orNull(z.number()),
|
|
537
|
+
entries: z.array(z.union([z.object({}).passthrough(), z.null()])).nullish(),
|
|
538
|
+
})
|
|
539
|
+
.passthrough(),
|
|
540
|
+
}, async (_args, extra) => {
|
|
541
|
+
const client = await makeClient({ ...extra, arguments: {} });
|
|
542
|
+
const entries = (await store.list(await ownerOf(client))).map((e) => ({
|
|
543
|
+
id: e.id,
|
|
544
|
+
platform: e.platform,
|
|
545
|
+
handle: e.handle,
|
|
546
|
+
note: e.note ?? null,
|
|
547
|
+
addedAt: e.addedAt,
|
|
548
|
+
// When "since I last looked" starts from; null until the first catch-up.
|
|
549
|
+
// The baseline's post ids stay out: they are the store's bookkeeping,
|
|
550
|
+
// and a long list of them is context spent on nothing.
|
|
551
|
+
lastCaughtUpAt: e.baseline?.capturedAt ?? null,
|
|
552
|
+
}));
|
|
553
|
+
return text({ watching: entries.length, entries });
|
|
554
|
+
});
|
|
516
555
|
server.registerTool("catch_up_watchlist", {
|
|
517
556
|
title: "Catch Up On Watchlist",
|
|
518
557
|
_meta: {
|
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.42",
|
|
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": {
|