<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: TJ</title>
    <description>The latest articles on DEV Community by TJ (@tj_v_b04dbd6f4957aa83a196).</description>
    <link>https://dev.to/tj_v_b04dbd6f4957aa83a196</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4029379%2F3bc473fa-daa9-4ecd-bdc4-7b3fe2b84794.png</url>
      <title>DEV Community: TJ</title>
      <link>https://dev.to/tj_v_b04dbd6f4957aa83a196</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tj_v_b04dbd6f4957aa83a196"/>
    <language>en</language>
    <item>
      <title>Google Ads + Codex: Build the Read Loop Before You Allow Writes</title>
      <dc:creator>TJ</dc:creator>
      <pubDate>Sat, 29 Aug 2026 19:48:51 +0000</pubDate>
      <link>https://dev.to/tj_v_b04dbd6f4957aa83a196/google-ads-codex-build-the-read-loop-before-you-allow-writes-je7</link>
      <guid>https://dev.to/tj_v_b04dbd6f4957aa83a196/google-ads-codex-build-the-read-loop-before-you-allow-writes-je7</guid>
      <description>&lt;p&gt;A Google Ads Codex workflow becomes risky at the exact moment the agent can act. The safe first run should not “optimize” anything. It should prove that it has the right account, the right date range, and enough evidence to make a recommendation.&lt;/p&gt;

&lt;p&gt;That sounds conservative. It is also the fastest way to find whether the integration is trustworthy.&lt;/p&gt;

&lt;p&gt;The implementation pattern I would use is simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;bind the agent to one explicit account;&lt;/li&gt;
&lt;li&gt;read a bounded slice of data;&lt;/li&gt;
&lt;li&gt;return evidence in a predictable shape;&lt;/li&gt;
&lt;li&gt;propose changes without applying them;&lt;/li&gt;
&lt;li&gt;require confirmation for every write; and&lt;/li&gt;
&lt;li&gt;read the platform again to verify any approved change.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This article shows what that loop looks like in practice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with a read-only contract
&lt;/h2&gt;

&lt;p&gt;Google’s current &lt;a href="https://developers.google.com/google-ads/api/docs/developer-toolkit/mcp-server" rel="noopener noreferrer"&gt;Google Ads MCP server&lt;/a&gt; is strictly read-only. It can list accessible customers, inspect resource metadata, and run Google Ads Query Language searches. That makes it a useful reference point even if the integration you eventually use also supports writes.&lt;/p&gt;

&lt;p&gt;For the first run, pass a contract that removes as much interpretation as possible:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"account_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CUSTOMER_ID"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"window"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YYYY-MM-DD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"end"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YYYY-MM-DD"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"grain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"campaign"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"metrics"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"cost"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"clicks"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"conversions"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"conversion_value"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"read_only"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important field is not &lt;code&gt;mode&lt;/code&gt;. It is &lt;code&gt;account_id&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;An advertising login can expose a manager account, several client accounts, or both. If the agent silently chooses one, every conclusion after that point can be well-formed and still be wrong. The run should stop when the account is missing or ambiguous.&lt;/p&gt;

&lt;p&gt;The same rule applies to dates. “Recently” is not a data contract. Use an exact start date, exact end date, and the account’s reporting timezone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Separate observation from judgment
&lt;/h2&gt;

&lt;p&gt;Do not let the agent jump from a query result to a command. Make it produce an evidence object first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"account_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CUSTOMER_ID"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"window"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YYYY-MM-DD..YYYY-MM-DD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"observations"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"entity_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"campaign"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"entity_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CAMPAIGN_ID"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"finding"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Spend increased while recorded conversions fell"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"support"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"cost_change_pct"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;18.4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"conversion_change_pct"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;-11.2&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"blocked_by"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This boundary catches several common failures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the selected window has no delivery;&lt;/li&gt;
&lt;li&gt;conversion tracking changed inside the window;&lt;/li&gt;
&lt;li&gt;money values were compared without checking currency;&lt;/li&gt;
&lt;li&gt;a paginated result was treated as the complete account; or&lt;/li&gt;
&lt;li&gt;a campaign was evaluated without its status or budget context.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When one of those conditions is present, &lt;code&gt;blocked_by&lt;/code&gt; should explain it. The agent should not fill the gap with a guess.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make proposals executable, but not automatic
&lt;/h2&gt;

