Skip to main content
AllMCPs
BrowseBestCategoriesStackCompareToolsGuidesBlog Log in Submit MCP

Stay in the loop

Get new MCP servers and top picks in your inbox.

AllMCPs

The open directory for discovering and installing Model Context Protocol servers.

Explore

  • Browse servers
  • Best MCP servers
  • Categories
  • MCP clients
  • Agent prompts
  • Stack Builder
  • Compare servers
  • Tags index
  • Submit a server
  • Pricing

Learn

  • Guides hub
  • What is MCP?
  • Install guide
  • Troubleshooting
  • Security
  • Blog
  • Blog RSS

Tools

  • All tools
  • Config generator
  • Config validator
  • MCP playground
  • OpenAPI โ†’ MCP
  • Badge generator

For agents

  • API docs
  • Trust & traffic
  • llms.txt โ†— (opens in a new tab)
  • Catalog JSON โ†— (opens in a new tab)
  • Remote MCP โ†— (opens in a new tab)

Company

  • About
  • Contact
  • X (@AllMCPs) โ†— (opens in a new tab)
  • GitHub โ†— (opens in a new tab)
  • Terms
  • Privacy
AllMCPs VerifiedAllMCPs VerifiedFeatured on Nick LaunchesFeatured on Nick LaunchesLaunch Llama NewsletterLaunch Llama NewsletterVerified DR - allmcps.comVerified DR - allmcps.comFeatured on SaaSGrowFeatured on SaaSGrowFeatured on Twelve ToolsFeatured on Twelve ToolsFeatured on Saaspa.geFeatured on Saaspa.geFeatured on Findly.toolsFeatured on Findly.toolsFeatured on Startup FameFeatured on Startup FameFeatured on LaunchKiwiFeatured on LaunchKiwiFeatured on ScrollLaunchFeatured on ScrollLaunchFeatured on DailyPingsFeatured on DailyPingsFazier badgeFazier badgeFeatured on NewTool.siteFeatured on NewTool.siteFeatured on saasfame.comFeatured on saasfame.comDR Checker - Domain RatingDR Checker - Domain RatingListed on Turbo0Listed on Turbo0Launched on LaunchBoard - Product Launch PlatformLaunched on LaunchBoard - Product Launch PlatformList on SimilarlabsList on Similarlabshttps://codetrendy.comhttps://codetrendy.comListed on DevTool.ioFeatured on BuildlistFeatured on BuildlistAllMCPs VerifiedAllMCPs VerifiedFeatured on Nick LaunchesFeatured on Nick LaunchesLaunch Llama NewsletterLaunch Llama NewsletterVerified DR - allmcps.comVerified DR - allmcps.comFeatured on SaaSGrowFeatured on SaaSGrowFeatured on Twelve ToolsFeatured on Twelve ToolsFeatured on Saaspa.geFeatured on Saaspa.geFeatured on Findly.toolsFeatured on Findly.toolsFeatured on Startup FameFeatured on Startup FameFeatured on LaunchKiwiFeatured on LaunchKiwiFeatured on ScrollLaunchFeatured on ScrollLaunchFeatured on DailyPingsFeatured on DailyPingsFazier badgeFazier badgeFeatured on NewTool.siteFeatured on NewTool.siteFeatured on saasfame.comFeatured on saasfame.comDR Checker - Domain RatingDR Checker - Domain RatingListed on Turbo0Listed on Turbo0Launched on LaunchBoard - Product Launch PlatformLaunched on LaunchBoard - Product Launch PlatformList on SimilarlabsList on Similarlabshttps://codetrendy.comhttps://codetrendy.comListed on DevTool.ioFeatured on BuildlistFeatured on Buildlist
ยฉ 2026 Jackalope Digital LLC. All rights reserved.
  1. Home
  2. ๐Ÿ’ป Developer Tools
  3. Lulu Ads โ€” Publisher Concierge
L
Health: Not checked yetWe have not completed a health check for this listing yet.Last checked 8/11/2026, 12:06:04 AM

Lulu Ads โ€” Publisher Concierge

Enrichment pendingWe havenโ€™t run our AI enrichment pass on this listing yet, so the overview, use cases, and FAQ below may be sparse or missing. We work through the catalog over time โ€” check back soon.
View Repository

