Meta is read-only. Never pause, never spend.
Alex, 23 Aug 2026: *"i dont want you to be pausing anything. just read meta. this is important to prevent mistakes."*
The guarantee is structural, not a policy
The system user token carries ANALYZE on every client account — Meta's read-only analytics permission. Verified directly:
AtlasandBone tasks: ['ANALYZE']
Damien's Ad Account tasks: ['ANALYZE']
A write probe against a live ad returns Unsupported post request… cannot be loaded due to missing permissions. There is no pause path to disable because the capability does not exist.
That is the right place for this to live. A policy can be forgotten by whoever writes the next feature; a permission cannot.
What this forbids
Never request MANAGE or ADVERTISE on a client account, and never add a Graph write for pausing, budget changes, status changes or ad creation — not behind a confirmation, not behind a feature flag, not "just for testing". The failure mode of an automated budget change is silent and expensive: it happens to somebody else's money, at a scale nobody notices until the invoice.
The one existing POST in the codebase (graphPost) grants a system user *read* access to an account and touches nothing else. It lives in reconcileAssignments and is reachable only through ?action=reconcile.
What the system does instead
src/lib/services/media-buying.ts produces recommendations — pause this, scale that, this one is saturating — each carrying the arithmetic that produced it. A person reads them and decides.
The failure modes are not comparable. An automated change that is wrong costs money quietly. A recommendation that is wrong gets argued with, which is the outcome you want from a machine that cannot see the conversation the client had last week.
On moving Meta to MCP — 24 Aug 2026
Alex asked whether to swap the Graph API for Meta's MCP, since other agent products have. There are two different Meta MCP servers and they answer different questions.
Devtools MCP (developers.facebook.com/documentation/mcp/devtools-mcp) is for people *building on* Meta's platform: exploring the Graph API, debugging errors, checking app health. It is a developer-assistance tool, not a data path. Useful to whoever is writing the sync; irrelevant to what the sync does at 3am.
Meta Ads AI Connectors (mcp.facebook.com/ads, open beta since 29 April 2026) is the real candidate: 29 tools over reporting, campaign management, catalog management and signal diagnostics, authenticated with Meta Business OAuth.
It should not replace the sync
The portal's Meta need is a scheduled job that pulls ad-level rows and writes them to Postgres. That is batch ETL, and the Graph API is the right shape for it: deterministic, paginated, cheap, and no model in the loop. MCP is built for an agent choosing tools conversationally, and putting it in the nightly path would mean a model call inside the high-volume path — the thing docs/rules/cost.md exists to prevent — plus an OAuth session to keep alive and non-deterministic tool selection over data we already know how to ask for.
And it would weaken the guarantee, unless authorised carefully
The read-only property here is structural: the token carries ads_read and not ads_management, so pausing and spending are not capabilities we decline to use, they are capabilities we do not have.
The Ads MCP ships campaign-management tools. Connecting it under a management-scoped OAuth grant would turn that structural guarantee back into a policy — "do not call those tools" — which is precisely the failure this rule exists to prevent, and the kind a future session forgets.
If it is ever connected, authorise it with read-only scopes. The management tools then exist in the manifest and fail at the permission layer, and the guarantee stays where it belongs.
Where it would genuinely earn its place
Not in the sync — in an analyst surface. A founder asking "why did CPM jump on Atlas & Bone last week" is an agentic, exploratory, read-only question, and that is exactly what MCP is good at. That is a real future feature. It is a different feature from the nightly pull, and it should not replace it.