@ounie/mcp
v0.14.0
Published
Model Context Protocol server exposing your Ounie AI Second Brain and the Ounie app store to AI clients (Claude, Cursor, ChatGPT) — ask cited questions, save knowledge, browse and buy per-source brain feeds, and discover + run every agent-callable Ounie a
Readme
@ounie/mcp
A Model Context Protocol (MCP) server that exposes your Ounie AI Second Brain (https://ounie.com) and the Ounie app store to AI clients like Claude Desktop, Claude Code, Cursor, Windsurf, VS Code, and other MCP-capable apps — ask cited questions, save knowledge, and discover + run every agent-callable Ounie app through three fleet-gateway tools.
It speaks MCP over stdio and calls the Ounie REST API on your behalf using a Bearer API key.
Full setup guide (all clients, including remote/ChatGPT): https://ounie.com/docs/mcp
Tools
| Tool | Arguments | Description |
|---|---|---|
| ounie_list_brains | — | List your brains (id, name, emoji, source count). |
| ounie_explore_brains | q?, filter?, category?, sort?, limit? | Discover other people's public brains — including paid listings — from the public brain directory. Filter all\|free\|paid\|per_source\|x402\|forkable\|widgets, sort new\|popular\|price, optional category. Returns each brain's id, public URL, and pricing; any result's brainId works with the ask/search tools. |
| ounie_ask_brain | brainId, question, payWithCredits? | Ask a grounded question; returns an answer + citations. Works on your own brains, public brains (your normal question allowance), and paid listings — a per-question brain returns a payment_required quote first; retry with payWithCredits: true after the user confirms. On a per-source brain the flag does nothing: asking is free across whatever you own (see below). |
| ounie_ask_brain_set | setId, question | Ask a brain set — a primary brain blended with advisor brains — in one answer that tags each citation by source brain. |
| ounie_create_brain_set | primaryBrainId, advisorBrainIds, name? | Create a brain set: your primary brain + 1-4 advisor brains — your own, or public brains found with ounie_explore_brains. Returns the set_… id for ounie_ask_brain_set. |
| ounie_add_to_brain | brainId, content, title?, mode?, sourceUrl?, externalId? | Save a chat result (or any text). mode:'synthesize' (default) connects it into the wiki+graph and makes it citable; mode:'note' stores it verbatim. |
| ounie_add_note | brainId, title, body | Save a text note as a new source (verbatim). |
| ounie_add_link | brainId, url | Save a web link as a new source. |
| ounie_save_asset | brainId, title, description?, type?, status?, links?, tags?, client? | File an output made with the brain (carousel, article, ad…) into its Assets library. The opposite of ounie_add_to_brain: knowledge IN vs output OUT — assets are never ingested, never cited, cost nothing. Owner/editor only. |
| ounie_upload_image | brainId, imageBase64 | imageUrl, filename?, title?, description?, assetId?, client? | Hand Ounie an image you generated and get back a permanent public URL to embed as Markdown in the note you save next. PNG/JPEG/WebP/GIF only (magic-byte sniffed — a claimed mime is never believed); JPEG EXIF is stripped; the image is also filed in the brain's Assets library, so pass the returned assetId on later uploads to group a set. Inline base64 is capped near 2.8 MB — use imageUrl above that. The URL is unguessable but NOT access-controlled: anyone given it can fetch the image even if the brain is private. Owner/editor only. |
| ounie_generate_image | brainId, prompt, size?, filename?, title?, description?, assetId?, client? | Describe an image, get back a permanent public URL, filed in the brain's Assets library. Built for hosts that cannot upload a file — most agents can only put text in a tool argument. size defaults to 1024x1280 (4:5, the Instagram carousel ratio, so slides need no cropping); also 1024x1024, 1024x1536, 1536x1024. Spends credits per image, refunded pool-exact if generation fails. Pass the returned assetId on later calls to group a carousel set. Owner/editor only. |
| ounie_search | brainId, query, payWithCredits? | Retrieve & summarize relevant saved sources. |
| ounie_get_context | brainId, query | Retrieve the raw matching wiki pages without generating an answer — your assistant reasons over them and cites by slug. Cheaper/faster than ounie_ask_brain. Not available on paid brains over REST: use ounie_ask_brain with payWithCredits (per-question), or ounie_brain_catalog + ounie_read_source (per-source). |
| ounie_brain_catalog | brainId | Free. List every source in a per-source brain — id, title, date, whether you already own it — plus the price of one source, the whole archive, and an all-access pass. Start here before buying anything. |
| ounie_read_source | brainId, sourceId | Read one source in full. Works when you own it (single, archive, or an active pass) or when the owner flagged it a free sample. Otherwise returns a payment_required result listing every price. Never charges. |
| ounie_buy_source | brainId, tier, sourceId?, autoRenew?, confirm | Spends credits. Buy one source, the archive, or an all-access pass. confirm: true is required — set it only after telling the user the exact price and getting agreement. |
| ounie_list_apps | q? | Browse the Ounie app store — every public app at <slug>.ounie.com (lead scrapers, enrichment, content & voice studios, and more) with tagline, categories, declared per-action credit prices, and MCP + x402 endpoints. |
| ounie_describe_app | slug | One app's card plus its live tool contract — tool names, descriptions, and input schemas fetched from the app's own MCP server — annotated with credit prices. Free. |
| ounie_invoke_app | slug, tool, arguments? | Run any store app's tool through the fleet gateway. Your OUNIE_API_KEY is the forwarded credential (enable "Use across Ounie apps" in Settings → API keys); each run bills that app's own credit price to your shared balance — refused, never overdrawn, when the balance is short. |
| ounie_check_credits | — | Free. The one shared credit pool that funds apps, the widget, the marketplace and agent runs: the spendable total plus the monthly (expiring), purchased and earned pools, and a top-up URL. Call it before dispatching a paid app run or buying a source. |
| ounie_list_pending_synthesis | brainId, limit? | External synthesis (BYO model): list the sources parked for your model to synthesize (when the brain's synthesis mode is External via MCP). |
| ounie_get_source_text | sourceId | External synthesis: atomically claim a parked source and get its raw text + the synthesis contract (system prompt + JSON shape). First-writer-wins. |
| ounie_submit_synthesis | sourceId, title, markdown, entities? | External synthesis: submit the page you produced; validated against the same contract every house synthesis passes, then woven into the brain (ready, citable). |
| ounie_create_demo | url, businessName?, accent?, category?, variant?, suggestedQuestions?, docs? | Admin only. Build a full sales demo from a URL: a private brain seeded with five grounded docs (FAQ/About/Offerings/Policies/Contact), a chat widget, and a /demo/<slug> pitch page. category (ecommerce|real_estate|restaurant, omit → auto-detected from the site, defaults to ecommerce) tunes the page's accent/copy/sections/stats; variant (proof_first|hormozi_offer|purple_cow, default proof_first) picks the page template. Pass docs ({slug,title,markdown}[], write them with the ounie-brain-docs skill) or omit them to crawl + synthesize house-side. Returns { pitchId, slug, demoUrl, status }; poll ounie_demo_status. Non-admins get a permission error and nothing is created. |
| ounie_demo_status | pitchId | Admin only. A demo's build status: { status, step, demoUrl, docs:[{slug,status}] }. Poll while generating. |
Admin tools (
ounie_create_demo,ounie_demo_status) are hidden from non-admins intools/listand enforced fresh-from-the-DB on every call. Drive them with the/demo <url>(ounie-demo) skill.
Use a public brain as an advisor on your brain
A brain set blends your primary brain (authoritative facts) with up to 4 advisor brains (expert guidance) into one answer, every citation tagged by source brain — and advisors can be public brains you don't own. All from your assistant:
ounie_explore_brains— find the advisor (e.g.{ q: "sales", filter: "free" }).ounie_create_brain_set—{ primaryBrainId: <yours>, advisorBrainIds: [<public brain>] }→set_…id.ounie_ask_brain_set—{ setId, question }→ one blended, dual-cited answer.
Costs one normal question; free public advisors add nothing. Sets also appear in Brain Lab (https://ounie.com/dashboard/lab) and vice versa.
Run any Ounie app (the fleet gateway)
The three app tools are a discovery loop over the whole store — new apps appear to your client automatically, no reconfiguration:
ounie_list_apps { q: "maps" }→ find an app (slug, prices, endpoints).ounie_describe_app { slug: "google-maps" }→ its live tools + input schemas.ounie_invoke_app { slug, tool, arguments }→ run one.
Invocation forwards your own key to the app, so it must have
"Use across Ounie apps" enabled at https://ounie.com/dashboard/settings/api-keys.
Runs bill each app's own credit price to your shared balance; the gateway adds nothing on
top, and app errors (auth_required, insufficient_credits with a top-up URL) pass
through unchanged — fix what the error names and retry. Every app also stays reachable
directly at https://<slug>.ounie.com/api/mcp as a scoped endpoint when an agent should
only see that one app.
Per-source brains (feeds sold by the source)
Some brains are feeds: a daily news brain, a weekly teardown, a research digest — where each source is one edition and the edition, not the answer, is the thing worth paying for. Those are sold three ways: one source, the whole back-catalog in a single payment, or an all-access pass covering everything including future editions.
The catalog is always free. The loop is:
ounie_brain_catalog— see every title, date and price, and what you already own. Free.ounie_read_source— read anything you own, or any source the owner flagged a free sample. Free.ounie_buy_source— spend credits, withconfirm: true, once the user has agreed to the amount. Buying the archive or a pass is usually cheaper than several singles.ounie_ask_brain— free, across everything you own. An active pass asks the whole brain; otherwise questions are answered only from the sources you have.
Two things worth knowing: payWithCredits does nothing on these brains — you buy sources,
not answers — and you should never buy anything merely to ask a question, because asking is
already free within what the user owns.
Paid brains (payWithCredits)
Some public brains are paid marketplace listings (priced in Ounie credits per question).
Asking one without the flag returns a payment_required notice with the price and any free
previews; your assistant should confirm the charge with you, then retry the same call with
payWithCredits: true to spend your prepaid credits. Free previews never need the flag, and
a non-answer ("I don't have info on this") is never charged. Top up credits at
Settings → Billing (https://ounie.com/dashboard/settings/billing).
Prefer ounie_get_context for answering
ounie_ask_brain runs Ounie's own LLM to write the answer; ounie_get_context returns just
the retrieved pages so your assistant writes the answer. When the assistant is already a
capable model (Claude, ChatGPT), prefer ounie_get_context — it's faster and avoids a
redundant second model call. Add a standing instruction to your client so it routes there by
default:
When answering from my Ounie brains, prefer
ounie_get_contextoverounie_ask_brain: callounie_get_context, read the returned pages, and write the answer yourself, citing pages inline as[[slug]]. Only useounie_ask_brainif I explicitly ask Ounie to answer.
External synthesis (bring your own model)
A brain owner can set a brain's synthesis mode to "External via MCP" (Settings → Synthesis model). When they do, Ounie still extracts each new source's text and embeds it house-side, but it does not run its own model to write the wiki page — it parks the source and waits for your model (e.g. Opus inside your own Claude Code / Claude.ai session) to do the synthesis. Ounie never holds your model credentials; your client does the work and pushes the result back through the same contract every house synthesis passes. Embeddings always stay house-side (one vector space), so retrieval quality is unchanged.
The loop, run from your assistant:
ounie_list_pending_synthesis{ brainId }— the sources waiting for synthesis. (Notes and verbatim sources never appear — they're never synthesized on any rail.)ounie_get_source_text{ sourceId }— claims the source (atomic, first-writer-wins within the brain's TTL) and returns its raw text plus the synthesis contract: the exact system prompt to follow and the JSON shape to return.- Synthesize the raw text with your own model, following the returned
contract.systemPromptexactly — produce{ title, markdown (with [[wikilinks]]), entities }. ounie_submit_synthesis{ sourceId, title, markdown, entities }— Ounie validates against the same contract, weaves the page into the brain, embeds it, and rebuilds the graph. The source becomesreadyand citable.
Error semantics:
- 409 (claim conflict) — another client claimed the source first (or it's no longer awaiting synthesis). Re-list and pick another. A claim older than the brain's TTL (default 24h) is reclaimable.
- 422 (invalid synthesis) — your payload failed the contract (e.g. missing
markdown, a too-long title, >20 entities). The response carries the validation issues; fix and resubmit.
A standing instruction for your client:
When I ask you to "synthesize my Ounie queue" for a brain, loop:
ounie_list_pending_synthesis→ for each,ounie_get_source_text, write the wiki page following the returned contract exactly, thenounie_submit_synthesis. Skip any source that returns a 409 and move on.
In Claude Code / Claude.ai, this loop ships as a packaged skill —
skills/ounie-synthesize/SKILL.md in the Ounie repo. It carries the full operational loop plus the
exact house synthesis contract (generated from SYNTH_CONTRACT, drift-gated in CI), so you can just
say "synthesize my Ounie queue" and the model drains the brain end-to-end against the same gate.
UI templates (ChatGPT app / MCP Apps)
Beyond tools, the server advertises the resources capability and serves a small set of
self-contained HTML UI templates (ui://ounie/<name>.html) so Apps-SDK hosts (ChatGPT,
MCP Inspector) render interactive cards inline. Non-Apps clients (Claude.ai, Claude Desktop,
Cursor) ignore resources and just use the text in each tool result — no behaviour change.
| Template | Tool(s) | What it shows |
|---|---|---|
| ui://ounie/brain-picker.html | ounie_list_brains | Grid of brains; tap to select. |
| ui://ounie/source-list.html | ounie_search | Ranked matching sources with links. |
| ui://ounie/saved-toast.html | ounie_add_note, ounie_add_link | Save confirmation. |
ounie_get_context and ounie_ask_brain are text-only by design (no card): the host model
writes the answer from the returned pages. (get_context briefly had an answer card; it was
dropped because the card sat above the streamed answer as an empty frame — the text answer is the
output.) Tool descriptors also carry MCP annotations (readOnlyHint/destructiveHint/
openWorldHint) so hosts label reads correctly.
The templates are defined once in web/ and vendored here as src/ui-resources.ts
(hand-synced copy, like the tool list) since this package can't import from web/. The hosted
POST /api/mcp is also a submitted ChatGPT app (OpenAI Apps directory); domain verification
is served from web/src/app/.well-known/openai-apps-challenge.
Get an API key
Generate a personal key (ounie_live_…) at Settings → API keys
(https://ounie.com/dashboard/settings/api-keys). You'll see the full key once — copy it
into the OUNIE_API_KEY field below.
Environment variables
| Var | Required | Default | Notes |
|---|---|---|---|
| OUNIE_API_KEY | ✅ | — | Bearer key from Settings → API keys. |
| OUNIE_BASE_URL | ❌ | https://ounie.com | Override for local/staging. |
The server exits with a clear error on stderr if OUNIE_API_KEY is missing.
(All protocol traffic uses stdout; logs go to stderr.)
Configure in Claude Desktop
Edit your claude_desktop_config.json
(macOS: ~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"ounie": {
"command": "npx",
"args": ["-y", "@ounie/mcp"],
"env": { "OUNIE_API_KEY": "ounie_live_…" }
}
}
}Configure in Claude Code
claude mcp add ounie --env OUNIE_API_KEY=ounie_live_… -- npx -y @ounie/mcpConfigure in Cursor / Windsurf
~/.cursor/mcp.json (or the in-app MCP settings) uses the same shape as Claude Desktop.
Remote (HTTP) clients
Web clients connect to the hosted endpoint instead of running this package:
URL: https://mcp.ounie.com(https://ounie.com/api/mcp keeps working as the same server.)
- Claude.ai / ChatGPT custom connectors sign in with OAuth — paste the URL, click Connect, and approve on Ounie. No API key needed; the server is a full OAuth 2.1 authorization server (dynamic client registration + PKCE). Manage/revoke connections at Settings → API keys → Connected apps.
- Header-capable clients (e.g. Cursor remote) can instead send
Authorization: Bearer ounie_live_…with a personal API key.
See https://ounie.com/docs/mcp for per-client steps.
Local development
Run from source without publishing:
cd mcp
npm install
npm run build # tsc -> dist/
npm run dev # or: tsx src/index.ts (no build step)Then point a client at the built file:
{
"mcpServers": {
"ounie": {
"command": "node",
"args": ["/absolute/path/to/ounie/mcp/dist/index.js"],
"env": {
"OUNIE_API_KEY": "ounie_live_…",
"OUNIE_BASE_URL": "http://localhost:3000"
}
}
}
}Files
mcp/
├── package.json # @ounie/mcp, type:module, bin, SDK dep
├── tsconfig.json # NodeNext, strict, outDir dist
├── README.md
└── src/
├── index.ts # MCP stdio server: tools (+ annotations) & resources
├── ui-resources.ts # vendored UI templates (ui://ounie/*) — sync w/ web
└── api.ts # typed fetch client for the Ounie REST APIWhen you change a UI template or the tool spec, update both web/ and this package
(src/index.ts tools, src/ui-resources.ts templates) — they're hand-synced.
ounie_search currently maps onto ounie_ask_brain with a retrieval-oriented prompt; it
will repoint to a dedicated search endpoint when one ships.
