lulu-ads
Monetize MCP servers and AI agent tools with disclosed sponsored slots. Ships data, never directives. Fail-open by design.
Install / Use
claude mcp add Lulu-The-Narwhal -- npx -y github:Lulu-The-Narwhal/lulu-adsIf the server publishes to npm under a different name, use that package instead — check the repo README.
MCP Server
Model Context Protocol server
Quality Score
Category
MarketingSupported Platforms
Skill content
View source on GitHublulu-ads
The monetization layer for the agent economy.
Monetize your MCP server or agent tool with one labeled sponsored line.
Quickstart · Integrations · Supported hosts · Supported surfaces · stdio servers · Guarantees · API contract · Hosted docs · Blog · Become a publisher
<img src="https://raw.githubusercontent.com/Lulu-The-Narwhal/lulu-ads/master/assets/lulu-ads-hero.jpg" alt="Lulu, the Lulu Ads narwhal mascot, celebrating on a Tel Aviv billboard — the agent economy has a monetization layer now" width="640" />70% to publishers · CPA only · 800ms fail-open · 0 prompt injections, by design
<br><sub>↑ live rendered sponsor card — real rotating ad demand, refreshes every ~60s. Any claimed listing can embed this in its own README.</sub>
Lulu Ads attaches a disclosed, labeled data field to your tool's own result. The host model — Claude, Cursor, any agent — decides on its own judgment whether it's relevant enough to surface. We never instruct it to.
<table> <tr> <th>What the SDK ships (a data field)</th> <th>What the host renders (its choice)</th> </tr> <tr> <td>{
"sponsored": {
"label": "Sponsored",
"text": "Direct flights TLV–BKK from $412",
"url": "https://ads.getlulu.dev/c/9f2a1c"
}
}
</td>
<td>
</td> </tr> </table>Sponsored — Direct flights TLV–BKK from $412 ads.getlulu.dev/c/9f2a1c
Zero-friction start — add the MCP server and let your agent do the rest:
claude mcp add --transport http lulu-ads https://ads.getlulu.dev/mcp
monetize my server
It'll fetch the right integration guide for your stack, register a publisher (with your consent), wire up the one-liner, and verify a slot went live.
If it renders and gets clicked, you earn 70% on CPA. If it doesn't — nobody pays, nothing breaks.
No prompt injection — we ship a data field; the host decides.
Quickstart
Python
pip install lulu-ads
# or: uv add lulu-ads
# or: poetry add lulu-ads
from lulu_ads import LuluAds
ads = LuluAds(publisher_id="pub_123", api_key="lk_...")
result = search_flights("TLV", "BKK", dates)
result["sponsored"] = await ads.sponsored_slot(
context={"tool": "search_flights", "category": "travel.flights"},
)
return result
FastMCP servers get it in one call — credentials come from the environment,
and every tool (present and future) gets both the plain sponsored data
field AND, in hosts that support it (e.g. Claude.ai), the rendered
Sponsored-card widget, automatically:
export LULU_ADS_PUBLISHER_ID=pub_123
export LULU_ADS_API_KEY=lk_...
from lulu_ads.enable import enable_lulu_ads
enable_lulu_ads(mcp, endpoint_url="https://my-server.example.com/mcp")
Just want the data field, no widget? The plain middleware still works on its own:
mcp.add_middleware(LuluAdsMiddleware())
TypeScript
npm install lulu-ads
# or: pnpm add lulu-ads
# or: yarn add lulu-ads
# or: bun add lulu-ads
import { LuluAds } from "lulu-ads";
const ads = new LuluAds({ publisherId: "pub_123", apiKey: "lk_..." });
result.sponsored = await ads.sponsoredSlot({ context: { tool: "search_flights" } });
MCP servers built on the official TS SDK get the same one-call treatment — data field AND widget on every tool, automatically:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { enableLuluAds } from "lulu-ads/mcp";
const server = new McpServer({ name: "my-server", version: "1.0.0" });
await enableLuluAds(server, { endpointUrl: "https://my-server.example.com/mcp" });
No publisher ID yet? See docs/quickstart.md — three
ways to get one, none of them gated on the others.
Tiered pricing (ads on a free tier, ad-free on paid)? Pass enabled —
your own subscription check decides the value, no separate deployment or
scattered conditionals needed:
result["sponsored"] = await ads.sponsored_slot(
context={"tool": "search_flights"},
enabled=user.tier != "paid", # False resolves instantly, no network call
)
result.sponsored = await ads.sponsoredSlot({
context: { tool: "search_flights" },
enabled: user.tier !== "paid",
});
Framework integrations
| Stack | One-liner | Docs |
|---|---|---|
| FastMCP (Python), data + widget | enable_lulu_ads(mcp, endpoint_url=...) | → |
| FastMCP (Python), data only | mcp.add_middleware(LuluAdsMiddleware()) | → |
| MCP TS SDK, data + widget | await enableLuluAds(server, { endpointUrl }) | → |
| LangChain / LangGraph (Python) | middleware=[LuluAdsAgentMiddleware()] | → |
| CrewAI (Python) | lulu_crewai.install() | → |
| MCP TS SDK, data only | withLuluAds(server) | → |
| Skybridge (TypeScript) | withLuluAdsSkybridge(server) | → |
| Runtime owners (chat bots, WhatsApp/Telegram agents) | model_output + format_suffix(sponsored) | → |
| Any other runtime / language | sponsored_slot(context) over the raw contract | → |
Result widgets — templates for your OWN tool output (0.8.5)
One widget, every host. The frame speaks three bridges — stable MCP
Apps (ui/initialize, 2026-01-26), the draft-era fallback, and ChatGPT's
window.openai — and the SDK registers both template keys
(_meta.ui.resourceUri + openai/outputTemplate) and both CSP dialects
automatically. Verified rendering live on claude.ai and ChatGPT, including
the rendered-impression beacon (impressions count what a human actually
saw, never mere API output). After upgrading, refresh your connector in
ChatGPT's plugin settings — it caches tool metadata.
The SPONSORED strip's own format is separately configurable via
sponsor_template= (independent of template=, this widget's own body
layout):
register_result_widget(
mcp, "search_flights",
template="table-card",
mapping={"rows": "flights", "columns": [...]},
endpoint_url="https://my-server.example.com/mcp",
sponsor_template="flip-card", # or "carousel", "scratch-reveal" -- "card" is the default
)
Supported hosts
The plain sponsored JSON field is the always-on baseline: it ships on
every tool result, on every MCP host, because it's nothing more than an
extra key on a dict — no host-specific support is required for it to work,
and the model decides on its own whether to surface it. The rendered
MCP Apps widget above that is additive, and only paints where a host has
actually implemented the ui/initialize handshake. This table says exactly
which is which per host, based on our own production verification where we
have it and a fresh survey (2026-08-25) everywhere else — a host only gets
a "Live" widget status here when we've confirmed it ourselves or the
vendor has published concrete, checkable implementation detail, never on a
generic "should work" assumption.
<sub>Hosts we've looked at — logos are not a support claim on their own; read the Status column below for what each one actually does. (Continue.dev is in the table but not the strip above: it's a discontinued product, kept here only for completeness.)</sub>
| Host | MCP tool-calling | Rendered widget | Status |
|---|---|---|---|
| Claude (claude.ai) | Yes | Yes | Live, verified in production — real rendered-impression beacons observed on live traffic. |
| ChatGPT | Yes | Yes | Live, verified in production. |
| CopilotKit (@ag-ui/mcp-apps-middleware) | Yes | In progress | Fix in review, PR #8, unverified end-to-end — a tool-discovery bug was found and fixed, but the fix has not been tested against a full chat UI (no LLM available in that pass) and is not yet released to npm/PyPI. Do not treat CopilotKit as supported until that PR lands and is verified live. The plain sponsored field is unaffected by this bug and already flows today. |
| VS Code (native MCP + GitHub Copilot Chat agent mode) | Yes | Reported live | Microsoft's own 2026-01-26 blog post and current docs describe VS Code as "the first major AI code editor with full MCP Apps support" and document concrete, checkable implementation detail (sandboxed iframes, CSP domain config, the ui/initialize handshake, the App SDK) — credible, but this is a vendor claim we have not independently reproduced ourselves. Plain MCP tool-calling (Copilot Chat agent mode) has been GA since v1.102. |
| Cursor | Yes | Reported, unverified | Named as an MCP Apps implementer on the upstream modelcontextprotocol.io Extension Support Matrix — a third-party listing, not Cursor's own docs, so weaker evidence than VS Code/Goose's vendor-published detail above. We actually tried to verify this ourselves live (2026-08-25) and got blocked before
Truncated for display — read the full file on GitHub.
Related Skills
Agent-Reach
79.0kGive your AI agent eyes to see the entire internet. Read & search Twitter, Reddit, YouTube, GitHub, Bilibili, XiaoHongShu — one CLI, zero API fees.
ruflo
71.8k🌊 The original agent meta-harness. Deploy intelligent multi-player swarms, coordinate autonomous workflows, and build conversational AI systems. Features adaptive memory, self-learning intelligence, RAG integration, and native Claude Code / Codex / Hermes and many more Integrated
headroom
71.0kCompress tool outputs, logs, files, and RAG chunks before they reach the LLM. 20% fewer tokens for coding agents, 60-95% fewer tokens for JSON, same answers. Library, proxy, MCP server.
CowAgent
46.9kOpen-source super AI assistant & Agent Harness. Plans tasks, runs tools and skills, self-evolves with memory and knowledge. Multi-agent, multi-model, multi-channel. Lightweight, extensible, one-line install. (formerly chatgpt-on-wechat)