&lt;p&gt;A useful proposal is specific enough to apply later and clear enough to reject now.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pause_campaign"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"entity_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CAMPAIGN_ID"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Cost increased and recorded conversions declined in the bounded comparison"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"evidence_refs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"observation-1"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expected_effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Stop additional delivery while tracking is checked"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reversal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"enable_campaign"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"requires_confirmation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three details matter here.&lt;/p&gt;

&lt;p&gt;First, the proposal refers to evidence returned by the read step. Second, it describes a reversal. Third, confirmation is data, not a vague sentence in a system prompt.&lt;/p&gt;

&lt;p&gt;This makes the boundary testable. A tool wrapper can reject any mutation when &lt;code&gt;requires_confirmation&lt;/code&gt; is absent, false, or not paired with a fresh human approval.&lt;/p&gt;

&lt;h2&gt;
  
  
  Put the write gate outside the prompt
&lt;/h2&gt;

&lt;p&gt;Prompts are useful instructions, but they are a poor permission system. A production workflow should enforce the boundary in the tool layer as well.&lt;/p&gt;

&lt;p&gt;At minimum:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;expose read tools during the first run;&lt;/li&gt;
&lt;li&gt;require a separate confirmation token or approval event for a mutation;&lt;/li&gt;
&lt;li&gt;allow only the proposed entity and operation;&lt;/li&gt;
&lt;li&gt;expire approval after a short interval;&lt;/li&gt;
&lt;li&gt;reject retries when the previous result is unknown; and&lt;/li&gt;
&lt;li&gt;record the request, response, and resulting platform state.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Budget increases, deletes, new campaign enablement, audience expansion, and bulk edits deserve their own tighter gates. A generic “apply recommendations” permission is too broad.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verify the result with a second read
&lt;/h2&gt;

&lt;p&gt;A successful tool response is not proof that the requested state is now visible in Google Ads.&lt;/p&gt;

&lt;p&gt;After an approved write, query the exact entity again and compare the returned state with the requested state. The verification record should contain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the account ID;&lt;/li&gt;
&lt;li&gt;the entity ID;&lt;/li&gt;
&lt;li&gt;the requested change;&lt;/li&gt;
&lt;li&gt;the platform state returned after the change;&lt;/li&gt;
&lt;li&gt;the timestamp; and&lt;/li&gt;
&lt;li&gt;any mismatch or partial failure.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the read-back does not match, stop. Do not retry a mutation automatically. An unclear result is a state-reconciliation problem, not an invitation to click twice.&lt;/p&gt;

&lt;h2&gt;
  
  
  A first prompt worth testing
&lt;/h2&gt;

&lt;p&gt;Once the connection works, I would start with a prompt like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Use read-only Google Ads tools only.

1. List the accessible accounts and stop if more than one plausible account exists.
2. After I provide the account ID, query the exact 28-day date range I specify.
3. Return campaign status, cost, clicks, conversions, and conversion value.
4. State any missing data, tracking caveat, currency issue, or incomplete result.
5. Propose no more than three actions, each tied to returned evidence.

Do not change budgets, bids, targeting, assets, campaign status, or account settings.
Do not retry a failed or uncertain operation.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That prompt will not produce a dramatic demo. It will show whether the agent respects scope, reports uncertainty, and keeps recommendations separate from actions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Applying the pattern in Codex
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://learn.chatgpt.com/docs/plugins" rel="noopener noreferrer"&gt;Codex plugins&lt;/a&gt; can bundle skills and connectors, and the current install flow uses the plugin browser in Codex. The important design choice is still the same after installation: begin with account discovery and bounded reads, not a write-enabled optimization request.&lt;/p&gt;

