Tempio

Connect Claude Code to your ad accounts

Tempio10 min read

Claude Code talks to Tempio over the Model Context Protocol. There is no SDK to install and no REST surface to learn: one endpoint, one bearer key, and the same organisation-scoped tools the in-app agent uses. This guide takes you from nothing to a client that can read an account and prepare a change, and it shows what comes back at each step so you can tell whether it worked.

What you need before you start

Three things, and none of them is a developer environment.

A Tempio workspace with at least one platform connection that has finished a sync. A connection that has never synced answers every reading tool with an empty set, which looks like a broken client and is not one.

Claude Code installed and working in a terminal. The command in this guide is its own, and nothing here depends on which project directory you run it from.

Ten minutes, most of which you will spend deciding what the key is allowed to do. That decision is the whole security model, so it is worth the time.

Mint a key, and choose its scopes

Keys live in the product under Settings and then MCP. A key is scoped to exactly one workspace, it is hashed at rest, and it is revocable on its own without touching any other key.

Three scopes exist and they are not a ladder. Every key carries read, whether or not you asked for it. Draft and approve are independent additions, and the check a tool makes at request time is an exact match on the one scope it needs rather than a rank — a key holding approve and not draft can clear a batch at the gate and cannot write one.

  • read covers connections, ad accounts, campaigns, the structure underneath them, the media library, metrics, the list of change requests, and media plans. It alters nothing.
  • draft covers writing change requests, replacing the payload of one that is waiting, and editing media plans. What it writes is a record of an intended change rather than a change.
  • approve covers clearing a batch at the gate, and rejecting one so it can never be cleared. It is opt-in at mint time and the in-app agent never holds it.

Mint your first key with read only. You can confirm the whole connection works, ask the agent every question you had, and add a second key with drafting later. A read key is also the one to hand a client you are still evaluating.

One property is worth restating because it is the thing that makes a shared key safe: the workspace is taken from the verified key, never from a tool argument. No tool accepts a workspace to name, so a client holding one key can read one workspace and nothing else.

Add the server to Claude Code

One command. Replace the key with the one you just minted.

claude mcp add --transport http tempio https://app.tempio.ai/api/mcp --header "Authorization: Bearer YOUR_KEY"

The transport is streamable HTTP. There is no server-sent-events transport to select, and clients that try to negotiate one will fail rather than fall back.

That command is the Claude Code path, and it is the only one. Other MCP clients take the same server as a block of configuration instead. This is Cursor's mcp.json, and any client that can send an Authorization header over streamable HTTP takes the same shape:

{
  "mcpServers": {
    "tempio": {
      "url": "https://app.tempio.ai/api/mcp",
      "headers": { "Authorization": "Bearer YOUR_KEY" }
    }
  }
}

Clients that connect only through an OAuth connector directory need a flow this server does not serve yet, and a bearer key alone will not satisfy them. That is a different setup and it is not this one.

Confirm it connected

Ask the client to list its tools. What you should see is a set whose size depends on the scopes your key carries: a read key surfaces the reading tools and nothing else, and the drafting and approving tools are absent rather than present and refusing. If the whole set is missing, the header is wrong or the key was revoked; if the set is smaller than you expected, the key is narrower than you thought.

Then ask the first real question:

Which ad platforms are connected, and when did each last sync?

The connections tool answers it, and each row carries more than a status. There is a capabilities object on every connection saying what that platform can actually express: which change operations it supports, where budget attaches, which objectives it accepts, what a deletion means on that platform, and whether a change can be dry-run against the platform before it is applied. That last pair matters — on some platforms a removal is irreversible and the entity is gone rather than archived, and the capabilities object is where that is stated rather than in a footnote somebody has to remember.

Read the account

The reading is what changes a working day, so it is worth a few prompts before anything is drafted.

Blended spend and results for the last thirty days, with the per-channel breakdown.

You get the blended figures with each channel underneath them. Money comes back as an integer in minor units with its currency beside it, never as a formatted string, and a blend across two currencies is returned as a labelled estimate rather than silently summed. Ratios are computed when they are read and come back as null when the denominator is zero. Read a null as "there is nothing to divide by", never as a zero — that substitution is the most common way a report ends up lying to the person holding it.

Which campaigns carried the spend last week?

The per-campaign rows come back highest spend first, each carrying a marker that says what the ordering means: whether the figures were natively comparable, converted to compare them, or left raw. The marker is there so that a ranking across accounts in different currencies cannot be read as though it were exact.

