Open source MCP server · MIT
Daily budget moves for your own ad accounts, proposed and never applied
PULSE reads Meta and Google Ads, compares your campaigns and writes a budget plan with its evidence. You read it in Claude, ChatGPT or Grok, and you make the change yourself.
Today's play, synthetic healthy account
| Campaign | Daily budget | Proposed | ROAS 30d |
|---|---|---|---|
| example-aDamped marginal-ROAS allocation, capped at the 20% move limit | $100 | $120 ↑ | 4.00 |
| example-bDamped marginal-ROAS allocation, capped at the 20% move limit | $100 | $80 ↓ | 1.80 |
| Total | $200 | $200 | conf. 0.35 |
Both moves: damped marginal-ROAS allocation, capped at the 20% move limit. Confidence 0.35.
It cannot touch your spend
There is no pause, mutate or apply tool. A proposal is a set of instructions you can carry out in Ads Manager or Google Ads. Recording a move as applied records your report; it does not perform the action.
What you get
An MCP server with seven tools. Your AI client explains the numbers; PULSE itself makes no model calls.
- Every move with its evidenceObserved window, conversions, CPA, ROAS, confidence and the reason, for each campaign.
- Local or remoteClaude Code and Claude Desktop over stdio; Claude connectors, ChatGPT and Grok over self-hosted Streamable HTTP.
- Read-only grantsMeta
ads_read. Google Ads restricted to tested read endpoints and fixed GAQL queries. - Your machine, your keysCredentials stay under
~/.config/pulsewith owner-only permissions. - Nothing else to sign up forNo Finsi account, database, warehouse or model-provider key.
Why not just ask Claude to read the API?
You can. The difference is what decides the number.
With PULSE, a tested, deterministic core computes every move and your AI client explains it. Asked freeform, the model computes the move itself, and can differ from one run to the next.
- Same input, same answerNo model calls in the core, and an explicit seed and clock, so a play can be re-run and audited.
- A fixed, honest window30 days ending two days before today, so late conversions do not make yesterday look broken. Missing numbers stay missing; they never become zeros.
- Capped movesCurve moves are damped by confidence and capped at 20%. Proposals never exceed your total budget, and rounding never adds money.
- It knows when not to moveLearning campaigns, shared or lifetime budgets, thin data and protected campaigns are held, with the reason stated.
- Exact moneyEach amount carries its currency, Meta budget offsets follow Meta's published table, and thresholds use your account currency.
Four synthetic accounts report the same 30-day ROAS for their two campaigns, 4.00 and 1.80, and PULSE gives four different answers: a capped move, a learning hold, a shared-budget hold and a thin-data hold. On the same five fixtures, 10 freeform Claude runs each, 40 of 100 budget decisions moved a budget by more than 20%. Read the benchmark, its method and its caveats.
Try it with synthetic data
Node 22 or later. No credentials needed for the demo.
git clone https://github.com/finsilabs/pulse.git
cd pulse
npm ci
npm run build
node dist/index.js smoke --demo
Then connect your own accounts with node dist/index.js login meta or login google, following platform credentials and the client guides.
Tools
| Tool | What it returns |
|---|---|
pulse.accounts | Accounts your grant can see, with currency and timezone |
pulse.snapshot | Validated daily campaign metrics and observed budgets |
pulse.todays_play | Scale, shift, hold, watching and warnings |
pulse.explain_move | Evidence, window, target, cooldown and clamps for one campaign |
pulse.propose_move | An expiring action plan, always applied: false |
pulse.config | Bounded targets, caps, protected campaigns and ceilings |
pulse.record_decision | Your reported applied or dismissed decision, for cooldowns |
What has been verified
The offline gate passes: rules and invariants, replayed synthetic Meta and Google responses, all seven tools, real stdio and Streamable HTTP transports, and OAuth with PKCE. A read-only live smoke against five real Meta ad accounts passed on 2026-11-06 and nothing was changed in them; it also caught and fixed a Meta pagination bug. Google Ads live checks and native-client screenshots are still to come.
These are test results, not a revenue-lift claim. Validate every recommendation against your own business before you act on it. See the verification record.