Self-serve publisher onboarding for Lulu Ads. 70% CPA, disclosed sponsored field.

Quick Install

Automated & IDE Setup

Copy the AI prompt to install this server into Claude Code, Cursor, or another agent โ€” or use 1-click editor setup below.

Add to CursorAdd to VS Code
Manual Client & Custom JSON ConfigExpand JSON โ–พ

Install Config Generator

Choose your client
claude_desktop_config.json
{
  "mcpServers": {
    "lulu-ads-publisher-concierge": {
      "command": "npx",
      "args": [
        "-y",
        "lulu-ads-publisher-concierge"
      ]
    }
  }
}

๐Ÿ’ก Paste into ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows)

Install Directory Badge Claim listing Alternatives๐Ÿ’ป More in Developer Tools

Documentation Overview

lulu-ads

The monetization layer for the agent economy.

Monetize your MCP server or agent tool with one labeled sponsored line.

PyPI npm License: MIT Backend Publisher beta Rev share Lulu MCPs

Quickstart ยท Integrations ยท Guarantees ยท API contract ยท Hosted docs ยท Blog ยท Become a publisher

Lulu, the Lulu Ads narwhal mascot, celebrating on a Tel Aviv billboard โ€” the agent economy has a monetization layer now

70% to publishers ยท CPA only ยท 800ms fail-open ยท 0 prompt injections, by design

Sponsored
โ†‘ live rendered sponsor card โ€” real rotating ad demand, refreshes every ~60s. Any claimed listing can embed this in its own README.

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.

What the SDK ships (a data field)What the host renders (its choice)
config.json
{
  "sponsored": {
    "label": "Sponsored",
    "text": "Direct flights TLVโ€“BKK from $412",
    "url": "https://ads.getlulu.dev/c/9f2a1c"
  }
}

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:

Terminal
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

Terminal
pip install lulu-ads
# or: uv add lulu-ads
# or: poetry add lulu-ads
server.ts
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:

server.ts
export LULU_ADS_PUBLISHER_ID=pub_123
export LULU_ADS_API_KEY=lk_...
server.ts
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:

python
mcp.add_middleware(LuluAdsMiddleware())

TypeScript

Terminal
npm install lulu-ads
# or: pnpm add lulu-ads
# or: yarn add lulu-ads
# or: bun add lulu-ads
server.ts
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:

server.ts
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:

python
result["sponsored"] = await ads.sponsored_slot(
    context={"tool": "search_flights"},
    enabled=user.tier != "paid",  # False resolves instantly, no network call
)
ts
result.sponsored = await ads.sponsoredSlot({
  context: { tool: "search_flights" },
  enabled: user.tier !== "paid",
});

Framework integrations

StackOne-linerDocs
FastMCP (Python), data + widgetenable_lulu_ads(mcp, endpoint_url=...)โ†’
FastMCP (Python), data onlymcp.add_middleware(LuluAdsMiddleware())โ†’
MCP TS SDK, data + widgetawait enableLuluAds(server, { endpointUrl })โ†’
LangChain / LangGraph (Python)middleware=[LuluAdsAgentMiddleware()]โ†’
CrewAI (Python)lulu_crewai.install()โ†’
MCP TS SDK, data onlywithLuluAds(server)โ†’
Runtime owners (chat bots, WhatsApp/Telegram agents)model_output + format_suffix(sponsored)โ†’
Any other runtime / languagesponsored_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.

Don't design UI. Pick one of four predefined, host-native-quality result widgets and map your tool's structuredContent fields into it โ€” the frame, design tokens, and the disclosed SPONSORED strip are fixed by the SDK. The strip renders only when a live sponsored payload exists, always at the bottom, always labeled, with the advertiser's logo (letter-tile fallback when none loads). Your body can't remove or restyle it.

Templates: stat-card (big value + chips + optional condition-keyed atmospheric background), table-card (headed rows, mono numerics, best-row highlight), notice-card (verdict glyph + detail rows), carousel-card (3โ€“8 swipeable option cards).

server.ts
from lulu_ads.widgets import register_result_widget

# after your @mcp.tool definitions:
register_result_widget(
    mcp, "get_weather",
    template="stat-card",
    mapping={
        "eyebrow": "location.name",
        "value": {"path": "temperature_c", "suffix": "ยฐ"},
        "condition": "conditions",
        "chips": [{"path": "humidity_pct", "prefix": "๐Ÿ’ง ", "suffix": "%"}],
        "atmosphere": "weather_code",   # WMO code or words -> sky gradient
    },
    endpoint_url="https://my-server.example.com/mcp",
)

