๐ŸŽ Free: The MCP Growth Kit โ€” the 9-tactic playbook + skills your agent can run to grow your MCP โ†’

Don't read this. Have your agent do it.

Copy one prompt, paste it into Claude/Cursor/whatever runs your coding agent โ€” it installs the MCP, registers you, integrates the SDK, and verifies your first slot.

A live Claude.ai chat asking weather-mcp for Tel Aviv's weather and forecast, with a real Lulu Ads sponsored slot rendering in both tool results
Real capture, not a mockup: weather-mcp (one of our own FastMCP servers, monetized with LuluAdsMiddleware) answering a real Claude.ai request, with the disclosed sponsored slot rendering live in both tool results.

Quickstart

Four steps: install, get a publisher ID, integrate, verify.

1. Install

Python:

pip install lulu-ads

TypeScript:

npm install lulu-ads

2. Get a publisher ID

Pick whichever is easiest for your setup โ€” all three produce the same publisher_id / api_key pair.

Option A โ€” MCP tool (zero-friction, recommended if you're already talking to an agent). Add the Lulu Ads MCP server:

claude mcp add --transport http lulu-ads https://ads.getlulu.dev/mcp

Then ask the agent to call create_publisher (it will ask for your consent first, since it registers your email). The tool returns publisher_id, a one-time api_key, and a snippet tailored to your stack. Store the api_key immediately โ€” it's shown once.

Option B โ€” web form. https://getlulu.dev/publishers

Option C โ€” curl.

curl -X POST https://ads.getlulu.dev/publishers \
  -H "content-type: application/json" \
  -d '{"name": "my-server", "contact_email": "you@example.com", "server_url": "https://my-server.example.com"}'

Response:

{"publisher_id": "pub_...", "api_key": "lk_..."}

Set the credentials as environment variables โ€” every SDK entry point (client, FastMCP middleware, LangChain/LangGraph middleware, CrewAI hook) reads them from here by default, so no code needs to hardcode a key:

export LULU_ADS_PUBLISHER_ID=pub_...
export LULU_ADS_API_KEY=lk_...

3. Integrate

Pick the line that matches your stack. See integrations for the full set (LangGraph, CrewAI, TypeScript MCP servers, any custom runtime) and the examples on GitHub for runnable files.

FastMCP server (Python), argument-free โ€” credentials come from the env vars above:

from fastmcp import FastMCP
from lulu_ads.middleware import LuluAdsMiddleware

mcp = FastMCP("my-server")
mcp.add_middleware(LuluAdsMiddleware())

Any Python tool, called directly:

from lulu_ads import LuluAds

ads = LuluAds()  # reads LULU_ADS_PUBLISHER_ID / LULU_ADS_API_KEY from env
result = my_tool(...)
result["sponsored"] = await ads.sponsored_slot(context={"tool": "my_tool"})
return result

TypeScript MCP server:

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

const server = new McpServer({ name: "my-server", version: "1.0.0" });
withLuluAds(server); // call before registerTool โ€” reads env vars

4. Verify

Option A โ€” verify_integration MCP tool. From the same MCP session you used in step 2, call at least one of your own tools once, then ask the agent to call verify_integration with your publisher_id. It checks for a live slot_served event:

{"verified": true, "latest_slot_served": "2026-07-15T..."}

If verified is false, the response includes next_steps (double-check LULU_ADS_API_KEY is set and that the middleware/sponsored_slot call is actually reached).

Option B โ€” call a tool and check the field yourself. Invoke any tool that goes through the middleware/adapter and look for the sponsored key (or, for the TypeScript MCP adapter, _meta["ads.getlulu.dev/sponsored"]) on the result:

result = await my_tool(...)
assert "sponsored" in result  # present when a slot filled; absent on no-fill, never an error

A missing sponsored field is expected and fine โ€” it means no campaign matched, the client is inert (no creds), or the backend didn't respond in time. It's never a broken call.