The blocked-response contract is the part worth copying: when a live dependency is missing, the tool returns a structured refusal instead of a plausible number, which is exactly the failure mode you want from a reporting tool. The demo mode makes it testable end to end before any ad account is connected.
A Python MCP server for campaign work that is unusually explicit about its own boundaries. Four tools are the supported surface; other modules in the tree are marked as internal and not to be treated as features. Every response carries a `status` of ok or blocked, the `mode` it ran in, and a reason when it is blocked — so a missing credential returns a structured refusal rather than invented numbers.
- `generate_campaign_report` — deterministic campaign reporting, with live runs able to persist normalised campaign snapshots for replay
- `optimize_campaign_budget` — provider-backed budget optimisation, with decision history retained on live runs
- `create_campaign_copy` — ad copy generation through the configured AI provider
- `analyze_audience_segments` — audience segmentation, demo-only today
- A demo mode toggled with `DEMO_MODE=true` that returns deterministic sample data, which makes contract testing and demos possible with no platform credentials at all
In demo mode, nothing. For live runs, `DEMO_MODE=false` plus platform credentials, and for copy generation an AI provider — `AI_PROVIDER=openai` with `OPENAI_API_KEY` and `AI_OPENAI_MODEL`, or the Anthropic and Gemini equivalents through `ANTHROPIC_API_KEY` or `GEMINI_API_KEY`. Set `SECRET_KEY` and `ENCRYPTION_KEY` for stable live behaviour; without `ENCRYPTION_KEY` the process disables API-key encryption and logs a warning. Python 3.12 or 3.13 with `uv sync`; the documented client entry runs `uv run python -m src.server`. stdio is the only supported transport.
Build from source — clone the repository and build it, then point your client at the binary