TypeScript: import { registerResultWidget } from "lulu-ads/widgets" โ€” same templates and mapping shape; spread the returned _meta into server.registerTool(...). Mapping entries are dot-paths or {path, prefix, suffix}; a body_html= escape hatch accepts custom markup composed from the .lw-* primitives for the cases the templates don't cover. Calling it for a tool deliberately replaces the generic sponsored card from enable_lulu_ads on that tool โ€” the sponsored data still flows and renders in the widget's own strip.

Widget rendering (MCP Apps UI)

The plain sponsored field always ships and always works โ€” some hosts render it as a card purely on the model's own judgment, no instruction anywhere. For hosts that support the MCP Apps extension (io.modelcontextprotocol/ui), enable_lulu_ads / enableLuluAds (see Quickstart above) already register an actual rendered widget and attach it to every tool automatically โ€” you don't need anything below this line for that. It exists as a distinct step at all because register_sponsored_widget() requires your server's exact public endpoint URL, which LuluAdsMiddleware/withLuluAds alone have no way to know.

Prefer per-tool control (a different widget on different tools, or only some tools get one)? Use the lower-level building block directly instead of enable_lulu_ads:

server.ts
from fastmcp import FastMCP
from lulu_ads.widget import register_sponsored_widget

mcp = FastMCP("my-server")
sponsored_app = register_sponsored_widget(
    mcp,
    endpoint_url="https://my-server.example.com/mcp",  # your public MCP connector URL
    text="Save 15% at checkout",
    url="https://example.com/deal",
    logo="https://example.com/logo.png",  # optional, see "Logos" below
)

@mcp.tool(app=sponsored_app)
def search(...): ...

Same helper, official TS SDK, for MCP servers built in Node instead of Python (registration is async โ€” it may fetch a logo before returning):

server.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { registerSponsoredWidget } from "lulu-ads/widget";

const server = new McpServer({ name: "my-server", version: "1.0.0" });
const appMeta = await registerSponsoredWidget(server, {
  endpointUrl: "https://my-server.example.com/mcp", // your public MCP connector URL
  text: "Save 15% at checkout",
  url: "https://example.com/deal",
  logo: "https://example.com/logo.png", // optional, see "Logos" below
});

server.registerTool("search", { ...appMeta }, handler);

This is also what enable_lulu_ads/enableLuluAds do internally, on your behalf, for every tool โ€” found live (2026-07-26) that getting this step right per-tool is easy to forget: our own dogfood server had it wired onto exactly one tool by hand, and every tool added since then silently never got it. If you want automatic coverage with no per-tool step, use enable_lulu_ads/enableLuluAds instead of this directly.

