@nooticr/mcp 1.26.46 → 1.26.47

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 CHANGED
@@ -52,7 +52,7 @@ npx @nooticr/mcp login # one-time sign-in (Google)
52
52
 
53
53
  ## Tools
54
54
 
55
- 86 tools, grouped by what you are trying to do. Prices are in nooticr credits and
55
+ 87 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
  The job tools under **Answer a question you actually have** are not
@@ -174,6 +174,7 @@ a `scanId` for the rest.
174
174
  | `check_nooticr_credits` | free | Balance and billing URL. |
175
175
  | `list_tool_runs` | free | Your tool-call history: what ran, whether it worked, and the credits each call actually took (0 for a free, refunded, cached or replayed call). `scope: "workspace"` shows every member's runs to owners and admins. |
176
176
  | `get_tool_run` | free | One run from that history by id, with its full error. |
177
+ | `get_usage_report` | free | The run ledger added up over a window: total calls, failures and credits actually taken, per tool, per day and the most recent failures. `scope: "workspace"` adds one row per seat, for the workspace's owners and admins. |
177
178
  | `nooticr_login` | free | Re-link the account when a call fails with an authentication error. |
178
179
  | `list_social_connections` | free | What you have connected and what each connection is actually allowed to do — read the account, publish a post, manage comments — as yes, no or **unknown**. "Manage comments" reports the platform's grant; no nooticr tool posts or replies to a comment. Unknown means the grant predates scope recording: treat it as "try it", not as a refusal. Also says which platforms can be linked at all, which is a smaller set than the networks nooticr reads. |
179
180
  | `connect_social_account` | free | A link the user opens to connect one account. They approve at the provider in their own browser; nothing is connected until they do, and no credential passes through the tool or the model's context. Each call mints a fresh link — an old one does not work twice. |
@@ -495,6 +495,7 @@ export const LOADING_PLANS = {
495
495
  check_nooticr_credits: { label: "Checking your balance", kind: "text", n: 1, steps: [], free: true },
496
496
  list_tool_runs: { label: "Reading your run history", kind: "text", n: 1, steps: [], free: true },
497
497
  get_tool_run: { label: "Reading the run", kind: "text", n: 1, steps: [], free: true },
498
+ get_usage_report: { label: "Adding up the run ledger", kind: "text", n: 1, steps: [], free: true },
498
499
  nooticr_getting_started: { label: "Reading where your account stands", kind: "text", n: 1, steps: [], free: true },
499
500
  nooticr_login: { label: "Getting a sign-in link", kind: "text", n: 1, steps: [], free: true },
500
501
  watch_creator: { label: "Adding to your watchlist", kind: "text", n: 1, steps: [], free: true },
@@ -1135,6 +1135,23 @@ export const OUTPUT_SCHEMAS = {
1135
1135
  userId: scalar().describe("Whose run it was; only in the workspace scope."),
1136
1136
  }).nullish(),
1137
1137
  }),