Give me the brief for that ad account.

The account brief is the long answer: what the last ninety days covered, the totals, a comparison against the previous twenty-eight days, the mix of objectives, how concentrated the spend is, the rankings by return and by cost per acquisition, and the rhythm across weekdays. It is computed rather than live, so it carries its own age and is flagged once it is more than a week old. Say when it was computed before you quote it.

Draft a change

With a draft-scoped key, ask for the change in words:

Draft a fifteen per cent daily budget increase on the two campaigns with the best return over the last fortnight.

What the client calls is the drafting tool, and what it writes is one reviewable batch of change requests. A batch can carry up to fifty entries and a longer one is refused rather than quietly truncated. Where entries depend on each other — a campaign, the group inside it, the ad inside that — each child names the index of the entry that creates its parent, so nothing carries an identifier that does not exist yet.

What comes back is the batch, a link to the gate for the whole group, and the authority that cleared it:

{
  "correlationId": "3c7d5a90-1b48-4e62-a0f5-9d6c2e18b473",
  "reviewUrl": "/drafts?batch=3c7d5a90-1b48-4e62-a0f5-9d6c2e18b473",
  "approvedBy": "pending",
  "drafts": [
    {
      "id": "5a0e7c21-98d4-4b3f-8607-c1f2a9b40d56",
      "operation": "update_budget",
      "targetLevel": "campaign",
      "status": "pending_approval",
      "batchSeq": 0,
      "approverKind": null,
      "warnings": []
    }
  ]
}

Read three fields and you know where you stand. The status says the row is waiting. The authority field says pending when the batch is waiting for a person and policy when a rule the workspace wrote cleared every row of it on the way in — those are the two authorities there are, and the row says which one it got. The review link is the gate for the whole group rather than for one row, so it is the thing to hand to whoever you drafted for.

Two details the guide would be dishonest to leave out. Money in a drafted payload is minor units, and which currency it is in is not yours to state: the server stamps it from the target account and overwrites whatever arrived. And a create is drafted with the entity paused, so the structure and the decision to start delivery are two rows carrying two authorities rather than one act with a side effect.

Worth stating once in full, because this is the step where it gets assumed. Your connections read your accounts. A change the client asks for is written as a draft on the record, with the state it would change from and the checks it passed. Applying it is gated on approval — by you, or by a rule you wrote — and no platform write reaches a customer account until that platform has granted write access. Applying a cleared change to a platform is in private beta.

At the gate: approve, reject, replace

Approving takes either the batch identifier or a list of individual rows, and moves them whole or not at all. The rows are claimed at approval, and the record is what you read afterwards rather than the response you got: the status on each row, the authority that cleared it, and whatever warnings it carried. Approving the same batch a second time picks up the rows that are not yet cleared instead of starting the batch over.

Rejecting is terminal. A rejected row can never be cleared, by you or by a rule.

A draft that needs a change does not have to be rejected and re-drafted. Replacing its payload re-validates it, re-stamps the currency, re-runs the guardrails, and a row that failed its checks goes back to waiting. An undo has the same shape as anything else here: the inverse is drafted at the same gate, with the same guardrails and the same two authorities. There is no separate rollback path, and that is deliberate.

When it does not work

If the client reports no tools at all, the Authorization header is the first thing to check, and a revoked key is the second. If it reports fewer tools than you expected, the key's scopes are narrower than you thought; mint a new one rather than editing the old.

If a reading tool answers with nothing, check that the connection has finished a sync. An account that has never synced is empty rather than broken, and the two look identical from the client.

If requests start failing under load, you have met the rate limit: a hundred and twenty requests a minute per address before authentication, sixty a minute per key after it. An agent looping over campaigns one at a time will find that ceiling; asking for the rows in one call will not.

What you have when this is done

A client that can answer questions about every connected account in one exchange, and prepare changes that wait for an authority rather than arriving as a surprise. The reading gets faster and the authority for a change stays exactly where it was: approved by you, or by a rule you wrote, with the record saying which.

The reasoning behind that arrangement — why the unit is a change request rather than a change, and what it means for a rule to be an authority — is in Why every change is a draft.

Read next

Questions

Put frontier AI to work on every ad account you run.

Tempio connects your ad accounts into one workspace and hands your own AI the numbers and the tools. Every change it writes is a draft until you approve it, or until a rule you wrote does.

Request early access
  • Read and draft by default. Approve is a separate permission you grant.