Ships a floating, rounded, gradient card (same visual system as getlulu.dev) with a disclosed Sponsored label โ€” still just markup, never a directive. Three host-specific quirks this handles for you: Claude requires an undocumented _meta.ui.domain value derived from your endpoint URL (self-computed here, not a credential), the widget must send a ui/notifications/initialized handshake on load or Claude keeps the iframe hidden, and logos are inlined rather than linked (next section) so the widget sandbox's own CSP can't silently drop them. Verified live against production (dali.getlulu.dev/mcp, ext-apps#671), current as of 2026-07-19 โ€” Claude's own rendering of MCP Apps widgets was broken platform-wide before that fix landed, so treat any "should render" claim (including this one, elsewhere) as unverified until you've checked it live in your own host.

The widget shows a shadcn <Skeleton> immediately on load, then swaps to real content only once a live tool call arrives โ€” text/url/logo passed to register_sponsored_widget() are not rendered as initial content; only label/cta/accent* from those options are actually used by the live path (as defaults for fields the wire payload omits, and as the static per-integrator brand theme). On every real tool call, the widget listens for the MCP Apps host's own ui/notifications/tool-result push (a fresh iframe is mounted per call, not reused โ€” "per call, not per tool" is a protocol guarantee, nothing had to be built server-side to get it) and renders with that call's structuredContent.sponsored โ€” live, per-call ad content, not a fixed payload baked in once at registration. A host that never sends the notification keeps showing the skeleton indefinitely (not a fallback ad โ€” see the open gap noted in js/widget-src/src/mcpBridge.ts's InitialOptions docstring); a call with no sponsored field (the normal fail-open case) renders an empty card with only the footer. Card, skeleton, and the "Powered by Lulu Ads" footer are one compiled React/shadcn bundle shared byte-for-byte between the Python and TypeScript SDKs (js/widget-src/, checked in, embedded by both languages), and the footer always renders inside that same persistent card shell, in every state.

Logos

logo takes a URL to fetch a brand mark from, not a URL to embed directly โ€” pass it and the SDK downloads the image right there at registration time and inlines it into the widget as a data: URI. This isn't incidental: the MCP Apps spec has hosts enforce img-src 'self' data: <resourceDomains> inside the widget's sandboxed iframe, and unless you separately declare your logo's domain in that resource's CSP config, a <img src="https://your-cdn.com/logo.png"> gets silently dropped โ€” no error anywhere, the card just renders with a blank slot forever, in every host. data: URIs are always allowed under that same rule, so fetching and inlining server-side sidesteps the whole failure mode โ€” there is no CSP config for you to get right or forget.

A bad or unreachable logo never breaks registration โ€” it's skipped (with a warning log) and the card renders without one, same as leaving logo unset. Only image/png, image/jpeg, image/svg+xml, image/webp, and image/gif are accepted, capped at 200KB (the logo renders at 28ร—28 in the card โ€” there's no reason to ship more than that over the wire).

CLI rendering

Terminals have no widget surface โ€” the model's own text is the only output there is, and it's genuinely the model's judgment call whether to mention the disclosed line at all (never forced, ever โ€” see Guarantees). LuluAdsMiddleware / withLuluAds detect known CLI clients via the MCP clientInfo.name sent at initialize (currently: claude-code, verified live) and, when connected from one, append a bordered plain-text card to content[] in addition to the plain field โ€” still just data, still zero instruction to the model, just formatted so it reads as a distinct block instead of a plain sentence if the model does choose to relay it:

Code
โ•ญโ”€ Sponsored โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚ Search 700+ airlines in one place โ€” Kiwi.com   โ”‚
โ”‚ finds routes other search engines miss.        โ”‚
โ•ฐโ”€ via Lulu Ads โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
โ†’ https://ads.getlulu.dev/c/9f2a1c

Known limitation, disclosed here rather than glossed over: some MCP clients don't forward every content[] block to the model when structuredContent is also present on the same result โ€” an open client-side bug in Claude Code, twice reported and twice closed without a fix (#55677 โ†’ consolidated into #45575 โ†’ auto-closed stale). Live-tested against Claude Code specifically (2026-07-21): with structuredContent present (the shipped default), the boxed card never reaches the model, but the plain sponsored field still does โ€” the model reliably surfaces it as an honest, labeled "Sponsored: ..." line in its own words, 3/3 runs, no issues.

cliTextMode โ€” opt-in fix for the client bug above

We also tested the obvious-looking fix โ€” omit structuredContent so content[] has nothing competing with it โ€” and the result depended entirely on what else was in content[]:

  • Ad alone, no real tool data alongside it: the card arrives every time, but the model flags it as a suspected prompt-injection attempt and warns the user off it, 3/3 runs. Worse than not showing it.
  • Ad alongside a real, complete rendering of the tool's own result: the card arrives every time, the model treats it as an ordinary disclosed ad and mentions it neutrally, 3/3 runs. No suspicion.

So the fix is real, but conditional on your tool's own behavior โ€” which this SDK can't verify for you, hence opt-in, off by default:

python
mcp.add_middleware(LuluAdsMiddleware(cli_text_mode=True))
typescript
withLuluAds(server, ads, { cliTextMode: true });

Turn this on only if your tool's content[] already contains a complete, human-readable rendering of the result on its own โ€” not a placeholder like "see structuredContent". When on, detected CLI clients with no declared outputSchema get structuredContent stripped so content[] (your tool's own text + our card) reliably reaches the model. Tools that declare an outputSchema are never touched by this โ€” stripping structuredContent there would break client-side schema validation outright (confirmed: fastmcp.exceptions.ToolError "outputSchema defined but no structured output returned"), which is a broken tool call, a strictly worse outcome than a dropped card. This SDK never drops structuredContent.sponsored on schema'd tools to chase card visibility, cliTextMode or not.

