Skip to main content
Glama

mureo_state_upsert_campaign

Atomically write or update a campaign snapshot in STATE.json, syncing metadata changes from vendor MCPs or BYOD imports and persisting metrics and ad status for dashboard reporting.

Instructions

Atomically upsert a CampaignSnapshot into STATE.json (root campaigns array). Use this to keep STATE.json in sync with campaign metadata changes the agent observes via vendor MCPs or BYOD imports. Pass the optional metrics object to persist the campaign's performance numbers (spend, clicks, conversions, cpa, ctr, …) so the reporting dashboard can render KPIs from STATE.json. Pass the optional ads array to persist ad-level delivery status, so a pause applied outside mureo is recorded and can be diffed on the next run.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
pathNoOptional path to the file. Defaults to STRATEGY.md / STATE.json in the MCP server's current working directory. Paths outside cwd are refused.
reasonNoWhy this change is being made: one or two sentences naming the evidence and the expected effect. Stored in the journal and on the action_log entry this call produces, for the operator and the next session.
campaignYesA CampaignSnapshot for STATE.json plus its platform context. Required: campaign_id, campaign_name, status, platform, account_id. The platform + account_id populate the per-platform ``platforms`` section the dashboard reads (omit them and the client renders as inactive). Optional fields mirror the snapshot schema in docs/strategy-context.md, including ``metrics`` (spend / impressions / clicks / conversions / cpa / ctr / result_indicator / period / fetched_at) for dashboard KPIs.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed1 schema field changedv0.21.3
    • changedInput schema / properties / campaign / properties / account_id / description
      Previous value: -"Platform account id (Google ``customer_id`` / Meta ``act_*``) written onto the platform entry, and used to detect a second entry for the same account."New value: +"Platform account id (Google ``customer_id`` / Meta ``act_*``) written onto the platform entry, and used to detect a second entry for the same account. A placeholder (``unknown``, ``n/a``, …) is refused: if the collection could not resolve an id, use mureo_state_platform_not_collected_set."
  2. Changed1 schema field changedv0.20.0
    • addedInput schema / properties / reason
      Added value: +{
      +  "description": "Why this change is being made: one or two sentences naming the evidence and the expected effect. Stored in the journal and on the action_log entry this call produces, for the operator and the next session.",
      +  "maxLength": 500,
      +  "type": "string"
      +}
  3. Changed4 schema fields changedv0.13.1
    • changedInput schema / properties / campaign / properties / bidding_details / description
      Previous value: -"Free-form bidding detail (e.g. {'target_cpa': 5000}). One key is read by mureo: for Google Ads, 'bidding_strategy_system_status' — the value google_ads_campaigns_get / google_ads_campaigns_diagnose returns — is what the learning-period pre-flight (mureo_learning_reset_preflight, and the block_learning_resets* guardrails) uses to tell whether the campaign is already re-learning. Without it that state is reported 'unknown', never 'steady'."New value: +"Free-form bidding detail in the platform's own vocabulary (e.g. {'target_cpa': 5000}); omit it alongside bidding_strategy_type where the platform has neither. One key is read by mureo: for Google Ads, 'bidding_strategy_system_status' — the value google_ads_campaigns_get / google_ads_campaigns_diagnose returns — is what the learning-period pre-flight (mureo_learning_reset_preflight, and the block_learning_resets* guardrails) uses to tell whether the campaign is already re-learning. Without it that state is reported 'unknown', never 'steady'."
    • addedInput schema / properties / campaign / properties / bidding_strategy_type / description
      Added value: +"Bid strategy as the platform itself names it, verbatim. Omit it for a platform that does not select delivery by a bid — never borrow another platform's strategy name."
    • addedInput schema / properties / campaign / properties / monthly_budget
      Added value: +{
      +  "description": "The campaign's own MONTHLY budget, on a platform that has that concept alongside the daily one. Omit it entirely for a platform configured per day (Google Ads, Meta) — do not send a daily budget multiplied out, which is an implied cap and not what the campaign is set to spend. mureo never stores a total over these: it sums them on read, and only where every campaign of a declaring platform carries one.",
      +  "type": "number"
      +}
    • changedInput schema / properties / campaign / properties / platform / description
      Previous value: -"Platform key this campaign belongs to, e.g. ``google_ads`` / ``meta_ads`` / ``tiktok_ads``, or a plugin bridge ``plugin:<dist>``. Use the SAME key the account is already stored under — one ad account has exactly one platform key, and a second key for an account another key already holds is REJECTED (the reporting view sums the entries, so it would double-count)."New value: +"Platform key this campaign belongs to, e.g. ``google_ads`` / ``meta_ads`` / ``tiktok_ads``, a platform an installed plugin registered (its provider name), or a plugin bridge ``plugin:<dist>:<provider>``. Use the SAME key the account is already stored under — one ad account has exactly one platform key, and a second key for an account another key already holds is REJECTED (the reporting view sums the entries, so it would double-count). A NEW key that is none of the three is REJECTED too: do not invent or abbreviate a platform name."
  4. Changed1 schema field changedv0.10.44
    • addedInput schema / properties / campaign / properties / bidding_details / description
      Added value: +"Free-form bidding detail (e.g. {'target_cpa': 5000}). One key is read by mureo: for Google Ads, 'bidding_strategy_system_status' — the value google_ads_campaigns_get / google_ads_campaigns_diagnose returns — is what the learning-period pre-flight (mureo_learning_reset_preflight, and the block_learning_resets* guardrails) uses to tell whether the campaign is already re-learning. Without it that state is reported 'unknown', never 'steady'."
  5. Changed4 schema fields changedv0.10.43
    • changedInput schema / properties / campaign / properties / account_id / description
      Previous value: -"Platform account id (Google ``customer_id`` / Meta ``act_*``) written onto the platform entry."New value: +"Platform account id (Google ``customer_id`` / Meta ``act_*``) written onto the platform entry, and used to detect a second entry for the same account."
    • addedInput schema / properties / campaign / properties / account_id / minLength
      Added value: +1
    • changedInput schema / properties / campaign / properties / platform / description
      Previous value: -"Platform key this campaign belongs to, e.g. ``google_ads`` / ``meta_ads``."New value: +"Platform key this campaign belongs to, e.g. ``google_ads`` / ``meta_ads`` / ``tiktok_ads``, or a plugin bridge ``plugin:<dist>``. Use the SAME key the account is already stored under — one ad account has exactly one platform key, and a second key for an account another key already holds is REJECTED (the reporting view sums the entries, so it would double-count)."
    • addedInput schema / properties / campaign / properties / platform / minLength
      Added value: +1
  6. Changed2 schema fields changedv0.10.37
    • addedInput schema / additionalProperties
      Added value: +false
    • addedInput schema / properties / campaign / properties / ads
      Added value: +{
      +  "description": "Ad-level (creative-level) delivery state for this campaign. Send it so a change made OUTSIDE mureo — an ad paused by hand in the platform UI, stopped by its ad set/campaign, or rejected by policy — is recorded as fact and can be diffed on the next run. ``status`` is what the ad is configured as; ``effective_status`` is whether it is actually delivering, and the two disagreeing is the signal. Omit the whole field when you did not fetch ad-level status (that is different from sending an empty list, which means 'fetched, this campaign has no ads').",
      +  "items": {
      +    "properties": {
      +      "ad_id": {
      +        "description": "Platform ad id.",
      +        "type": "string"
      +      },
      +      "as_of": {
      +        "description": "IGNORED — the server stamps each ad with its own clock (ISO 8601 with UTC offset), so a drifted client date can never be persisted and later read back as when the status was observed.",
      +        "type": "string"
      +      },
      +      "effective_status": {
      +        "description": "Actual delivery status where the platform exposes one (Meta: ACTIVE / ADSET_PAUSED / CAMPAIGN_PAUSED / DISAPPROVED / …). Omit when the platform does not report it rather than copying ``status`` into it.",
      +        "type": "string"
      +      },
      +      "name": {
      +        "description": "Ad name.",
      +        "type": "string"
      +      },
      +      "status": {
      +        "description": "Configured status (e.g. ACTIVE / PAUSED).",
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "ad_id"
      +    ],
      +    "type": "object"
      +  },
      +  "type": "array"
      +}
  7. Changed5 schema fields changedv0.10.7
    • changedInput schema / properties / campaign / description
      Previous value: -"A CampaignSnapshot for STATE.json. Required: campaign_id, campaign_name, status. Optional fields mirror the snapshot schema in docs/strategy-context.md."New value: +"A CampaignSnapshot for STATE.json plus its platform context. Required: campaign_id, campaign_name, status, platform, account_id. The platform + account_id populate the per-platform ``platforms`` section the dashboard reads (omit them and the client renders as inactive). Optional fields mirror the snapshot schema in docs/strategy-context.md, including ``metrics`` (spend / impressions / clicks / conversions / cpa / ctr / result_indicator / period / fetched_at) for dashboard KPIs."
    • addedInput schema / properties / campaign / properties / account_id
      Added value: +{
      +  "description": "Platform account id (Google ``customer_id`` / Meta ``act_*``) written onto the platform entry.",
      +  "type": "string"
      +}
    • addedInput schema / properties / campaign / properties / metrics
      Added value: +{
      +  "description": "Optional performance metrics for the reporting dashboard: spend, impressions, clicks, conversions, cpa, ctr, result_indicator (Meta: clicks vs leads), period (e.g. ``LAST_30_DAYS``), fetched_at (ISO 8601).",
      +  "type": "object"
      +}
    • addedInput schema / properties / campaign / properties / platform
      Added value: +{
      +  "description": "Platform key this campaign belongs to, e.g. ``google_ads`` / ``meta_ads``.",
      +  "type": "string"
      +}
    • changedInput schema / properties / campaign / required
      Previous value: -[
      -  "campaign_id",
      -  "campaign_name",
      -  "status"
      -]New value: +[
      +  "campaign_id",
      +  "campaign_name",
      +  "status",
      +  "platform",
      +  "account_id"
      +]
  8. Addedv0.9.12
  9. Removedv0.9.6
  10. Addedv0.9.2
  11. Removedv0.9.1
  12. Addedv1.0.6

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden of behavioral disclosure. It reveals atomicity, path restrictions ('Paths outside cwd are refused'), the journal/action_log side effect, the server-stamped timestamp ignoring client-provided as_of, platform key uniqueness and rejection rules, monthly_budget semantics (not a derived total), and the distinction between omitting ads vs sending an empty list. This is exceptionally transparent about side effects and constraints.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is lengthy but every sentence adds value, covering edge cases and parameter nuances. It is front-loaded with the primary purpose, then organizes details by parameter. While it could be more terse, the complexity of the tool justifies the depth. It is structured well, with clear logical flow from purpose to parameter-specific guidance.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description fully equips an agent to call the tool correctly: it explains when to use it, what each optional field is for, how to handle platform-specific quirks, and points to alternative tools for edge cases. Given the nested object structure and numerous optional fields, the description leaves no ambiguity about the expected payload or the consequences of omissions. It is complete for a tool of this complexity.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Though schema coverage is 100%, the description adds substantial meaning beyond the schema: it explains the semantic difference between status and effective_status, the meaning of omitting ads vs empty list, the rejection of a second platform key for the same account, the monthly_budget rule (don't send a multiplied daily budget), and the special bidding_details key for Google Ads learning status. These insights are not inferable from the schema alone.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a precise verb and resource: 'Atomically upsert a CampaignSnapshot into STATE.json (root campaigns array).' It then states the exact use case — keeping STATE.json in sync with campaign metadata changes observed via vendor MCPs or BYOD imports. This clearly differentiates it from sibling state tools (e.g., mureo_state_platform_metrics_set) by specifying the resource and the atomic operation.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly states when to use the tool ('Use this to keep STATE.json in sync with campaign metadata changes...') and gives guidance on when to include optional fields (metrics, ads). It does not explicitly contrast with sibling state tools or state when not to use it, but the context is clear. It also references the alternative mureo_state_platform_not_collected_set for unresolved account IDs, which is a helpful pointer.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Deploy Server

Other Tools