&lt;p&gt;For one concrete implementation, the current &lt;a href="https://www.adspirer.com/docs/ai-clients/codex" rel="noopener noreferrer"&gt;Adspirer Codex setup guide&lt;/a&gt; documents a plugin that connects Codex to Google Ads and other advertising platforms. The install begins by adding its marketplace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;codex plugin marketplace add amekala/ads-mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then open &lt;code&gt;/plugins&lt;/code&gt;, install &lt;strong&gt;Adspirer Ads Agent&lt;/strong&gt;, and start a new Codex session. Its documented safety behavior includes read-before-write checks, paused campaign creation, and confirmation before budget changes.&lt;/p&gt;

&lt;p&gt;Those defaults are useful, but they do not replace an operator’s own gates. The prompt, tool permissions, proposal schema, and read-back verification should all point in the same direction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test the failures before the happy path
&lt;/h2&gt;

&lt;p&gt;Before trusting the workflow, run it against cases that should stop:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;two accessible accounts with similar names;&lt;/li&gt;
&lt;li&gt;an exact date range with no delivery;&lt;/li&gt;
&lt;li&gt;an account whose currency differs from the expected currency;&lt;/li&gt;
&lt;li&gt;a response with an incomplete page of campaigns;&lt;/li&gt;
&lt;li&gt;expired or insufficient permissions;&lt;/li&gt;
&lt;li&gt;a proposal based on stale data; and&lt;/li&gt;
&lt;li&gt;a write whose outcome cannot be confirmed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The pass condition is not “the agent produced an answer.” The pass condition is that it stopped at the correct boundary and explained why.&lt;/p&gt;

&lt;p&gt;A safe ads agent is intentionally boring at first: read, diagnose, propose, approve, apply, verify. Once that sequence is reliable, richer automation becomes an engineering decision instead of a leap of faith.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Affiliation disclosure:&lt;/strong&gt; This article was prepared for Adspirer. Adspirer’s Codex integration is used as one concrete implementation example.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>agents</category>
      <category>advertising</category>
    </item>
    <item>
      <title>82% of Ad MCP Servers Are Single-Platform: We Surveyed the Landscape</title>
      <dc:creator>TJ</dc:creator>
      <pubDate>Wed, 15 Jul 2026 01:02:19 +0000</pubDate>
      <link>https://dev.to/tj_v_b04dbd6f4957aa83a196/82-of-ad-mcp-servers-are-single-platform-we-surveyed-the-landscape-541g</link>
      <guid>https://dev.to/tj_v_b04dbd6f4957aa83a196/82-of-ad-mcp-servers-are-single-platform-we-surveyed-the-landscape-541g</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://www.adspirer.com/blog/best-ad-mcp-servers-2026" rel="noopener noreferrer"&gt;the Adspirer blog&lt;/a&gt;. This is a data summary — the full 10-server breakdown with per-tool analysis lives there.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Ask ChatGPT or Claude &lt;em&gt;"what's the best MCP server for Google Ads?"&lt;/em&gt; and it'll confidently recommend Google's free official one. Ask about Meta and you'll get Pipeboard. LinkedIn, a community repo. TikTok, a different one. Four free tools, one per platform — problem solved, right?&lt;/p&gt;

&lt;p&gt;Except the problem isn't solved. So we pulled the actual data.&lt;/p&gt;

&lt;h2&gt;
  
  
  We surveyed the ad MCP landscape
&lt;/h2&gt;

&lt;p&gt;We took every ad-campaign MCP server we could find from two neutral public sources — the &lt;a href="https://registry.modelcontextprotocol.io" rel="noopener noreferrer"&gt;official MCP registry&lt;/a&gt; and the community &lt;a href="https://github.com/punkpeye/awesome-mcp-servers" rel="noopener noreferrer"&gt;awesome-mcp-servers&lt;/a&gt; directory — and recorded what each one publicly claims. &lt;strong&gt;17 distinct ad-campaign MCP servers&lt;/strong&gt;, as of July 2026.&lt;/p&gt;