Until the upstream client bug is fixed, treat the CLI card as "renders reliably once you opt in and your tool qualifies, on top of a disclosure that already works either way" โ€” same verify-in-your-own-host caveat as the widget path above.

Guarantees (enforced in code, not just promised)

GuaranteeHow
A tool call can never break because of adsevery failure path returns None/null; hard 800ms wall-clock timeout (3000ms when the call implies server-side classification)
Always disclosedlabel: "Sponsored" is set by the SDK, never sourced from the response body
No prompt injection, everwe ship a data field; there is no display instruction anywhere in the contract
No PII leaves your servercontext is filtered against an allowlist client-side, before any request is built
Quality-gatedevery creative passes Dali scoring (โ‰ฅ70) before it can fill a slot
Intent, not identitytargeting uses this call's stated context only โ€” no user profiles, no cross-session ID
Misconfigured? Still safemissing credentials โ†’ client is inert, returns None/null, zero network calls

Why not justโ€ฆ

โ€ฆtell the model to mention a sponsor in its reply? Display instructions get MCP servers delisted by registries that scan for injected directives. We ship a plain data object โ€” label, text, url โ€” with no field, anywhere in the contract, that tells a model how to render or phrase anything.

โ€ฆcount impressions and charge per view? An "impression" only exists if a model actually rendered it, and that's unverifiable from the server side โ€” easy to game, hard to audit. We charge CPA only, on a click that redeems a signed, server-verified token. Payment maps to a real user action, not a claim.

โ€ฆscan the conversation to target better? Reading transcripts to target ads is a privacy trap: everything a user says becomes ad-targeting data. We accept six allowlisted context keys โ€” tool, category, query, route, locale, country โ€” stated intent for this call only. No transcripts, no profiles, no PII fields exist in the schema.

How it works

Code
tool call
   โ”‚
   โ–ผ
your tool's own result
   โ”‚
   โ–ผ
POST /slot  (1500ms cap โ€” 3000ms when classifying a raw prompt โ€” allowlisted context only)
   โ”‚
   โ–ผ
labeled data field  { label: "Sponsored", text, url }   โ† attached, never injected
   โ”‚
   โ–ผ
host / model judgment   โ†’   renders it, or doesn't โ€” not our call
   โ”‚  user clicks
   โ–ผ
GET /c/{token}   โ†’   signed redirect, click recorded
   โ”‚
   โ–ผ
advertiser's affiliate rails   โ†’   POST /postback on conversion
   โ”‚
   โ–ผ
70% publisher / 30% Lulu, on the ledger. Earnings accrue to your balance from the first audited conversion โ€” cash out from $100.

Full wire-level detail: docs/contract.md.


Docs: https://getlulu.dev/docs ยท Quickstart ยท API contract ยท Integrations ยท Publisher signup ยท Quality gate: Dali ยท MIT

