This lesson got rebuilt. The one-click OAuth connector we taught before kept breaking for students (login bugs, "Invalid Scope", features gated by Meta's rollout). So now we teach the connection our own agency runs in production: your own Meta App + a System User token that NEVER expires. It takes 15 minutes once, it works for every client you will ever sign, and there is no login window to break. All clicks, no code.

๐Ÿšจ Why We Switched (Read This First)

โŒ The OAuth connector (old lesson)

  • Login window fails for many students ("redirect_uris", "Invalid Scope")
  • Some tools gated by Meta's rollout: video upload and drafts may not work on your account
  • Auth tied to YOUR Facebook login: session hiccups break the whole connection
  • Every fix attempt = remove, reinstall, re-auth, pray

โœ… System User token (this lesson)

  • A machine user that belongs to your Business Manager, not to your login
  • Token set to NEVER expire: configure once, forget forever
  • Full Marketing API: nothing gated, nothing waiting on rollout
  • New client? Assign the ad account to the System User. Done. No new auth.
  • This is how real agencies (including ours) run automation in production

The whole thing is 4 moves:

1 ยท CREATE THE APPYour own Meta app, 3 minutes, no approval needed
โ†’
2 ยท SYSTEM USERA robot employee inside your BM
โ†’
3 ยท THE TOKENGenerated once, never expires
โ†’
4 ยท PLUG INOne paste in Claude Code

๐Ÿ“‹ Before You Start

Setup Progress 0 / 3 complete

๐Ÿงฑ Step 1: Create Your Meta App (~3 min)

Open the developer console

developers.facebook.com โ†’ My Apps โ†’ Create App

Log in with the same profile that admins your BM. First time here? It asks you to register as a developer: confirm your email, done. No review, no waiting.

Pick the type and name it

Use case: Other โ†’ App type: Business โ†’ Name: "[YourAgency] Media Buyer"

When it asks for a Business Portfolio, select YOUR Business Manager. This ties the app to your BM, which is what lets the System User use it later.

Add the Marketing API product

App dashboard โ†’ Add products โ†’ Marketing API โ†’ Set up

Scroll the product list until you see Marketing API, hit Set up. You do NOT need to complete any "quickstart" it offers. The product just needs to be attached to the app.

๐Ÿ’ก "But doesn't my app need App Review?" No. App Review is for apps that other people log into. Your app only talks to YOUR OWN Business Manager through a System User, and for that, Dev Mode + standard access is enough. This is the part 99% of tutorials get wrong.

๐Ÿค– Step 2: Create the System User (~2 min)

A System User is a robot employee inside your Business Manager. It has no password, no login, no 2FA, no "we noticed a new device" emails. It exists only to hold permissions and generate tokens. This is what your connection will run as.

Create it

business.facebook.com/settings โ†’ Users โ†’ System users โ†’ Add

Name it something obvious like "claude-media-buyer". Role: Admin. (Admin role on the system user is what allows full campaign management later. You can tighten it once everything works.)

Assign your app to it

Select the system user โ†’ Assign assets โ†’ Apps โ†’ [YourAgency] Media Buyer โ†’ Full control

This connects the robot to the app you created in Step 1.

Assign the ad accounts (and pages)

Assign assets โ†’ Ad accounts โ†’ select yours โ†’ Manage ad account

Select every ad account you want Claude to control. Do the same under Pages for any page you run ads from (needed to create ads later). Client accounts shared with your BM show up here too.

๐Ÿ’ก The agency superpower: this step IS your client onboarding from now on. Sign a new client, get their ad account shared with your BM, assign it to this System User. Thirty seconds, zero new auth, and Claude sees the new account immediately.

๐Ÿ”‘ Step 3: Generate the Forever Token (~2 min)

Generate

System user โ†’ Generate token โ†’ App: [YourAgency] Media Buyer โ†’ Expiration: Never

Pick your app, set expiration to Never, and check exactly these three permissions:

  • ads_management: create, edit, pause campaigns
  • ads_read: read performance data
  • business_management: see the accounts inside your BM

Copy it ONCE and store it

Meta shows the token one single time. Copy it into your password manager right now, labeled "Meta System User token". If you lose it, you just generate a new one, but save yourself the trip.

โš ๏ธ This token IS the keys to your ad accounts. Anyone holding it can spend your clients' money. Never paste it in Discord, never screenshot it, never commit it to a repo. Password manager or nothing. If it ever leaks: Business Settings โ†’ System users โ†’ your robot โ†’ revoke the token, generate a new one.

โšก Step 4: Plug It Into Claude Code (1 paste)

Open Claude Code in your agency folder, replace PASTE_YOUR_TOKEN_HERE with the token from Step 3, and paste this:

set up my meta ads mcp connection using my system user token.

1. if the old oauth server is still registered, remove it first:
   claude mcp remove meta-ads -s local
   (ignore errors if it doesn't exist)

2. check if `uvx` is installed (run: uvx --version). if it's missing, install uv first:
   curl -LsSf https://astral.sh/uv/install.sh | sh
   then restart the shell so uvx is on the PATH.

3. register the mcp server with my token, user scope so it works in every project:
   claude mcp add --scope user meta-ads --env META_ACCESS_TOKEN=PASTE_YOUR_TOKEN_HERE -- uvx meta-ads-mcp

4. run `claude mcp list` and confirm meta-ads shows as connected.

5. then prove it works: list my ad accounts with names, IDs and currency in a table. do not print my token back to me in any output.

You should see your accounts in a table

Found 3 ad accounts:

Account ID           Name                     Currency
act_88472910045296XX  Joe's HVAC               USD
act_55128990371644XX  Smile Dental             USD
act_10228005507357XX  My Agency Main           USD

That table means the whole chain works: app, system user, token, MCP. There is nothing else to configure, ever. This connection survives restarts, new sessions, new projects, and Meta's login moods.

โš ๏ธ Spend safety, non-negotiable: whenever you ask Claude for anything that creates or changes campaigns, end the request with "create everything PAUSED and show me before activating". The token can move real money. Claude is your media buyer, you are still the director.

๐Ÿ’ฌ Your First Real Prompts (Copy & Paste)

Show me the last 7 days of performance for every active campaign in ad account [PASTE ACCOUNT ID]. Sort by ROAS descending. Flag any campaign with CPA above $50 or ROAS below 1.5.
Look at ad account [PASTE ACCOUNT ID]. Find the top 3 ad creatives from the last 30 days based on ROAS. Tell me what they have in common (format, hook style, CTA) and suggest 5 new creative angles based on what's working.
Create a new campaign in ad account [PASTE ACCOUNT ID] for client [CLIENT NAME]. Objective: leads. Daily budget: $50. Use the existing Page [PAGE ID] and pixel [PIXEL ID]. Create everything PAUSED and show me the full structure before I activate anything.

๐Ÿ”ง Troubleshooting (The Real Errors, With Fixes)

"(#190) Invalid OAuth access token"

The token got pasted wrong (missing characters, extra space) or was revoked. Fix: generate a fresh token (Step 3), then re-run the Step 4 paste with the new one. The claude mcp add command overwrites the old registration.

"(#100) Missing permissions" or an ad account is invisible

The System User cannot see that asset. This is ALWAYS an assignment problem, never a token problem. Go to Business Settings โ†’ System users โ†’ your robot โ†’ Assign assets, and confirm the ad account (and its Page) are assigned with Manage access. Changes apply instantly, no new token needed.

"uvx: command not found" even after installing

The shell has not reloaded its PATH. Close the terminal completely (or VS Code entirely, Cmd+Q / close window) and reopen it. Then re-run the Step 4 paste. On Windows, use the PowerShell installer from the uv install page.

"App not active" or "app is in development mode" complaints

Dev mode is fine for this setup: a System User of the SAME Business the app belongs to has full access to that Business's own assets. If you see this error, the app was created outside your BM. Check App dashboard โ†’ Settings โ†’ Basic โ†’ Business verification section, and make sure the app belongs to your Business Portfolio.

Claude says the meta-ads server is not connected

Paste this in Claude Code:

run `claude mcp list` and show me the status of meta-ads. if it shows as failed, remove it and re-add it with my token using: claude mcp remove meta-ads -s user, then the claude mcp add command from before. then list my ad accounts.

What about the one-click OAuth connector from the old lesson?

It still exists (claude mcp add --transport http meta-ads "https://mcp.facebook.com/ads"), and if it works on your account, fine. But its login flow has been failing for a lot of students, and some tools are gated by Meta's gradual rollout. The System User path in this lesson has neither problem, which is why it is now the recommended route and the one our own agency runs.

๐Ÿท๏ธ Bonus Download: The Ad Naming Skill

Your connection is live. Before you create a single campaign with it, install this. It is the naming system our own agency runs, packaged as a Claude Code skill: the name of every campaign, ad set, and ad is the primary key of your attribution. It flows through UTMs into your CRM and reports. A space, an emoji, or a "- Copy" in a name becomes garbage in every report downstream, forever.

โŒ Without it

  • "Copy of Copy of Leads Campaign 3"
  • The same angle spelled 4 different ways
  • Reports full of %20 and orphan rows
  • Claude invents a new naming style every session

โœ… With it

  • Closed token dictionary, campaign numbers (#001, #002...)
  • Names lint-checked BEFORE anything uploads
  • Everything created PAUSED, url_tags on every ad
  • File name = ad name = Meta library title, searchable forever

Install once (2 minutes)

โฌ‡ Download ad-naming-skill.zip

Then in your terminal:

$ mkdir -p ~/.claude/skills && unzip ~/Downloads/ad-naming-skill.zip -d ~/.claude/skills

Restart Claude Code. From now on, whenever you ask it to create or upload anything on Meta, it names everything by the system, checks the lint, sets the UTMs, and creates it all PAUSED. First run, it interviews you once for your client's product codes and builds a registry file that remembers every hook number and angle you ever use.

๐Ÿ’ก Why this makes the MCP stronger: the MCP gives Claude hands. This skill gives it discipline. Six months from now, when you ask "compare every LEAKY_ROOF_CHECK ad we ever ran", the answer exists because every name was a database key from day one.

โœ… You're In, Permanently

Claude Code is now wired to Meta the way production systems are: your own app, a machine user, a token that never expires. Every other lesson in Module 5 runs on this connection. And your client onboarding just became "assign the ad account to the robot".

Next up: Lesson 5.4, The AI Media Buying Agent.

โ† Back to all lessons