@nooticr/mcp
MCP (Model Context Protocol) server for nooticr.
Gives an AI assistant three things: it can read real social posts across ten networks (TikTok, Instagram, YouTube, X, Reddit, LinkedIn, Douyin, Xiaohongshu, Weibo, Bilibili), understand them — transcript, video frames, comments, the numbers — and make something from what it learned: hooks, variants to film, a scored draft, a repurposed thread.
The understanding is your model's, not ours. Every tool here fetches material and hands it over with an account of what to do with it; none of them ask a model of ours for an opinion first. You pay for the fetch and nothing else.
It also monitors a name: search_mentions sweeps nine of those networks for
every comment that says your brand, inside a date window you choose, and
search_spoken_mentions reads the words actually said out loud in TikTok,
YouTube and Douyin videos for the mentions that were never typed anywhere.
Runs over stdio locally or as a hosted connector at https://mcp.nooticr.com/mcp.
Billed against your nooticr credits; new accounts get 20 free.
Install (one link)
Claude Code — register the marketplace, then install the plugin:
/plugin marketplace add Nooticr/nooticr-mcp
/plugin install nooticr@nooticr
Claude Code / CLI without the plugin:
claude mcp add nooticr --user -- npx -y @nooticr/mcp
npx @nooticr/mcp login # one-time sign-in (Google)
Cursor / any stdio MCP client (claude_desktop_config.json, .mcp.json, …):
{
"mcpServers": {
"nooticr": {
"command": "npx",
"args": ["-y", "@nooticr/mcp"],
"env": { "NOOTICR_BASE_URL": "https://api.nooticr.com" }
}
}
}
Tools
169 tools, grouped by what you are trying to do. Prices are in nooticr credits and match what the server actually charges.
The job tools under Answer a question you actually have are not endpoint wrappers. Each names a job, fans out over the calls that job needs, groups the evidence by whatever you are deciding about, gives every item an id a follow-up tool can act on, and hands the reading to your model rather than to ours. They fan out, so they cost the sum of what they fetched and every one of them caps that fan-out with an argument.
Read a post
Understand a post
Research a niche or a creator
Answer a question you actually have
Study a market on Amazon
Everything above reads social content. These read a marketplace — the place where the same audience says what it actually bought and why it sent it back. The question they exist for is a category decision: we are considering ashwagandha — what makes people buy, what stops them, what are the incumbents good at, and where is the gap? Every one of those answers lives in review text, so the unit of work is a scan of the category rather than a lookup of a product.
Collection is genuinely slow, and honestly so: each listing is a real browser
render behind Amazon's bot defences, and the pace between them is set by what
Amazon tolerates rather than by what the machine can do. Ten listings with their
reviews is minutes, not seconds — so a scan returns what is ready and hands back
a scanId for the rest.
Make something
Account
Your own product
Everything above reads someone else's content. These read and generate for your own — a different product, a different balance (your workspace's plan AI credits, not the personal MCP credits above), billed exactly like the matching dashboard button.
How billing works
- New accounts get 20 free credits.
- Every tool is priced at what it fetches upstream, and bills from the first
call. A tool that fans out to two fetches costs both —
analyze_postis 2 for the frames plus 1 for the transcript — and its description says so. There is no free first use: that grant belonged to the AI calls, and there are none. - A call that fails is refunded automatically, and a call interrupted mid-flight is billed once at most — retries are idempotent.
- Platform admins bypass credit debiting entirely.
Interactive cards in Claude / ChatGPT chat
Every tool that returns posts also renders inline interactive cards directly
in the chat (MCP Apps ui://nooticr/view resource rendered in a sandboxed
iframe):
- Video posts (TikTok/IG/YouTube/Douyin/LinkedIn) — an inline
<video>player with the thumbnail as poster, playing the re-hosted permanent MP4 (no expiring CDN tokens). - Carousels / slideshows — a horizontally scrollable strip of every slide with an image count chip.
- Single images — inline thumbnail.
- Text-only posts (LinkedIn / X) — a styled quote block of the post text.
- Official brand marks — each card shows the platform's real logo (simple-icons) in its brand color instead of an emoji.
search_mentions renders a different view, because monitoring is triage rather
than browsing:
- Each row is a person saying something. Their picture, with the network's
mark on it, then the handle and when they wrote it — "today at 7:25 PM", not
a timestamp — then the comment with the term highlighted everywhere it
appears, then what it earned in likes and replies. A
×Nbadge marks a comment that names the brand more than once. - Each post carries its reach. The same sentence under a 25K-upvote thread and under a post nobody saw are not the same problem, and nothing else on screen tells you which one you are reading.
- The per-network counts are filters. Click one to narrow to it; a network that answered with nothing is shown but is not clickable, because filtering to it is a dead end. Filtering and sorting redraw from what you already paid for — the view never re-queries.
- A burst collapses. One post with a run of near-identical replies (a coordinated fan campaign, say) shows the first few and offers the rest, so it cannot push four other networks off the screen.
- Comments are selectable, and the selection is agentic. Tick any comments, or select a whole thread at once, and send them — the view hands the host the comment ids the tool issued, so the model can reply to, escalate or analyse exactly those. Selections survive filtering and sorting.
- Load more pages from the
nextOffsetthe tool returned.
Also inline thumbnails render for the model / plain-text clients:
get_social_post/understand_social_post/analyze_post— up to 4 frames inline (poster + carousel slides). The fullmediaItems[].preview_url+thumbnailUrlstay instructuredContentfor the model to reason over.discover_social_posts/get_user_posts/analyze_creator_profile— each returned post shows its thumbnail inline (up to 4 at once) together with its title/caption + views/likes/comments. Say "next" or "show more" — Claude will re-call withoffset/limitpagination. Say "analyze the 2nd one" — Claude callsanalyze_postorunderstand_social_poston that URL.- Batch analysis — ask "analyze all 4" or "understand these 3 in batch" and Claude will call
analyze_post/understand_social_postonce per URL in parallel and summarize. For large batches,discover_social_posts+ a follow-upanalyze_postper URL covers it.
Your own model does the thinking
There is no other mode. Every tool returns the material an analysis would have been built from, at the price of the fetch, and asks your model to reason over it. Nothing here calls a model of ours, so nothing here sells you a judgement.
For the visual tools that means actual frames — real image content blocks, not a description of them — paired with the transcript. Measured on Claude Code: ~1,200 tokens per 1280×720 frame, eight frames read back correctly and in order. Twenty frames is about 2.4% of a million-token context.
The prices above are derived from the fetches each tool makes rather than
written down twice — see EVIDENCE_PLANS and planCost in
src/shared/evidence.ts. evaluate_social_draft has no plan at all: it reviews text you
already have, so there is nothing to fetch and nothing to charge for.
show_comment_review closes the loop: hand back your classifications and it
draws them — every comment with its sentiment and category, filterable and
selectable. Free, and it makes no requests.
Before an expensive call, it asks
Most tools print their price in their own description, so a call costs what you already read. Six do not, because their price is set by an argument — or by a list the request never mentions:
search_mentionsbills per network swept, so a bare "monitor my brand" sweeps all nine for 21 credits.catch_up_watchlistbills per creator, so the price is the length of your watchlist.answer_my_audience,content_ideas_from_audienceandmeasure_audience_overlapbill per post opened (14, 12 and 16 credits at their defaults), so they ask once the real post count is known rather than on a worst case.search_spoken_mentionsbills per network, per handle and per transcript, so it asks on the worst case it could reach.create_brand_watchasks however small the number is, because what is being agreed to is a recurring charge rather than one run.
Above 6 credits those first five ask first, over MCP elicitation — the client
shows the number and you accept or decline. Declining spends nothing and is not
an error. A client that does not support elicitation is not blocked; the call
runs as it always did.
Two rules go further than the threshold. A scheduled watch always asks, because
the standing arrangement is the thing being approved. And a watch that emails a
digest refuses to accept a deliverTo address unless a person actually saw the
dialog and said yes — "nobody could be asked" is a fine answer for a spend and
the wrong one for an address.
Prerequisites
- Node.js >= 18 (tested on Node 22)
- An nooticr account (created automatically on first sign-in — Google sign-in
via
npx @nooticr/mcp login; the dashboard is not required: the server auto-creates a default workspace + app for new accounts) - Access to an nooticr server — defaults to the cloud API
(
https://api.nooticr.com); pointNOOTICR_BASE_URLat your own deployment for local development
Quick start
# 1. Sign in with your nooticr account (Google sign-in opens in your browser)
npx @nooticr/mcp login
# or with email/password:
npx @nooticr/mcp login --email [email protected] --password '...'
# 2. Add it to your MCP client (see install section above) — or run it manually:
npx @nooticr/mcp # stdio (default; for Claude Desktop / Cursor)
npx @nooticr/mcp --http # remote HTTP with OAuth (for OpenAI Agents SDK)
login stores your nooticr tokens in ~/.config/nooticr-mcp/credentials.json
(mode 0600). If your browser cannot be opened automatically, copy the URL it
prints into a browser manually.
Running on a server (no browser)
login and the OAuth flow both need a browser once, and both leave behind a
15-minute access token kept alive by a refresh token. That is the wrong shape
for an integration running server-side: nothing there can complete a consent
screen, and a refresh chain that breaks has to be repaired by hand.
An API key is the other way in. Mint one from a browser at mcp.nooticr.com/dashboard — sign in, name the key, copy it — or from a machine you can sign in on, once:
npx @nooticr/mcp login # the one interactive step
npx @nooticr/mcp api-key create --name "aybee-prod" # prints the key, once
Then give it to the deployment. There is nothing else to configure and nothing to refresh — the key stays valid until you revoke it.
# stdio, as a subprocess of your own service
NOOTICR_API_KEY=nk_... npx @nooticr/mcp
On a server, prefer the environment variable: it is what every container runtime and secret manager already injects, and it leaves nothing on a disk that a redeploy is going to throw away. Where setting one is the awkward part — a laptop, or a client like Claude Desktop that does not inherit your shell — store the key in the credentials file instead:
npx @nooticr/mcp login --api-key nk_...
That checks the key against the server before writing it, so a typo fails
while you are still there to fix it, and then behaves exactly like the
environment variable: no browser, no refresh, valid until revoked. The file
holds one credential, so this replaces any session an earlier login left
behind (and NOOTICR_API_KEY still overrides it).
# or against the remote endpoint, as an ordinary bearer token —
# no /authorize, no PKCE, no redirect URI
POST https://mcp.nooticr.com/mcp
Authorization: Bearer nk_...
Managing them, from the dashboard's API keys card or the CLI:
npx @nooticr/mcp api-key create --name "staging" --expires-in-days 90
npx @nooticr/mcp api-key list
npx @nooticr/mcp api-key revoke <id>
Every command takes --json for scripting. A few things worth knowing:
- The key is shown once, by
api-key create. nooticr stores only a hash of it and cannot show it again;listshows the head (nk_1a2b3c4d…) so you can tell keys apart. - A key carries the scope of whoever created it — their account, and the
workspace they were in (
--workspace-idpicks another one you belong to). - A key cannot create or revoke keys. That is deliberate: it means a leaked key cannot mint a replacement that outlives revoking the original.
--expires-in-daysis optional. Without it a key does not expire on its own.- Revoking takes effect immediately at the API. The hosted remote endpoint caches the "this key is real" answer for up to five minutes, which only ever lets a revoked key see the tool list — every call that touches your account is checked against nooticr-server on the spot.
Usage in Claude Desktop
After npx @nooticr/mcp login, add to claude_desktop_config.json:
{
"mcpServers": {
"nooticr": {
"command": "npx",
"args": ["-y", "@nooticr/mcp"]
}
}
}
Tokens are resolved from the credentials file written by login (or from
NOOTICR_ACCESS_TOKEN). If your client does not inherit your shell environment,
set the env vars explicitly:
{
"mcpServers": {
"nooticr": {
"command": "npx",
"args": ["-y", "@nooticr/mcp"],
"env": {
"NOOTICR_BASE_URL": "http://localhost:8080"
}
}
}
}
Usage in Cursor
In .mcp.json (project root) or ~/.cursor/mcp.json:
{
"mcpServers": {
"nooticr": {
"command": "npx",
"args": ["-y", "@nooticr/mcp"]
}
}
}
After adding the server, run npx @nooticr/mcp login in your terminal — Cursor
spawns the server with your environment, so it picks up the stored credentials.
For remote/HTTP usage, Cursor can connect to the OAuth-enabled HTTP mode with
npx @nooticr/mcp --http running, pointing the server URL at
http://localhost:3457/mcp. Remote clients (including Cursor) discover the
OAuth endpoints from
http://localhost:3457/.well-known/oauth-authorization-server, open the
"Sign in with Google" page, and store the resulting access token.
Usage with OpenAI Agents SDK
Start the HTTP transport:
npx @nooticr/mcp --http --port 3457
Python:
from agents import Agent, Runner
from agents.mcp import RemoteMCPClient
async def main():
async with RemoteMCPClient(
url="http://localhost:3457/mcp",
auth_provider="oidc", # OAuth flow opens your browser once
) as client:
agent = Agent(name="nooticr", mcp_servers=[client])
result = await Runner.run(
agent,
"Analyze this video: https://www.youtube.com/watch?v=dQw4w9WgXcQ",
)
print(result.final_output)
TypeScript (Agents SDK):
import { Agent } from "agents";
import { RemoteMCPClient } from "agents/mcp/client";
const client = new RemoteMCPClient({
url: "http://localhost:3457/mcp",
authProvider: "oidc", // opens the browser for the OAuth flow
});
const agent = new Agent({
name: "nooticr",
mcpServers: [client],
});
const result = await agent.run(
"Analyze this video: https://vm.tiktok.com/abc123/",
);
console.log(result.output);
For a local stdio process with the Agents SDK, use StdioMCPClient (Python:
StdioMCPClient(command="npx", args=["@nooticr/mcp"])).
Command line
nooticr-mcp Start in stdio mode (default transport)
nooticr-mcp --stdio Same as above
nooticr-mcp --http [--port N] Start the remote HTTP transport with OAuth (default port 3457)
nooticr-mcp login Sign in to nooticr via Google in your browser
nooticr-mcp login --email ... --password ... Password login
nooticr-mcp login --api-key nk_... Sign in with an API key, no browser
nooticr-mcp api-key create [--name N] [--expires-in-days D] [--workspace-id W] [--json]
Mint a key for a server with no browser
nooticr-mcp api-key list [--json] List this account's keys
nooticr-mcp api-key revoke <id> [--json] Revoke one
nooticr-mcp --help Show help
Environment variables
How authentication works
stdio mode (Claude Desktop, Cursor): the server uses the first credential
it finds — NOOTICR_ACCESS_TOKEN, then NOOTICR_API_KEY, then the credentials
file written by login. That file holds one credential, whichever login last
wrote: an API key (login --api-key) or a session. A session token that has
expired is refreshed with the stored refresh token, and a 401 from the
nooticr API is retried once after a refresh. An API key is never refreshed
wherever it came from: it does not expire, so a 401 on one means revoked or
mistyped, and retrying would only hide that.
API key (any transport, no browser): send it as the bearer token, or set
NOOTICR_API_KEY. nooticr-server resolves the key to its owner and the
workspace it was scoped to, and every authorization check downstream sees
exactly what it would have seen for a signed-in session. See
Running on a server.
HTTP mode (OpenAI Agents SDK, remote clients): the server runs its own OAuth 2.0 authorization server (Authorization Code + PKCE S256, public client, per the MCP 2025-03-26 spec):
GET /.well-known/oauth-authorization-server— metadataGET /authorize— validates the request (loopback or https redirect URIs) and forwards the browser to nooticr's Google sign-inGET /oauth/callback— our own loopback callback; exchanges nooticr's completion code for nooticr JWTs and redirects back to the MCP client with a one-time codePOST /token— verifies PKCE and issues an opaque Bearer token bound to the nooticr session (valid 1 hour)- every MCP RPC validates the Bearer token against the session map — or, when the bearer is an API key, forwards it to nooticr-server as the caller's own credential, which is what makes this endpoint usable with no browser at all
Supported URLs
- TikTok:
tiktok.com/*,vm.tiktok.com/*(andwww./m.subdomains) - Instagram:
instagram.com/*(reels, posts, carousels),instagr.am/* - YouTube:
youtube.com/*(including/shorts/),youtu.be/*,m.youtube.com/* - X:
x.com/*,twitter.com/* - Reddit:
reddit.com/*,redd.it/* - Weibo:
weibo.com/*,weibo.cn/* - Douyin:
douyin.com/* - Xiaohongshu:
xiaohongshu.com/*,xhslink.com/* - Bilibili:
bilibili.com/*,b23.tv/* - LinkedIn:
linkedin.com/*(posts, profile URLs)
All tools accept these hosts and handle video, image, carousel, slideshow and text posts.
Troubleshooting
Not authenticated with nooticr/ 401: runnpx @nooticr/mcp login, or setNOOTICR_API_KEY(see Running on a server).unknown, revoked or expired nooticr API key: the key is not one this account has live.npx @nooticr/mcp api-key listshows every key and its state, revoked ones included.API keys cannot manage API keys:api-key create/list/revokeneed a signed-in session, which is why they ignore a key inNOOTICR_API_KEYand one stored bylogin --api-key. Runnpx @nooticr/mcp loginfirst.- 402 paywall /
insufficient MCP credits: your nooticr account is out of credits. Prices are listed per tool in Tools — 1 credit for a post lookup or transcript, 2 for discovery and for a tool that makes one fetch, 3 for the two that fetch frames and transcript. Every tool bills from the first call. Top up viacheck_nooticr_credits, or the nooticr dashboard athttps://nooticr.com/settings?tab=billing. - Expired refresh token: the stored refresh token was rejected by the
nooticr server. Run
npx @nooticr/mcp loginagain to re-authenticate. Could not reach the nooticr server:NOOTICR_BASE_URLis unreachable or wrong.- Client shows "Bad Request" or connection errors in HTTP mode: make sure
the port matches
NOOTICR_PUBLIC_URLand that the client fetched a token first (the OAuth flow must complete once in your browser).
Platform submissions
See docs/SUBMISSION.md for ready-to-paste configs and the submission package for Claude Desktop, Cursor, and OpenAI Agents SDK.
The official MCP registry entry com.nooticr/mcp must equal server.json's version after each
release; see docs/registry-publishing.md for the drift check
(npm run registry:check) and the publish script (npm run registry:publish).
Security notes
- The credentials file is written with mode
0600and the directory with0700. - OAuth redirect URIs are restricted to loopback (
http://localhost,http://127.0.0.1,http://[::1]) orhttps://URLs; the/authorizeendpoint requires PKCE (S256). - Authorization codes and PKCE challenges are one-time use and short-lived (in-memory).
- Access tokens are opaque, random, and bound to the in-memory session map; they expire after 1 hour. Restarting the server invalidates all sessions.
- API keys are stored as a sha256 hash, never in the clear, so a database dump cannot be replayed against the API. They are scoped to one account and one workspace, carry their owner's role as it is now rather than as it was when the key was minted, and cannot be used to create or revoke keys.
- Never share your credentials file,
NOOTICR_ACCESS_TOKENorNOOTICR_API_KEY.
Development
npm install
npm run build # tsc
npm test # vitest (66 unit tests, mocked fetch — no network)
Those are unit tests against a stub backend. For real MCP-protocol
end-to-end tests — no Rust, no Postgres, no API key —
npm run test:e2e-smoke:fixture spawns this repo's real built CLI and
drives it over stdio against a pure-Node stand-in for nooticr-server. See
docs/testing/agentic-e2e-testing.md for that plus the tier above it
(npm run test:e2e-smoke against a real nooticr-server). For a real model
driving the server through whole journeys — asserting on the chain of tools
it walks, not on one call — see npm run test:quests and
docs/testing/tool-chaining-quests.md. And for
tests/e2e/agentic-visual*.e2e.ts — every reachable widget view, driven by
a real tool-call result, rendered in a real browser with every host-facing
button actually clicked (npx playwright test tests/e2e/agentic-visual.e2e.ts tests/e2e/agentic-visual-full-app.e2e.ts). That exercise found six real
product bugs, documented in the same doc.
License
MIT