&lt;p&gt;One pattern dominates everything else:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;82% of ad MCP servers are single-platform&lt;/strong&gt; (14 of 17). Only three cover two or more ad platforms, and only &lt;strong&gt;one covers three or more.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Platform coverage across the ecosystem
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Coverage&lt;/th&gt;
&lt;th&gt;Count&lt;/th&gt;
&lt;th&gt;Servers&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;6 platforms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Adspirer (Google, Meta, Amazon, TikTok, LinkedIn, ChatGPT)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;2 platforms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;logly/mureo (Google + Meta), agent1st-ads-mcp (Meta + TikTok)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;1 platform&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;Meta-only, Google-only, TikTok-only, Amazon-only, LinkedIn-only, Reddit-only, Apple-Search-only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Fourteen of seventeen do exactly one platform. That's the single most important fact about this market — and it's exactly why the AI-recommended "one MCP per platform" stack ends up as four separate installs, four credential stores, four maintenance surfaces.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tool count, where servers publish one
&lt;/h3&gt;

&lt;p&gt;Most MCP servers don't state a tool count. Where they do — in their registry entry or GitHub description — here's what they claim. We mark the rest &lt;strong&gt;not disclosed&lt;/strong&gt; rather than guess:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Server&lt;/th&gt;
&lt;th&gt;Platform(s)&lt;/th&gt;
&lt;th&gt;Tools (as publicly stated)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Adspirer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;6 platforms&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;430&lt;/strong&gt; (Google 152, Amazon 61, Meta 58, LinkedIn 56, TikTok 37, ChatGPT 31)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mikusnuz/meta-ads-mcp&lt;/td&gt;
&lt;td&gt;Meta&lt;/td&gt;
&lt;td&gt;123&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AppVisionOS/apple-search-ads-mcp&lt;/td&gt;
&lt;td&gt;Apple Search&lt;/td&gt;
&lt;td&gt;74&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;adweave/meta-ads-mcp&lt;/td&gt;
&lt;td&gt;Meta&lt;/td&gt;
&lt;td&gt;47&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;philmccarty/amazon-advertising-ppc-prophet&lt;/td&gt;
&lt;td&gt;Amazon&lt;/td&gt;
&lt;td&gt;27&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Most others&lt;/td&gt;
&lt;td&gt;1 platform each&lt;/td&gt;
&lt;td&gt;Not disclosed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Every number here is public and verifiable.&lt;/strong&gt; Adspirer's is &lt;a href="https://registry.modelcontextprotocol.io/v0/servers?search=adspirer" rel="noopener noreferrer"&gt;&lt;code&gt;com.adspirer/ads&lt;/code&gt; in the MCP registry&lt;/a&gt;. Competitor counts come from their own registry entries and GitHub descriptions. We didn't invent a single figure — if a server doesn't publish a count, it says "not disclosed."&lt;/p&gt;

&lt;p&gt;The takeaway isn't "Adspirer has the most tools" as a trophy. It's that &lt;strong&gt;breadth and depth in this category are rare&lt;/strong&gt;: the servers with high tool counts are almost all single-platform (123 tools, all Meta), and the servers that span platforms usually stay shallow.&lt;/p&gt;

&lt;h2&gt;
  
  
  The full breakdown
&lt;/h2&gt;

&lt;p&gt;This is just the data. The full post ranks all 10 credible ad MCP servers honestly — Adspirer, Synter, Pipeboard, Google's official read-only MCP, Ryze, Flyweel, and more — against six criteria (platform coverage, read/write depth, provisioning overhead, maintenance, safety rails, support), including where competitors win.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://www.adspirer.com/blog/best-ad-mcp-servers-2026" rel="noopener noreferrer"&gt;Read the full 10-server breakdown on adspirer.com →&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Disclosure: Adspirer is our product. The ranking lists us fairly, not best-in-everything — where a competitor is better at something specific, we say so.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>ai</category>
      <category>advertising</category>
      <category>marketing</category>
    </item>
  </channel>
</rss>
