0.4.2 provides Signals reads and governed Actions tools. Legacy launcher.* tools and Actions Tests are retired. Use MCP discovery to check the tools available on your connection. Account-scoped tools use an eligible account in the workspace key’s scope. Older account-bound keys retain their binding.
For credentials covering several workspaces, call list_workspaces and pass the selected workspace_id on each workspace-scoped tool call. The examples below can omit it only for a single-workspace credential. Workspace selection and account selection are separate checks.
Start with the MCP introduction for supported tasks and access boundaries. For client configuration and your first account read, follow Setup.
How do I read every result?
Use integer values forget_arrivals_comparison.days and get_cookie_scan.limit; booleans are rejected. Page-based Actions and Launch lists accept page from 1 to 10000000000000000 inclusive; omit it for page 1 and correct page-validation errors before retrying. Prefer returned continuation arguments, particularly in JavaScript where large integers can lose precision.
Collection tools return bounded pages of complete rows. A short page can reflect the response-size budget rather than the end of the collection. Follow the returned continuation fields, preserving workspace, account, filters and date window; stop only when the corresponding cursor is null. Do not report partial results as a complete account inventory or history.
For live creative assets, associated ads and performance scans, use the separate creative-library cursors. Cursors are tool-specific; do not substitute an inventory row ID or decode an opaque cursor into a new request. If a row cannot fit and the call returns a validation error, narrow the requested object or asset instead of treating the error as an empty result.
What do tool annotations mean?
The published server card and MCP discovery exposereadOnlyHint, destructiveHint, idempotentHint and openWorldHint. These describe effects for the client; they do not grant authority or replace Plainrouter policy. A tool that saves feedback, a rule, a diagnostic or a proposal writes Plainrouter state even when it makes no Meta change. An idempotent tool can still write, and a read-only tool can contact Meta, as get-creative-library does. Check the specific tool, grant and approval requirements before calling it.
The server version is separate from the MCP protocol revision. For custom clients, see the stateless discovery and request-header requirements. MCP App support is advertised under capabilities.extensions["io.modelcontextprotocol/ui"] in the server card.
Which test and legacy tools are retired?
Actions Tests is removed from the dashboard, HTTP API and MCP.actions.tests.list, actions.tests.show and actions.tests.create are no longer advertised. Actions rules, proposals, Inbox review and receipts remain separate supported workflows.
Legacy Drive connection, inventory sync and file import are also retired. launcher.draft_batch, launcher.batch_status and launcher.preview_batch are removed, rather than conditionally available. An old SDK model, stored grant or feature flag cannot restore these operations. Migrate from draft-file import.
Why is a creative tool unavailable?
Workspace availability is separate from tool discovery and key permissions. Alauncher_unavailable error means new Launcher or governed creative work is unavailable for the selected workspace. Reissuing a Write key cannot enable it. Existing plan reads can remain available.
Built-in creative generation and rendering are retired. list_templates, render_ad, get_render, resume_render, list_creatives and preview_creatives, plus the creative-preview MCP App, are removed. These are separate from the advertised launch.creatives.* operations for uploaded assets. Retired generation/render grant operations are no longer issued; OAuth refresh retains only supported operations. An older SDK type or stored grant cannot restore a removed tool.
Uploaded-file and vetted-URL creative intake, Meta library reads and governed creative proposals retain their own permission, workspace-availability and policy checks. Creative workflow.
Account-rule tools
Follow Meta ad account performance rules for disabled creation, complete definition inputs, explicit enabling, evaluation windows, pagination and troubleshooting. Rule reads accept Read or Write Workspace keys; writes require Write. The active grant must cover the correspondingactions.rules.index, actions.rules.show, actions.rules.store or actions.rules.update operation.
Rules use platform_ad_account_id on creation and the returned rule_id for show/update. Saving or enabling does not execute a change during the request. Scheduled evaluation creates separate governed proposals, whose receipts must be followed in Inbox. Creating a rule does not change Ask/Full or the loss threshold.
get_account_inventory and get_inventory_metrics
Read retained Meta campaign, ad-set and ad data without a live Meta request or refresh. Both tools require an active workspace grant, current issuer authority and an explicit internal Plainrouter account_id. Read and Write tiers include these tools when their grant covers the operation. Existing account bindings still apply.
get_inventory_metrics includes daily object_metrics for retained campaigns, ad sets and ads, plus a workspace/window unmatched_arrivals count. Arrival counts are null when unavailable. Ad-level rows can carry receipt-backed creative provenance; missing fields remain unknown. Follow Read Meta account inventory for filters, cursor pagination, date windows, counting limits, freshness and recovery flags. This retained inventory is separate from get-creative-library, which reads live creative assets from Meta. Inventory history is not a new proposal-evidence source.
submit_feedback
Save a bug report or missing-capability request from your authenticated MCP client. This tool writes a feedback record in Plainrouter; it makes no advertising-account change and sends no reply in MCP. Read and Write grants can include it, but the issuer must also retain workspace update permission; a Viewer-issued Read key cannot submit feedback. Check the tool in published discovery and your active grant before calling it.
Example tool arguments:
id and status: "received". Save the ID; received means the report was stored, not that someone has read it or that a fix or response is guaranteed. This tool has no idempotency key: repeating a successful call creates another report. Do not include credentials or unnecessary customer data. For validation errors, correct kind or message length; for missing authorization, check the workspace key and grant.
Authentication and the retired sandbox
Usehttps://plainrouter.com/mcp with workspace authorization for tool calls. Public protocol discovery, catalogs and static ui:// app shells do not prove permission to inspect workspace data or request a proposal. Account-scoped tools also require access to the selected account; workspace installation and arrival tools do not accept account_id.
The unauthenticated sandbox and validate_sandbox_event are retired. Old sandbox requests return HTTP 410 with error: "sandbox_retired". Migrate existing clients. A Write-authorized dry_run_actions call previews supported changes against real state; it does not recreate synthetic test mode.
Built-in Signals prompts
The production server publishes three reusable prompts. They have no arguments and return static instructions for the client to follow; retrieving a prompt does not run its tool, inspect your workspace data, install code, or change an ad.
Choose a prompt from your MCP client’s prompt selector, where supported. Custom clients use
prompts/list to discover names and prompts/get to retrieve a message. For example, the params object for prompts/get is:
prompts/get requires a valid workspace key. The response contains one user-role text message.
After loading a prompt, the tool call still needs its own active grant and permissions. install_the_count inherits the managed-hostname-only limitation; it cannot supply same-origin forwarding material. signal_health needs the eligible account selection required by get_signal_health. The published server card lists the prompt names and titles.
If prompts are missing from your client, confirm the production URL and refresh discovery. A client without a prompt selector can still run the referenced tools directly. If a tool requires approval, report that requirement and stop for the human decision.
Busy-workspace errors
A concurrent operation can returnworkspace_lock_timeout through MCP. Affected JSON HTTP routes return HTTP 503 with Retry-After: 1; the browser shows The workspace is busy. Retry the operation shortly. Wait before retrying and retain the original idempotency key for the same operation.
A proposal can also fail with The proposal could not be recorded. Retry. MCP returns a tool error; affected HTTP routes return 503, error.code: "proposal_not_recorded" and Retry-After: 1. No proposal is committed by that failed attempt. Retry the identical request with its original key after the delay. Do not interpret the failure as approval or queue a different proposal to bypass it. HTTP failure reference.
Shared authorization and scope
Signals read tools require an active principal withad-account.read. verify_signal_ingestion requires signals.verify. propose-actions and dry_run_actions require actions.propose. Creative-library reads require creative.read. Creative proposal tools require both creative.write and actions.propose.
Clients authenticate with a role-capped workspace execution token. A Read token derives the read scopes. Write keys also derive the proposal scopes. New keys offer Read and Write; older Admin-tier keys retain their existing role and grant checks. Management keys and OAuth management credentials cannot authenticate to the MCP server.
Every production tool call also checks an active, unexpired grant for the same workspace and the requested operation. Revoked grants cannot authorize a call. The revoke_grant tool applies its separate revocation-authority check described below.
Account-scoped Signals responses identify:
get_account_state, get_signal_health, and get_performance resolve this workspace’s bound Meta destination when account_id is omitted. Supplying an ID selects only that same bound account, not another account in the team. A missing destination returns no_signals_destination; a different available account returns account_not_bound. Older account-bound keys retain their restriction. Actions and creative tools use their separate eligible-account selection rules. See account selection.
list_workspaces
Call with no arguments using a workspace execution credential. Returnsworkspaces, each with workspace_id, workspace_name, ad_account_name and effective tier. Null metadata is unknown. This is a bounded directory of authorized workspaces, not all workspaces owned by a user or team. It does not require a Meta connection.
Use a returned workspace_id for subsequent tool calls when multiple workspaces are authorized. A supplied workspace or account ID cannot widen the credential’s scope.
get_cookie_scan
Use a Read or Write key. The only optional input isworkspace_id, required for multi-workspace selection. No account_id is needed.
Returns workspace_id, status: "ready" | "unavailable", and scans. Each scan includes its hostname, lifecycle status, last scan time, findings, categories, pages, truncation state and scanner location. Unavailable data is null; blocked or failed scans do not establish a clean website. This read neither queues nor performs a scan. Run and interpret a cookie scan.
get_consent_rates
Use a Read or Write key with a grant covering this read. The only optional input isworkspace_id, required for multi-workspace selection. No account_id is needed.
On status: "available", read window_days, since, until, summary, daily, daily_by_dimension, breakdowns and recent_receipts. The summary contains decisions, analytics_grants, marketing_grants, analytics_rate and marketing_rate. The rates count recorded decisions, not visitors. On status: "unavailable", report data is null, not zero. This tool neither records nor changes consent. Reporting limits and CSV export.
get_install_instructions
Read the authorized workspace’s managed-hostname installation material without provisioning a hostname, changing DNS, or sending a test event. With a single-workspace credential, this production-only tool accepts an empty arguments object. For multiple workspaces, pass the selected workspace_id instead:
get_install_instructions. No Meta ad account selection is needed. The result contains:
For SaaS, follow the returned steps and separate setup readiness from live Meta verification. Secrets are entered by the owner in the app, never in chat.
Copy the returned DNS record and snippet exactly. If material is missing, finish managed-hostname setup in the dashboard; do not invent a hostname or construct a snippet from incomplete fields. After installation, check the browser requests and read
first_arrival again. A true value records arrival collection, not consented conversion delivery or Meta acceptance.
Same-origin forwarding (Path B) returns an error. Use the same-origin setup page instead; this tool never returns forwarding material or server secrets. A missing or inactive workspace authorization also fails.
get_conversion_journeys and get_cac_by_channel
These production reads use a Read or Write Workspace key authorized for Signals reporting. Their grants use the existing get_arrivals_comparison read operation. They accept workspace_id and their documented filters, not account_id; provider figures remain limited by the credential’s account scope. They do not change ads or call live advertising APIs and are not new spend-affecting proposal-evidence sources.
Journeys default to conversion_kinds: ["subscribed", "purchase"]; channel costs default to singular conversion_kind: "subscribed". Both support the four credit models, 7/30/60/90-day lookbacks and channel dimensions in the canonical guide. Align filters explicitly and preserve basis_note, effective dates, currency and availability reasons. Journey pages continue through pagination.next_cursor.
get_signal_quality
Read stored delivery states and gaps, separate test sends, retained identity presence, journey coverage, stored EMQ and consent summaries. Use a Read or Write Workspace key authorized through the existing get_arrivals_comparison operation. Defaults are a 30-day window with trial_started and subscribed; supported kinds also include purchase and lead. The tool accepts optional inclusive from/to timestamps and no account_id.
Check scope and null reasons, and preserve the explicit unavailable figures rather than inventing latency, historical sent-key coverage or complete dedup counts. See signal-quality interpretation. This is a read, not an ingestion test or permission to send a conversion.
get_trial_proof and get_event_volume
Both are read-only stored-evidence tools. Use a Read or Write Workspace key with an active grant for the existing get_arrivals_comparison operation; pass workspace_id for a multi-workspace credential. They accept no account_id, make no provider calls and do not create proposal evidence or authorize a change.
- Trial-to-paid cohorts select trial-start dates and follow later first-positive payments. Defaults are 90 days,
ad_set_id,lastand a 90-day lookback. - Weekly event volume groups event occurrences into UTC weeks. Defaults use the same window, level, model and lookback, but the result is distinct delivery counts, not a cohort conversion rate or Meta learning status.
first or last integer assignment, rather than fractional credit. Read range_used, retained coverage and unavailable reasons. The guides define their different levels, maturity rules and denominators.
get_arrivals_comparison
Read the same stored workspace comparison used by Dashboard and Signals: counted arrivals beside Meta outbound clicks. This production-only tool performs no provider write or live Meta read.
get_arrivals_comparison. The tool uses the authorized workspace, with workspace_id required for a multi-workspace credential, and does not accept account_id. Confirm workspace_id and the returned period before interpreting the result.
The response includes collection and connection status, the reporting period, arrivals, separate today_so_far counts, nullable claimed_clicks, and the available difference. Arrivals cover all site channels; claimed clicks are Meta outbound clicks. A displayed delta_percent uses claimed clicks as its denominator, not arrivals, and cannot be calculated when that denominator is absent or zero. Use either days or months; for a year request send {"months": 12}. A window beyond the plan or shorter workspace setting is refused, not clipped. Check the returned completed-day boundaries. Preserve unavailable platform and arrival values as unavailable, not zero. An available arrival total is complete; a null value or availability: "unavailable" means the required evidence could not be read. Follow the arrival availability and platform-read guidance.
The response also includes claimed_clicks_label, claimed_clicks_outbound_since, and claimed_clicks_caveat. Keep the caveat with historical data: older days can still contain link clicks or all clicks. The separate purchases block includes Meta-attributed and accepted server-side Purchase counts, its completed account-local window, attribution details, and daily read timestamps. See Purchase comparison limits; it has no purchase gap or status.
If setup or platform data is unavailable, read the returned status and message before calculating a difference. Arrival/click differences do not establish lost conversions, attribution, or a cause. This tool is not an accepted propose-actions evidence source; use get_performance for eligible stored reconciliation evidence.
How should I handle bounded responses?
Responses are bounded. Missing optional detail or a smaller response is not evidence that no account capability, permission or history exists. Inspect explicit status, availability and pagination fields. Historical synthetic fixtures do not define the current response contract; use the published schema and authenticated result.get_account_state
Call this first to confirm the workspace’s Signals-bound Meta account. Omit account_id to resolve its destination binding, or supply that bound internal account ID. Confirm the returned scope before further calls. It returns:
- Agent ID, name, and granted permissions.
- Shared scope.
- Advertising account ID, platform-native ID, name, platform, currency, timezone, and status.
- Workspace ID and name.
- Raw platform connection
status, derivedhealth, and expiry. - Signal ID, status, collection domain, and connected destination.
- Available capability flags.
- Response generation time.
How should I interpret connection health?
connection.health uses the same provider-status calculation as Connections for the key’s authorized workspace. It is separate from the stored connection.status and from Signals collection health. For example, this illustrative excerpt shows an active record that needs attention:
not_connected, revoked, attention_needed, and connected. Use the provider connection status reference to choose the next step. An active record alone does not establish healthy credentials or a successful latest read. Conversely, connected does not prove fresh platform data or conversion delivery. The tool reads stored state; it does not make a live provider check.
Confirm the health returned for your authorized workspace; public discovery does not establish connection health.
What do the capability flags mean?
The flags describe different account capabilities. For example, this illustrative response has stored Signals context but no current management access:
A Page or Signals dataset is not required merely to report existing-object management availability. Creating new ads has additional requirements, including a Page. Tool grants, the caller’s permissions, policy, current provider state, and execution checks still apply.
writes: true is neither approval nor a guarantee that a requested change will execute. This inspection uses stored authorization state, not a live Meta permission probe. See Meta grant labels.
get_signal_health
Use this diagnostic for collection, delivery, and stored account health. The diagnostic accepts this optional reporting window:
activity.completed_window: arrivals for the retained completed-day reporting window.activity.today_so_far: the separate incomplete local-day arrival count.activity.receivingandactivity.last_event_at: collection activity and freshness.delivery: non-test delivery counts and acceptance rate for the last 24 hours, independently of the requested arrival window.match_quality: the latest stored Meta score, measurement time, and history when available.performance: stored reconciliation diagnostics. The standaloneget_performanceinput remains 1–90 days; the arrivalmonthsoption does not extend that contract.diagnostics,data_sources, andgenerated_at: explanations, provenance, and response freshness.
{"months": 12} for the 12-calendar-month window, when available. Do not assume the fixed 24-hour delivery diagnostics cover that same period. Read an available arrival total directly; the public response does not expose jurisdiction subtotals or a partial marker. A null arrival value or health status: "unavailable" means arrival evidence could not be read; it is not an awaiting-data or quiet result. Missing platform history is not zero activity. See Signals measurements and troubleshooting.
This call reads stored measurements. It does not trigger a live Meta Marketing API read.
get_performance
Use this tool for a stored reconciliation window.
Plainrouter excludes modeled and portfolio-only events from this proposal-evidence path. Only eligible observed reconciliation data contributes to its performance fields.
Input
Output
The response includes:- Shared scope.
- Requested period with
days,since, anduntil. - Measurability state.
- Gateway-verified accepted count.
- Existing reconciliation
meta_reportedcount: Meta dataset events from every sender, separate from ad-attributed Purchases. Neither side is an attributed-conversion count. purchases: Meta-attributed and accepted server-side Purchase counts with their own completed account-local window and attribution details. Missing or unmeasured counts remainnull;0is a measured zero. Check Purchase availability.platform_claimed_clicks: Meta outbound clicks, withclaimed_clicks_caveatfor historical rows.- Observed gap.
- Alignment percentage.
- Number of alert days.
- Latest report date.
- Totals grouped by event.
- Daily report rows.
data_source: "stored_reconciliation_reports".marketing_api_called: false.- Response generation time.
daily[].date is that UTC date; the account-timezone period selects which reports are included, except the empty fallback uses UTC dates. observed_gap is accepted Plainrouter events minus dataset events; alignment is a 0–100 percentage against the larger count, or null when both are zero. Neither diagnoses lost conversions.
The response also includes stored account-level platform_claimed_clicks, platform_impressions, platform_spend, per-day platform_daily read states, and claimed_clicks_last_read_at when available. These platform fields are descriptive context, not admissible spend or target-performance evidence for Actions proposals.
Measurability
no_history and stale_history. When quantitative_citations_allowed is false, an agent must not cite count, gap, alignment, alert-day, daily, or by-event values as proposal evidence.
verify_signal_ingestion
Use this tool to run an identity-free write-path diagnostic against an active Signal. It accepts no input and resolves the Signal from the authorized workspace.
The tool writes one idempotent signal_verification event and returns:
duplicate: true. The tool has no spend capability.
The call fails if the connection lacks signals.verify, the issuer lacks current workspace update permission, or the authorized workspace has no active Signal. A Viewer-issued Read key cannot run this write-path diagnostic.
revoke_grant
Revoke a workspace grant using its 26-character ULID in grant_id. Obtain the grant ID from the authorized token-management response; it is different from the token ID and plaintext credential.
revoke_grant permission or authority as the human who delegated the target agent. Admin-tier grants include revocation permission. Knowing a grant ID does not grant access to another workspace.
The tool returns grant_id, workspace_id, and revoked_at. It records the revocation in the audit chain and prevents credentials backed by that grant from authorizing subsequent operations. An authorized repeat returns the existing revocation without creating another one. It does not undo previously executed advertising actions.
Invalid grant-ID input fails validation. Missing or inactive caller authorization, another workspace’s grant, or insufficient revocation authority is refused.
Read Actions and policy
Use these five production tools to follow a proposal without visiting the dashboard. They read stored Plainrouter records, make no provider calls, and do not change ads, action decisions, or policy. All five requiread-account.read and an active workspace grant covering read-actions. Newly issued Read and Write keys include it. Existing propose-actions grants remain compatible for reads. Older Read credentials without either operation need replacement or fresh authorization; current workspace role and account scope still apply.
Every tool accepts an optional positive integer account_id, required when the workspace has multiple eligible active accounts. First confirm the workspace and account with get_account_state. Batch and action IDs are Plainrouter ULIDs returned by proposal or read responses, not Meta object IDs.
list_actions
Accepts account_id, optional status from the action-status list, page (positive integer, default 1), limit (1–100, default 25) and an integer cursor. For example:
data and pagination meta containing current_page, last_page, per_page, total, next_cursor, truncated, next_page_arguments and continuation. Actions are ordered by action ID descending. Follow meta.next_page_arguments until null; the response-size budget can return fewer rows than limit. Do not use batch-only statuses such as completed as an action filter.
get_action_batch
Requires batch_id, plus account_id when needed. Returns data with the batch ID, workspace and platform-account IDs, status, policy decision/reasons, rationale, idempotency key, and actions array.
Use limit from 1–100 (default 25). Continue with data.next_cursor as integer cursor until null. data.total, data.truncated and data.continuation describe the action collection. Read every page before concluding that all actions in a batch have been verified.
get_action
Requires action_id, plus account_id when needed. Returns one action under data. It includes id, batch_id, workspace_id, type, flat target_entity_type/target_entity_id/target_entity_name fields, params, rationale, status, policy_decision, and policy_reasons. These are also the action fields returned by the list and batch tools.
System proposals include proposer.type: "system". Their evidence identifies either the rule, revision, run and decision, or an inverse proposal’s original action and outcome. Inspect the two evidence shapes instead of assuming every system proposal is an inverse. The published server card defines the output schema.
get_action_decision_receipt
Requires action_id, plus account_id when needed. Returns document, document_sha256, chain_entry (sequence and hash), and current disposition at the top level. There is no data wrapper. Read the receipt’s outcome and integrity limits; its existence alone does not prove success.
get_action_policy
Accepts the optional account_id selector and workspace_id for multi-workspace selection. Returns the current workspace policy under data; see the policy response fields. If no policy is saved, the result contains effective defaults and id: null, without creating a record. Do not substitute current policy for an earlier proposal’s frozen mode or evidence.
Frozen target comparison
list_actions, get_action and batch action records can contain counted_evidence. It is a proposal-time display snapshot over seven completed account-local days, with target, capture time, window, currency, Meta-reported figures, counted figures and coverage. Null values remain unknown. It is not fresh policy state or spend-admission evidence. Interpret the comparison.
Current disposition in Actions reads
list_actions, get_action, and each action returned by get_action_batch include batch_status and disposition. The latter separates receipt status, later outcome, compensation reason, recovery disposition, and late restoration from the historical action status. A batch can also return restoration_summary.
When available, disposition.outcome_payload provides the stored comparison sides, observation time, reason and management verdict. Read the outcome payload fields and window and volume requirements. A verified action is not a performance verdict; a lost outcome does not automatically undo it.
get_action_decision_receipt returns current disposition beside the immutable document, document_sha256, and chain_entry. The projection is outside the document hash. Preserve nulls and do not turn late restoration into a successful original execution. Use the shared field reference.
Verify the result and handle failures
Confirm the returned workspace/account scope and follow action status through receipt evidence.approved_without_execution means no provider write occurred. In Ask mode, a person approves every change in Inbox first. Full can run admitted unprotected changes automatically; protected batches still require person approval. See protected changes.
If a read is refused, check the key’s current role and read-actions grant (or compatible propose-actions), then account selection. A missing or out-of-scope action, batch, or decision receipt returns a not_found tool error; a receipt may not exist yet. Correct invalid status, page, or ID arguments before retrying. An empty list can reflect a filter or the selected account.
Use the Actions HTTP API for the same scoped reads from a server integration. These tools do not provide a workspace audit export, policy editor, or approval/rejection operation.
dry_run_actions
Preview a proposed batch’s policy decisions and execution diffs without creating the proposal or changing Meta. This tool requires workspace authorization and can read real provider state.
Use a Write Workspace key, an active propose-actions grant, actions.propose, and the same complete input as propose-actions. Creative upload and duplication also require creative.write. The required rationale, retry key, human-selected targets, and evidence rules still apply.
The response contains dry_run.actions. Each evaluated item includes its policy decision, reasons, and diff.before/diff.proposed. approval_required and would_auto_execute reflect the combined batch decision. An item with status: "dry_run_unavailable" has a reason instead of a policy prediction or diff. Inspect every item before reporting the preview result.
A preview creates no batch, action, decision receipt, or proposal audit event and queues no execution. Repeating it evaluates current state again; it does not reserve an idempotency key. Submit separately with propose-actions when authorized, then direct the person to Inbox. Policy and execution checks still apply.
See the dry-run response reference and HTTP example. A schema-accepted type or passing preview does not establish that its execution workflow is available.
propose-actions
This tool validates the action shapes, captures fresh evidence provenance, evaluates policy, and creates one batch. The Actions API accepts the same proposal body. The public server card publishes the shared schema; accepted types do not by themselves establish execution availability.
Top-level input
Each evidence declaration contains:
source_tool values are get_account_state, get_signal_health, and get_performance. get_inventory_metrics can cite target-matching leaf fields for Meta inline link clicks, counted arrivals, and Purchase count/value. The cited row must match the proposed campaign, ad set or ad at its own level. Spend, impressions, child rows, containers and ratios are refused for that source. Creative actions can also use the action-bound sources get-creative-library and staged-asset-manifest.
Every field path must exist in a fresh server-side capture. Aggregate sources cannot include action_index. An action-bound source requires action_index and can cite only its matching creative action.
Action object
Every action has this common shape. This example pauses a human-selected campaign; policy, approval and verification still apply. Use supported canonical action names for new proposals; legacy names are refused. For creative work, use Create governed Meta creatives.ad_account, campaign, ad_set, and ad.
Action parameters
pause and resume
pause and resume
Use a human-selected campaign, ad set, or ad and empty
params: {}. Ask requires person approval. Full can automate admitted unprotected changes; campaign/ad-set resume remains protected. See approval boundaries.increase_budget and decrease_budget
increase_budget and decrease_budget
Use a campaign or ad set and
new_daily_budget_minor, a positive integer in the account currency’s Meta budget units. Budget increases require person approval in Ask and Full. An admitted decrease can follow Full automation; current policy and provider-state checks still apply.duplicate_ad_with_creative
duplicate_ad_with_creative
Requires
asset_id and status: "paused". An optional name_suffix can identify the duplicate.upload_asset
upload_asset
Requires
staged_asset_id, asset_type: "image", filename, and a lowercase 64-character content_sha256. Optional fields include detected mime_type and positive size_bytes.Validation and retry keys
These rules apply to bothpropose-actions and dry_run_actions:
- Send
actionsandevidenceas JSON arrays, not objects keyed by index or name. NUL characters (U+0000) are rejected in request keys and values. - Every action’s
paramsobject is closed: supply only fields documented for that type, even when an extra field would benull. Types with no parameters require{}. A stored response can include server-owned fields that cannot be submitted. - Boolean values do not satisfy integer inputs such as account selectors, page sizes or
evidence[].action_index. Use the documented type and range, and preserve each tool’s cursor type. - Each action can supply its own
idempotency_key. Otherwise its effective key is the batch key plus:and its zero-based action index. Effective action keys must be unique within the batch, including collisions between explicit and derived keys. - Keep the original batch key and payload when retrying the same submission after a timeout. A preview does not reserve that key or return a stored submission.
Canonical
pause and resume support campaign, ad set, and ad targets. increase_budget and decrease_budget support campaign and ad set daily budgets. These types require human approval in Ask; Full queues policy-allowed unprotected changes automatically; budget increases and campaign/ad-set resumes still need person approval. Current policy and execution authorization still apply. Historical Suggest only batches never execute. A kill switch can stop further writes without undoing prior changes.
New propose-actions and dry_run_actions calls refuse set_status, adjust_budget, replace_creative, shift_spend, and rollback with a tool validation error. Stored historical actions of these types never execute. Use a supported canonical type and correct the affected actions.N.type; a different retry key does not bypass validation. See action-type refusals, supported Actions, and the creative workflow.
HTTP field errors return 422; MCP exposes a tool validation error. Read the field path and message. action_request_invalid identifies list, NUL, or duplicate-key problems; action_target_id_invalid identifies the guarded target-ID rule; action_params_invalid identifies invalid typed parameters. Correct the payload rather than retrying it unchanged.
Repeated or unsettled management targets
When submittingpause, resume, increase_budget, or decrease_budget, include each target type and ID only once per batch. Repeating the same target with a different action or display name still returns action_target_repeated on the affected target field.
A target already held by an unsettled management proposal is also refused. Follow the existing batch instead of changing the idempotency key to bypass it. See management-target refusals for recovery steps. These rules do not mean two creation actions must have different parent destinations.
Missing staged assets or source ads
A proposal or dry run can return a field validation error when the state it references is unavailable:
Read the reported
actions.N field so you correct the affected action in a multi-action batch. These state refusals do not create a batch or perform a provider write. HTTP exposes 422; MCP exposes a tool validation error. Temporary provider failures are not evidence that an ad is missing. Check the actual error before replacing references or retrying.
Evidence restrictions
Plainrouter rejects evidence paths that claim campaign, ad set, ad, target, CPA, spend, budget, or learning-phase state from aggregate Signals reads.get_signal_health is diagnostic and cannot support a batch containing a status change, budget change, or duplicate_ad_with_creative. This rule applies to the entire batch: adding an upload_asset action does not make a mixed spend-affecting proposal eligible. An upload-only upload_asset batch is the current non-spend exception and may retain signal-health context.
Use get_performance when a spend-affecting proposal needs stored reconciliation-derived performance evidence. Quantitative citations need at least one ledger-class numeric leaf. Platform-reported or platform-derived values can accompany it as context but cannot satisfy that requirement alone. Descriptive-only citations do not claim measurability. An unclassified leaf is refused, including inside a cited container; narrow the citation to supported fields.
The reconciliation measurability flag applies to its reconciliation fields, not every ledger field. Retained inventory metrics and creative history do not become admissible proposal evidence just because they are visible.
It also rejects:
- Unknown source tools.
- Field paths absent from the cited response.
- Quantitative performance fields from non-measurable history.
- A target source other than
human_supplied. - Proposals from a connection without
actions.propose.
Response
The response includes the batch ID, scope, evidence provenance, status, policy decision and reasons, approval requirement, queue URL, proposer, rationale, and normalized action records. Evidence provenance records the source service, scope level, exact fields, observation time, reporting window, measurability, and the target-selection boundary.get-creative-library
This tool makes live Meta Marketing API reads for the approved account. It requires creative.read and an active Meta connection with ads_read or ads_management.
Input
Output
The response includes:- Images as
image:<hash>and videos asvideo:<id>. - Names, status, dimensions or duration, previews, and creation time when available.
- Ads historically associated with each asset.
- Aggregated last-30-day impressions, clicks, spend, and conversions.
- Separate image and video pagination cursors.
performance_window: "last_30d".
pagination.images.after or pagination.videos.after with has_more. Associated-ad pages use each asset’s performance.next_cursor and truncated. The provider performance scan has its own performance_pagination; pass its next_cursor as performance_cursor. Performance totals describe the scanned page, so a truncated scan is not complete last-30-day account history. Keep these three continuation paths separate.
upload-asset
This tool validates and privately stages an image, then creates one canonical upload_asset proposal. It requires creative.write and actions.propose.
The response wraps the canonical proposal with its ID, scope, evidence provenance, immutable execution-mode snapshot, status, policy result, approval requirement, approval-queue URL, and action record.
Plainrouter freezes the staged bytes and records only the approved manifest fields: staged ID, SHA-256 digest, detected MIME type, byte size, account binding, and frozen/immutable state. Raw bytes do not enter the action or audit record.
Only JPEG and PNG upload execution is supported. Video upload execution remains blocked.
duplicate-ad-with-creative
This tool creates one canonical duplicate_ad_with_creative proposal. It requires creative.write and actions.propose.
Plainrouter re-reads the source ad and selected asset under the approved account before creating the proposal. The normalized action always contains
status: "paused".
After Ask approval or an allowed Full proposal, the worker rechecks policy, creates the copy as PAUSED, persists the provider ad ID in a durable receipt, and verifies the exact receipt-addressed object. IN_PROCESS can enter scheduled verification for up to 48 hours. The batch is not Landed until every receipt is exactly verified.