1138
+ get_usage_report: open({
1139
+ report: scalar(),
1140
+ scope: scalar().describe("mine or workspace."),
1141
+ from: scalar(),
1142
+ to: scalar(),
1143
+ totals: open({
1144
+ calls: scalar(),
1145
+ failures: scalar(),
1146
+ credits: scalar().describe("Credits actually taken: free first uses, refunds and replays are zero."),
1147
+ tools: scalar(),
1148
+ seats: scalar(),
1149
+ }).nullish(),
1150
+ byTool: listOf(open({ tool: scalar(), calls: scalar(), failures: scalar(), credits: scalar(), lastUsedAt: scalar() })),
1151
+ bySeat: listOf(open({ userId: scalar(), email: scalar(), name: scalar(), calls: scalar(), failures: scalar(), credits: scalar() })).describe("Workspace reports only; empty on a personal one."),
1152
+ byDay: listOf(open({ day: scalar(), calls: scalar(), failures: scalar(), credits: scalar() })),
1153
+ recentFailures: listOf(open({ id: scalar(), tool: scalar(), error: scalar(), startedAt: scalar(), userId: scalar(), credits: scalar() })),
1154
+ }),
1138
1155
  nooticr_login: open({
1139
1156
  signedIn: scalar().describe("true when the session is already good and no link is needed."),
1140
1157
  loginUrl: scalar().describe("Only present when a sign-in is actually required."),
@@ -222,6 +222,12 @@ export const TOOL_DEFINITIONS = [
222
222
  description: "One run from list_tool_runs by its id, with its full error text, credentials scrubbed. A run outside your scope reads as not found. No cost to call. Use to look closely at one charge.",
223
223
  inputSchema: z.object({ id: z.number().int(), scope: z.enum(["mine", "workspace"]).optional() }).strict(),
224
224
  },
225
+ {
226
+ name: "get_usage_report",
227
+ title: "Get Usage Report",
228
+ description: "A usage report over a window: total calls, failures and credits actually taken, per tool, per day, and recent failures; scope workspace adds one row per seat, for owners and admins. No cost to call.",
229
+ inputSchema: z.object({ days: z.number().int().min(1).max(366).optional(), from: z.string().optional(), to: z.string().optional(), scope: z.enum(["mine", "workspace"]).optional() }).strict(),
230
+ },
225
231
  {
226
232
  name: "nooticr_login",
227
233
  title: "Nooticr Login",
@@ -30,7 +30,7 @@ import { registerCollabTools } from "./collab.js";
30
30
  import { registerAmazonTools } from "./amazon.js";
31
31
  import { registerMarketplaceTools } from "./marketplace.js";
32
32
  /** Current MCP server version — bumped on every deploy for traceability. */
33
- export const MCP_SERVER_VERSION = "1.26.46";
33
+ export const MCP_SERVER_VERSION = "1.26.47";
34
34
  /** MCP Apps extension identifier */
35
35
  const UI_EXTENSION = "io.modelcontextprotocol/ui";
36
36
  /** MIME type for MCP Apps HTML resources */
@@ -604,6 +604,7 @@ export function createMcpServer(rawMakeClient, opts) {
604
604
  "check_nooticr_credits",
605
605
  "list_tool_runs",
606
606
  "get_tool_run",
607
+ "get_usage_report",
607
608
  "list_watchlist",
608
609
  "nooticr_getting_started",
609
610
  "understand_social_post",
@@ -2610,6 +2611,43 @@ export function createMcpServer(rawMakeClient, opts) {
2610
2611
  return toolError("get_tool_run failed", err);
2611
2612
  }
2612
2613
  });
2614
+ // The run ledger added up (#93): the question an owner or a security review
2615
+ // asks ("what did the team run this month, what did it cost, what failed")
2616
+ // answered from the same rows as the history, never from memory.
2617
+ server.registerTool("get_usage_report", {
2618
+ title: "Get Usage Report",
2619
+ description: "A usage report over a window: total calls, failures and credits actually taken; one row " +
2620
+ "per tool, most expensive first; one row per day, quiet days included; and the ten most " +
2621
+ "recent failures with their errors, credentials scrubbed. scope: \"workspace\" covers " +
2622
+ "every member of your workspace and adds one row per seat; it is for the workspace's " +
2623
+ "owners and admins. Reads the stored run ledger, so it is free and says what happened, " +
2624
+ "not what is running. No cost to call. Use for \"what did we spend this month and on " +
2625
+ "what\" or an audit of what nooticr did on the workspace's behalf; list_tool_runs is the " +
2626
+ "call-by-call view.",
2627
+ _meta: {
2628
+ ui: { resourceUri: uiResource("get_usage_report") },
2629
+ "ui/resourceUri": uiResource("get_usage_report"),
2630
+ "openai/outputTemplate": appsSdkResource("get_usage_report"),
2631
+ },
2632
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
2633
+ outputSchema: OUTPUT_SCHEMAS.get_usage_report,
2634
+ inputSchema: z
2635
+ .object({
2636
+ days: z.number().int().min(1).max(366).optional().describe("The window, in days back from now (default 30)."),
2637
+ from: z.string().optional().describe("Start of the window, ISO date or timestamp. Overrides days."),
2638
+ to: z.string().optional().describe("End of the window, ISO date or timestamp (default now)."),
2639
+ scope: runFilters.scope,
2640
+ })
2641
+ .strict(),
2642
+ }, async (args, extra) => {
2643
+ const client = await makeClient({ ...extra, arguments: args });
2644
+ try {
2645
+ return await toToolResult(await client.callTool("get_usage_report", { ...args }));
2646
+ }
2647
+ catch (err) {
2648
+ return toolError("get_usage_report failed", err);
2649
+ }
2650
+ });
2613
2651
  server.registerTool("nooticr_login", {
2614
2652
  title: "Nooticr Login",
2615
2653
  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. No cost to call." +
@@ -45,6 +45,7 @@ export const NOOTICR_UI_TEMPLATE = `<!DOCTYPE html>
45
45
  check_nooticr_credits:"Check Credits",
46
46
  list_tool_runs:"Tool Runs",
47
47
  get_tool_run:"Tool Run",
48
+ get_usage_report:"Usage Report",
48
49
  list_watchlist:"Watchlist",
49
50
  detect_spoken_mentions:"Spoken Mentions",
50
51
  nooticr_getting_started:"Getting Started",
@@ -84,6 +85,7 @@ export const NOOTICR_UI_TEMPLATE = `<!DOCTYPE html>
84
85
  check_nooticr_credits:"View your Nooticr credit balance and usage.",
85
86
  list_tool_runs:"Where your credits went: every call, what it cost, and whether it worked.",
86
87
  get_tool_run:"One call from your history, with its full error.",
88
+ get_usage_report:"What was spent, on what, by whom, and what failed.",
87
89
  list_watchlist:"The creators you are watching, and when you last caught up on each.",
88
90
  detect_spoken_mentions:"Brands named out loud in a video, beside what its caption says.",
89
91
  nooticr_getting_started:"Where your account stands and what to try first.",
@@ -3152,6 +3154,48 @@ export const NOOTICR_UI_TEMPLATE = `<!DOCTYPE html>
3152
3154
  return '<div class="fade-in">'+head+body+"</div>";
3153
3155
  }
3154
3156
 
3157
+ // ─── Usage report (get_usage_report) ───
3158
+ //
3159
+ // A spend table: the totals first, then where it went. Credits are what was
3160
+ // actually taken, so a free first use or a refund reads as 0, not as the
3161
+ // tool's price.
3162
+ function renderUsageReport(d){
3163
+ var t=d.totals||{};
3164
+ function tile(label,val){
3165
+ return '<div style="flex:1;min-width:110px;padding:10px 12px;border:1px solid var(--border);border-radius:10px">'
3166
+ +'<div style="font-size:11px;color:var(--muted);text-transform:uppercase;letter-spacing:.05em">'+esc(label)+"</div>"
3167
+ +'<div style="font-size:18px;font-weight:700;margin-top:3px">'+esc(String(val==null?"—":val))+"</div></div>";
3168
+ }
3169
+ function table(title,cols,rows){
3170
+ if(!rows.length)return "";
3171
+ return '<div class="sec-label" style="margin-top:14px">'+esc(title)+"</div>"
3172
+ +'<table style="width:100%;border-collapse:collapse;font-size:13px">'
3173
+ +"<tr>"+cols.map(function(c,i){return '<th style="text-align:'+(i?"right":"left")+';font-weight:600;color:var(--muted);padding:4px 0">'+esc(c)+"</th>";}).join("")+"</tr>"
3174
+ +rows.map(function(r){return '<tr data-usage-row style="border-top:1px solid var(--border)">'+r.map(function(v,i){return '<td style="padding:6px 0;text-align:'+(i?"right":"left")+'">'+esc(String(v))+"</td>";}).join("")+"</tr>";}).join("")
3175
+ +"</table>";
3176
+ }
3177
+ var from=String(d.from||"").slice(0,10),to=String(d.to||"").slice(0,10);
3178
+ var days=(d.byDay||[]),max=1;
3179
+ days.forEach(function(x){max=Math.max(max,Number(x.credits)||0);});
3180
+ var bars=days.length?'<div class="sec-label" style="margin-top:14px">Credits per day</div><div style="display:flex;align-items:flex-end;gap:2px;height:48px">'
3181
+ +days.map(function(x){var c=Number(x.credits)||0;return '<span title="'+esc(String(x.day))+": "+c+' cr" style="flex:1;min-width:2px;background:var(--fg);opacity:'+(c?".6":".12")+';height:'+Math.max(2,Math.round(c/max*46))+'px"></span>';}).join("")+"</div>":"";
3182
+ var fails=(d.recentFailures||[]).map(function(f){
3183
+ return '<div style="padding:6px 0;border-top:1px solid var(--border);font-size:12.5px"><b>'+esc(f.tool||"")+"</b> "
3184
+ +'<span style="color:var(--muted)">'+esc(String(f.startedAt||"").slice(0,16).split("T").join(" "))+"</span>"
3185
+ +(f.error?'<div style="color:var(--muted);word-break:break-word">'+esc(f.error)+"</div>":"")+"</div>";
3186
+ }).join("");
3187
+ return '<div class="fade-in">'
3188
+ +'<div style="font-size:15px;font-weight:700">'+(d.scope==="workspace"?"Workspace usage":"Your usage")+"</div>"
3189
+ +'<div style="font-size:12px;color:var(--muted);margin:2px 0 12px">'+esc(from)+" → "+esc(to)+"</div>"
3190
+ +'<div style="display:flex;gap:8px;flex-wrap:wrap">'+tile("Credits",t.credits)+tile("Calls",t.calls)+tile("Failures",t.failures)
3191
+ +(d.scope==="workspace"?tile("Seats",t.seats):"")+"</div>"
3192
+ +table("By tool",["Tool","Calls","Failed","Credits"],(d.byTool||[]).map(function(r){return [r.tool,r.calls,r.failures,r.credits];}))
3193
+ +table("By seat",["Member","Calls","Failed","Credits"],(d.bySeat||[]).map(function(r){return [r.name||r.email||String(r.userId||"").slice(0,8),r.calls,r.failures,r.credits];}))
3194
+ +bars
3195
+ +(fails?'<div class="sec-label" style="margin-top:14px">Recent failures</div>'+fails:"")
3196
+ +"</div>";
3197
+ }
3198
+
3155
3199
  // ─── Vetting strip ───
3156
3200
  //
3157
3201
  // Only show_collab_shortlist sends these fields, so every other creator card
@@ -4850,6 +4894,7 @@ export const NOOTICR_UI_TEMPLATE = `<!DOCTYPE html>
4850
4894
  // Credits
4851
4895
  if(d.gettingStarted===true){app.innerHTML=renderGettingStarted(d);setTimeout(reportSize,50);return;}
4852
4896
  if(d.connector==="google_analytics"||d.connector==="search_console"||d.connector==="posthog"){app.innerHTML=renderConnector(d);setTimeout(reportSize,50);return;}
4897
+ if(d.report===true&&d.totals){app.innerHTML=renderUsageReport(d);setTimeout(reportSize,50);return;}
4853
4898
  if(d.balance!=null||d.tier){
4854
4899
  var bal=Number(d.balance)||0,tier=d.tier||"",ff=d.firstFreeTools||[];
4855
4900
  var freeHtml="";
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.46",
4
+ "version": "1.26.47",
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": {