Disclosed sponsored slots for MCP servers and AI agent tools. Ships data, never directives. Fail-open by design.
Project description
lulu-ads
Monetize your MCP server or agent tool with one labeled sponsored line.
Lulu Ads attaches a disclosed, labeled data field to your tool responses. The host model (Claude, Cursor, any agent) decides on its own judgment whether it's relevant enough to surface — we never instruct a model to display anything. If it renders and gets clicked, you earn 70% on CPA. If it doesn't — nobody pays, nothing breaks.
Zero-friction start (MCP)
Add the Lulu Ads MCP server and ask your agent to do the rest:
claude mcp add --transport http lulu-ads https://ads.getlulu.dev/mcp
Then just say:
monetize my server
The agent will fetch the right integration guide for your stack, register a publisher (with your consent), wire up the one-liner below, and verify it produced a live slot.
Quickstart (Python)
pip install lulu-ads
# pip install 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"},
timeout_ms=150,
)
return result
One line for FastMCP servers — credentials come from the environment, the call itself takes no arguments:
export LULU_ADS_PUBLISHER_ID=pub_123
export LULU_ADS_API_KEY=lk_...
mcp.add_middleware(LuluAdsMiddleware())
Quickstart (TypeScript)
npm install @getlulu/ads
// npm install @getlulu/ads
import { LuluAds } from "@getlulu/ads";
const ads = new LuluAds({ publisherId: "pub_123", apiKey: "lk_..." });
result.sponsored = await ads.sponsoredSlot({ context: { tool: "search_flights" } });
Guarantees (enforced in code, not just promised)
| Guarantee | How |
|---|---|
| A tool call can never break because of ads | every failure path returns None/null; hard 150ms timeout |
| Always disclosed | label: "Sponsored" is set by the SDK and cannot be removed |
| No prompt injection, ever | we ship a data field; there is no display instruction anywhere |
| No PII leaves your server | context accepts an allowlisted key set only |
| Quality-gated | every creative passes Dali scoring (≥70) before it can fill a slot |
| Intent, not identity | targeting uses the session's stated intent; no user profiles exist |
| Misconfigured? Still safe | missing creds → client is inert, returns nothing, never crashes |
Framework adapters
Full details, exclude-lists, and every supported stack: docs/integrations.md.
LangGraph / LangChain (langchain>=1.0) — middleware, no manual attach step:
from langchain.agents import create_agent
from lulu_ads.integrations.langchain import LuluAdsAgentMiddleware
agent = create_agent(model, tools, middleware=[LuluAdsAgentMiddleware()])
CrewAI (crewai>=1.9.1) — one call at startup, before you kick off a crew:
import lulu_ads.integrations.crewai as lulu_crewai
lulu_crewai.install()
TypeScript MCP servers — wrap the server once, before registering tools:
import { withLuluAds } from "@getlulu/ads";
withLuluAds(server);
Runtime owners (chat bots, WhatsApp/Telegram agents, anything that owns the final message the user sees) — append a disclosed suffix after generation, as harness code, never as a model instruction:
from lulu_ads import format_suffix
final_message = model_output + format_suffix(sponsored)
Get a publisher ID
Three ways:
- Ask an MCP-connected agent to call the
create_publishertool onhttps://ads.getlulu.dev/mcp - Early beta signup: https://getlulu.dev/publishers
POST https://ads.getlulu.dev/publisherswith{"name", "contact_email", "server_url"}
See docs/quickstart.md for the full walkthrough and
docs/contract.md for the wire-level API.
Docs: https://getlulu.dev/docs · Built by Lulu · Quality gate: Dali
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file lulu_ads-0.1.0.tar.gz.
File metadata
- Download URL: lulu_ads-0.1.0.tar.gz
- Upload date:
- Size: 8.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f33d635be97f54dae138d700d8ce5debaf93c23afc37b772b6b3ec7b9d2a7491
|
|
| MD5 |
2296a11a46f9691e8451eefe1d6c6f12
|
|
| BLAKE2b-256 |
140ca9bb84bfe5cdd7eee66d4ddb4517b4c8a0e25db12ae7bdd413ca6a7edcd1
|
File details
Details for the file lulu_ads-0.1.0-py3-none-any.whl.
File metadata
- Download URL: lulu_ads-0.1.0-py3-none-any.whl
- Upload date:
- Size: 8.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0cb633d5aad8b59217c890309928b3fa0c6eefa741dfdbfe2913842818e0228b
|
|
| MD5 |
4f27a07b1a2bcfef3b44ed5a2ff5ab1d
|
|
| BLAKE2b-256 |
52112e86c0ac7f885ddb57c5fbc66929b6f6ccb3b0398b1031a867c7ae3c0e33
|