Changelog

  • 0.8.1 โ€” Result-widget template gallery (supersedes 0.8.0, which briefly shipped on npm with a louder strip design): lulu_ads.widgets / lulu-ads/widgets with four predefined templates (stat-card, table-card, notice-card, carousel-card), design tokens + .lw-* primitives, and the disclosed SPONSORED strip built into the frame (advertiser logo via the slot's new logo_url, letter-tile fallback). register_result_widget() patches an already-registered FastMCP tool in place (or returns the AppConfig for explicit app=).

  • 0.7.4 โ€” Widget: the sponsored card's iframe canvas no longer paints an opaque white box on dark hosts. background: transparent alone is not enough for an embedded iframe: Chromium keeps the canvas transparent only when the embedded document's used color scheme matches the embedder's, and this document declared none (defaulting to light), so dark-themed hosts (e.g. claude.ai in dark mode) forced a white backdrop behind the card. The widget now declares color-scheme: light dark, which resolves to the user's preferred scheme โ€” matching hosts that follow it (claude.ai does by default) on both light and dark themes. Verified empirically against light- and dark-scheme embedding pages. (Also aligns lulu_ads.__version__, which had drifted to 0.7.2 while the packages published as 0.7.3.)

  • 0.7.0 โ€” Two real bugs, found live against a real third-party MCP server behind Claude.ai's remote connector, both fixed:

    • 0% ad delivery on hosts that reconnect per message (confirmed: Claude.ai opens a brand-new MCP session per chat message, not once per conversation). Root cause: this SDK's persistent HTTP connection goes cold on any real idle gap between messages, but only a one-time "have I ever succeeded" check protected the very first call ever โ€” every later cold call still got the tight steady-state timeout and failed. Fixed by re-checking coldness on every call, keyed to time since the last real success, not a permanent latch. Also: the fast steady-state timeout itself was raised 800ms โ†’ 1500ms (Python and TS) โ€” production evidence showed even "warm" calls sometimes measuring 796-802ms, right at the old line rather than comfortably under it.
    • Ad fetched successfully, never seen by the model. FastMCP/the MCP TS SDK build a tool result's content[] once, from the tool's original return value, before LuluAdsMiddleware/withLuluAds ever run โ€” mutating structuredContent alone (the only thing this SDK's own test suite checked) left content[] permanently stale. Confirmed live: the wire response's structuredContent demonstrably had sponsored, but Claude.ai read and reported back from content[], which didn't. Both SDKs now keep content[] in sync whenever it's safe to (a single auto-generated JSON text block); regression tests added for the exact gap that let this ship unnoticed the first time.
    • New: enable_lulu_ads() (Python) / enableLuluAds() (TS) โ€” one call that wires both the data field AND the rendered MCP Apps widget onto every tool automatically, present and future. Existing register_sponsored_widget()/registerSponsoredWidget() + app=/_meta.ui per tool still works and is now documented as the lower-level building block for per-tool control; the gap it left (an easy-to-forget manual step per tool) is exactly what this closes โ€” found live on our own dogfood server, which had wired the widget onto exactly one tool by hand and silently never updated it for tools added since.
  • 0.6.2 โ€” The sponsored card now plays a one-time diagonal light sweep across itself when it settles into the loaded state (a real ad won) โ€” pure CSS (.card-shine in js/widget-src/src/index.css), fires exactly once per mount (not a looping shimmer, since this sits inline in a real chat thread), and respects prefers-reduced-motion. Skeleton and no-fill states are unaffected.

  • 0.6.1 โ€” Corrects a stale 0.6.0 published to npm before dist/ was rebuilt from the merged source (js/ has no prepublishOnly build step) โ€” 0.6.0 is deprecated on npm pointing here. Also fixes README.md and both languages' widget.py/widget.ts docstrings, which incorrectly claimed text/url/logo passed to register_sponsored_widget() render as a fallback "house ad" until a live tool-result arrives; they never do โ€” the widget shows the skeleton indefinitely if a host never pushes it.

  • 0.6.0 โ€” The MCP Apps sponsored widget now shows live, per-call ad content instead of a fixed house ad baked in at registration time: rebuilt in React + shadcn/ui (Card, Skeleton, Button), compiled to a single self-contained bundle shared byte-for-byte by both SDKs (js/widget-src/). The widget shows a skeleton immediately on load, then listens for the MCP Apps host's own ui/notifications/tool-result push โ€” which the spec already delivers once per call, to a fresh iframe per call, with no server-side change needed โ€” and swaps to that call's real structuredContent.sponsored data โ€” the widget shows the skeleton indefinitely if a host never pushes it, not a fallback ad; only label/cta/accent* from register_sponsored_widget()/ registerSponsoredWidget()'s options are actually used by the live path. The "Powered by Lulu Ads" footer renders once, immediately, and is never itself part of the skeletonโ†’card swap. Live-verified against a real host (claude.ai) with a throwaway test server: skeleton renders before the tool call resolves, swaps to the real per-call card once it does, the footer never disappears or reflows during the swap, and two tool calls in the same turn render two fully independent widget instances, each showing only its own call's data โ€” confirming the "per call, not per tool" behavior this feature is built on. (The CTA's ui/open-link redirect โ€” vs. a raw navigation โ€” was re-confirmed by static code inspection and this repo's existing unit tests during this same pass; live click-through capture was attempted but blocked by browser-automation tooling limits reaching inside the host's double-sandboxed iframe, not by any observed product failure.)

  • 0.4.0 โ€” Automatic pre-connect on construction for LangChain's LuluAdsAgentMiddleware, CrewAI's install(), and TypeScript's withLuluAds (matching the FastMCP LuluAdsMiddleware, which already had this). Also: FastMCP's LuluAdsMiddleware and LangChain's LuluAdsAgentMiddleware now additionally warm the async connection pool their awaited sponsored_slot() traffic actually uses โ€” the construction-time warm-up above only ever touched the sync client, a separate pool the async path never touches. LuluAds.async_warm_up() is fired once per instance from a real framework lifecycle hook on the live serving event loop (FastMCP's on_initialize, LangChain's abefore_agent), since a background thread can't safely pre-warm a connection meant for a different event loop. Gated by the same auto_warm_up flag as the sync warm-up (this async path is Python-only โ€” TypeScript's autoWarmUp only ever had one pool to gate). This is the fix that closes the cold-start gap for dali-mcp in production, which consumes the async path. Short-TTL (default 45s) success-only cache in both base clients, keyed on resolved category or a hash of the prompt text. Corrected documentation: the real default timeout is 800ms (fast path) / 3000ms (classify path) adaptive, not a flat 300ms.

  • 0.3.7 โ€” cliTextMode (opt-in, off by default): fixes the Claude Code content[]-drop bug for real, but only for tools whose content[] already stands on its own without structuredContent โ€” live-tested both qualifying and non-qualifying cases, see "CLI rendering". Never touches tools with a declared outputSchema (would break client-side schema validation, confirmed via fastmcp.exceptions.ToolError).

  • 0.3.6 โ€” automatic connection warm-up on LuluAdsMiddleware construction (auto_warm_up, on by default): a genuinely cold first tool call measured 804ms against the 800ms fast-path default โ€” right at the ceiling, not under it. LuluAds itself still never auto-warms (a network call as a constructor side effect is surprising in a general-purpose client), but the middleware is the "one line, zero config" promise, so it warms itself.

  • 0.3.5 โ€” fixed a hardcoded 300ms default timeout_ms on LuluAdsMiddleware that silently dropped real, fillable ads on real network latency โ€” every test in the suite used an instant mock transport, which is exactly why this shipped unnoticed. Default is now None, deferring to LuluAds's own conditional 800ms/3000ms default.

  • 0.3.4 โ€” CLI card gets rounded corners and a "via Lulu Ads" footer (Unicode box-drawing only โ€” a live test against Claude Code confirmed it strips raw ANSI color escapes from tool output before the model sees them, so color was never on the table). Also live-tested and explicitly rejected dropping structuredContent to force content[] through: it does make the card arrive, but the model then flags it as suspected prompt injection and warns the user off it โ€” worse than the status quo, where the plain field still gets surfaced honestly even without the card. See "CLI rendering" for the full writeup.

  • 0.3.3 โ€” CLI-adaptive rendering: LuluAdsMiddleware / withLuluAds detect known CLI clients via the MCP clientInfo.name sent at initialize (currently: claude-code, verified live) and append a bordered plain-text card to content[] for them, in addition to the plain field โ€” terminals have no widget surface, so this is the CLI-safe equivalent of the MCP Apps widget above. Still just data; see "CLI rendering" for the disclosed known limitation on some clients' content[] forwarding.

  • 0.3.0 โ€” register_sponsored_widget() / registerSponsoredWidget() gain a logo option: fetched server-side at registration time and inlined into the widget as a data: URI, so it renders under the widget sandbox's CSP (img-src 'self' data: <resourceDomains>) with no resourceDomains config needed on your part โ€” a raw remote logo URL would otherwise be silently dropped, with no error anywhere. A bad/unreachable logo never breaks registration; the card just renders without one. TypeScript's registerSponsoredWidget() is now async (it may need to fetch the logo before returning) โ€” add await at existing call sites.

  • 0.2.0 โ€” register_sponsored_widget() (Python: lulu_ads.widget, now also TypeScript: lulu-ads/widget, official MCP SDK): registers a real rendered MCP Apps UI sponsored card on your server (not just the plain JSON field), handling Claude's undocumented iframe-domain requirement and the ui/notifications/initialized handshake for you. Generalizes the fix verified live on dali.getlulu.dev/mcp against ext-apps#671. Both SDKs produce byte-identical _meta.ui.domain values for the same endpoint URL.

  • 0.1.1 โ€” persistent HTTP clients in the Python SDK (per-call client construction could burn the entire slot budget on CPU-constrained containers; clients are now created once per LuluAds instance and reused with keep-alive). Fail-open behavior unchanged.

  • 0.1.0 โ€” initial release: Python + TypeScript clients, FastMCP / LangChain / LangGraph / CrewAI / MCP-TS adapters, suffix helpers, MCP concierge onboarding.

Related MCP Servers

View all in Developer Tools View all alternatives
  • Claude Task Master logoClaude Task Master

    AI-powered task management system for AI-driven development. Features PRD parsing, task expansion, multi-provider support (Claude, OpenAI, Gemini, Perplexity, xAI), and selective tool loading for optimized context usage.

    ๐Ÿ’ป Developer Tools7 views
    Compare vs Claude Task Master โ†’
  • A
    Ai Netcafe

    Compare LLM cost & latency on one prompt, translate PDF keeping layout, cited research, make PPTX

    ๐Ÿ’ป Developer Tools0 views
    Compare vs Ai Netcafe โ†’
  • Shadcn Ui Mcp Server logoShadcn Ui Mcp Server

    MCP server that gives AI assistants seamless access to shadcn/ui v4 components, blocks, demos, and metadata.

    ๐Ÿ’ป Developer Tools2 views
    Compare vs Shadcn Ui Mcp Server โ†’
  • World Monitor logoWorld Monitor

    Live global intelligence: real-time markets, conflicts, country risk, chokepoints, energy. 39 tools.

    ๐Ÿ’ป Developer Tools1 views
    Compare vs World Monitor โ†’

Frequently Asked Questions about Lulu Ads โ€” Publisher Concierge

Add the following block to your claude_desktop_config.json under mcpServers: "mcpServers": { "lulu-ads-publisher-concierge": { "command": "npx", "args": ["-y", "Lulu Ads โ€” Publisher Concierge"] } }

AllMCPs Directory Badge

Full Badge Customizer

Showcase your server listing on GitHub or your project documentation. Embed this dynamic SVG badge to highlight official listing status and live engagement.

Badge Style:
Live Dynamic SVG PreviewLulu Ads โ€” Publisher Concierge AllMCPs Directory Badge
Markdown (GitHub README)
[![AllMCPs](https://allmcps.com/api/badge/lulu-ads-publisher-concierge?style=directory)](https://allmcps.com/mcp/lulu-ads-publisher-concierge)
HTML Embed
<a href="https://allmcps.com/mcp/lulu-ads-publisher-concierge"><img src="https://allmcps.com/api/badge/lulu-ads-publisher-concierge?style=directory" alt="Lulu Ads โ€” Publisher Concierge on AllMCPs" /></a>

Technical Specs & Signals

Category๐Ÿ’ปDeveloper Tools
More technical detailsExpand โ–พ
TransportSTDIO
RuntimeNode.js
Views0
Unique ViewsTotal visits recorded for this listing page on AllMCPs.
Installs0
Installs & Copy ActionsTotal times users copied install commands or configuration snippets for this server.
27Quality signal: Emerging ยท 27/100How this signal is calculated โ–พ
Server availabilityNot measured

Not scored for repo-hosted servers โ€” we can't reach the running server, only its GitHub page. Hosted MCP endpoints are health-checked live.

Verified ownership8/20
Documentation & tools11/30
Adoption & activity1/15
Community engagement0/10

A guidance signal from public completeness & health data โ€” not a user rating. New listings start lower and rise as they add docs, get verified, and grow adoption. Signals we can't observe for a listing are skipped, not counted against it.

โ˜… FeaturedMoxie Docs MCP logo

Moxie Docs MCP

MCP & Agent Skills for Automated Documentation, and codebase conventions + context

Explore Server โ†’

Own this project?

This directory is pre-filled from public sources. Claim via GitHub README, site badge, or DNS TXT to get the verified badge and attach your website.

Free dofollow backlink: after claiming, verify your product site and place a dofollow AllMCPs badge โ€” we recheck it stays live.

Claim & get free dofollow

Share & Embed

Add our SVG badge (dark/light directory styles) or embeddable widget to your site.

Explore more

More in ๐Ÿ’ป Developer Tools โ†’Best MCP servers for Developers โ†’Alternatives to Lulu Ads โ€” Publisher Concierge โ†’Install in Claude DesktopInstall in CursorInstall in VS Code