[BUG] Meta Ads MCP: ads_get_ad_entities rejects every read with "OTID is a required input" — but otid is not an accepted argument

Resolved 💬 1 comment Opened Jun 17, 2026 by franciscooteiza Closed Jun 25, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

Summary
As of 2026-06-17, the official Meta Ads MCP connector (mcp.facebook.com/ads, Marketing API v25.0) rejects every call to ads_get_ad_entities with an input-validation error demanding an OTID:

Invalid input: required input information was missing, malformed, or incorrect
for the tool call. OTID is a required input and cannot be empty or malformed.
This is new — the same calls worked previously, and no Meta Ads MCP troubleshooting guide documents an OTID requirement. It breaks all read access to ad metrics through the connector.

The core problem (a contradiction the caller can't resolve)
otid is not an accepted argument of ads_get_ad_entities. The tool's own schema lists only ad_account_id, fields, level, limit, breakdowns, date_preset, filtering, sort, time_increment, time_range. So:

Omit otid → passes argument validation → fails a second check: OTID is a required input and cannot be empty or malformed.
Add otid (e.g. a UUID) → rejected immediately: Unknown argument(s): otid. Allowed argument(s): ad_account_id, fields, level, limit.
There is no value the caller can pass to satisfy this. The OTID must be injected by the MCP host/runtime in the request envelope, and right now it isn't being sent — so the read is unsatisfiable from any client.

Why this looks like a bug, not intended behavior
In Meta's Marketing API, otid is a create-time idempotency token used on write endpoints (e.g. POST /act_<id>/ads) to dedupe retried object creation. Requiring it on a read entity query has no semantic basis — it appears a write-path required-field check is leaking onto the read path (or a rollout step now expects the host to mint an OTID per call and the host isn't doing so).

Reproduction
Connect the official Meta Ads MCP (mcp.facebook.com/ads).
Call ads_get_ad_entities on any MCP-enabled, queryable account:
{
"ad_account_id": "<ad_account_id>",
"level": "campaign",
"fields": ["id", "name", "effective_status"],
"limit": 5
}
Actual: … OTID is a required input and cannot be empty or malformed. Expected: the campaign rows (as before 2026-06-17).
Retry with "otid": "<any-uuid>" added → Unknown argument(s): otid.
Scope (what still works — so it's isolated to this tool)
On the same account, in the same session:

ads_get_ad_accounts → ✅ works (account reports is_ads_mcp_enabled: true, is_queryable: true, not_queryable_reason: null)
ads_get_field_context → ✅ works
ads_get_ad_entities → ❌ OTID error (above)
(Unrelated Shopify MCP in the same artifact → ✅ works)
So the account is healthy and the connector is reachable; only the metrics/entities read tool is affected.

Environment
Connector: official Meta Ads MCP, mcp.facebook.com/ads, Marketing API v25.0
Host: Claude Cowork (live artifact calling the connector via the host bridge) — also reproduces from the Claude Code tool surface directly
Account: a valid, MCP-enabled, queryable Meta ad account (USD), owning business attached
First observed: 2026-06-17
Impact
ads_get_ad_entities is the only read tool for ad metrics, so this takes out all live spend/CPM/CPC/CTR/impressions reporting through the connector — including any artifact or agent that joins Meta spend with other sources. Campaign discovery (level: campaign) is also dead, so downstream tooling falls back to stale/seed data.

Requested fix
Either (a) stop requiring otid on read calls to ads_get_ad_entities, or (b) have the MCP host inject a valid OTID into the request envelope automatically (the caller cannot, since it isn't an accepted tool argument).

What Should Happen?

Either (a) stop requiring otid on read calls to ads_get_ad_entities, or (b) have the MCP host inject a valid OTID into the request envelope automatically (the caller cannot, since it isn't an accepted tool argument).

Error Messages/Logs

Steps to Reproduce

Steps

  1. Connect the official Meta Ads connector

Enable the Meta Ads MCP (mcp.facebook.com/ads) in Claude (claude.ai, Cowork, or Claude Code) and complete OAuth.

  1. Find a queryable ad account

Call ads_get_ad_accounts (no arguments). Pick any account in the response where is_ads_mcp_enabled: true and is_queryable: true. Note its numeric ad_account_id.

Natural-language equivalent: "Using the Meta Ads connector, list my ad accounts."

  1. Call ads_get_ad_entities with minimal valid arguments

{
"ad_account_id": "<AD_ACCOUNT_ID>",
"level": "campaign",
"fields": ["id", "name", "effective_status"],
"limit": 5
}
Natural-language equivalent: "Using the Meta Ads connector, fetch my campaigns — call ads_get_ad_entities with that account id, level campaign, fields id/name/effective_status, limit 5."

❌ Actual result:

Invalid input: required input information was missing, malformed, or incorrect
for the tool call. OTID is a required input and cannot be empty or malformed.
✅ Expected result: the list of campaigns (this exact call returned campaign rows before 2026-06-17).

  1. Try to satisfy the requirement by passing otid — it is rejected

{
"ad_account_id": "<AD_ACCOUNT_ID>",
"level": "campaign",
"fields": ["id", "name", "effective_status"],
"limit": 5,
"otid": "00000000-0000-0000-0000-000000000000"
}
❌ Actual result:

Unknown argument(s): otid. Allowed argument(s): ad_account_id, fields, level, limit.
This is the core bug: step 3 says OTID is required; step 4 shows OTID is not an accepted argument. There is no value the caller can pass to satisfy it — it would have to be injected by the MCP host/runtime in the request envelope, and currently nothing supplies it, so the read is unsatisfiable from any client.

  1. (Control) Confirm it's isolated to this one tool

On the same connector, same account, same session, these all succeed without any OTID:

ads_get_ad_accounts (no args) → ✅ returns accounts
ads_get_field_context (no args) → ✅ returns the field catalog
Only ads_get_ad_entities fails. So the account is healthy and the connector is reachable; the OTID check is specific to the entities/metrics read tool.

Notes on scope
otid in Meta's Marketing API is a create-time idempotency token for write endpoints (e.g. POST /act_<id>/ads). Requiring it on a read has no semantic basis — it looks like a write-path required-field check leaking onto the read path, or a rollout expecting the host to mint an OTID per call.
The tool's published JSON schema does not list otid as a parameter, which is consistent with step 4's rejection.
Requested fix
Either (a) stop requiring otid on read calls to ads_get_ad_entities, or (b) have the MCP host inject a valid OTID into the request envelope automatically (the caller cannot, since it is not an accepted tool argument).

Claude Model

None

Is this a regression?

Yes, this worked in a previous version

Last Working Version

_No response_

Claude Code Version

2.1.118

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Terminal.app (macOS)

Additional Information

_No response_

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