<?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: Quinten Van Meirvenne</title>
    <description>The latest articles on DEV Community by Quinten Van Meirvenne (@quinten_vanmeirvenne_2fd).</description>
    <link>https://dev.to/quinten_vanmeirvenne_2fd</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%2F4051934%2F2a1188a5-f955-4488-9cee-660169c2559e.gif</url>
      <title>DEV Community: Quinten Van Meirvenne</title>
      <link>https://dev.to/quinten_vanmeirvenne_2fd</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/quinten_vanmeirvenne_2fd"/>
    <language>en</language>
    <item>
      <title>I built an MCP server so Claude can run my Google Ads accounts (and what broke along the way)</title>
      <dc:creator>Quinten Van Meirvenne</dc:creator>
      <pubDate>Tue, 28 Jul 2026 19:04:08 +0000</pubDate>
      <link>https://dev.to/quinten_vanmeirvenne_2fd/i-built-an-mcp-server-so-claude-can-run-my-google-ads-accounts-and-what-broke-along-the-way-n4i</link>
      <guid>https://dev.to/quinten_vanmeirvenne_2fd/i-built-an-mcp-server-so-claude-can-run-my-google-ads-accounts-and-what-broke-along-the-way-n4i</guid>
      <description>&lt;p&gt;I'm a growth marketing consultant, not a professional developer. I manage Google Ads and Meta accounts for e-commerce brands, and until last year my reporting routine was fourteen browser tabs, a spreadsheet export and an afternoon I never got back.&lt;/p&gt;

&lt;p&gt;Then MCP happened. The Model Context Protocol lets you hand an AI assistant a set of tools it can actually call: not "here is my data, please summarize", but "go query the account yourself". So I built my own MCP server on top of the Google Ads API, and later Meta, GA4 and Search Console. It now has around a hundred tools and runs my monthly reporting, audits and a good chunk of optimization work.&lt;/p&gt;

&lt;p&gt;This post is about the three design choices that mattered and the three things that bit me. If you are a marketer who codes a little, or a developer who wants to see what MCP looks like outside the demo zone, this is for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design choice 1: passthrough beats wrappers
&lt;/h2&gt;

&lt;p&gt;My first instinct was to build a neat wrapper tool for every report I needed: get_campaigns, get_keywords, get_search_terms. That lasted a week. Every new question needed a new tool, and the assistant was always one tool short.&lt;/p&gt;

&lt;p&gt;The fix was embarrassingly simple: expose the query language itself. Google Ads has GAQL, a SQL-ish language that can answer almost anything. One passthrough tool replaced a dozen wrappers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@mcp.tool&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;google_query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gaql&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Run a raw GAQL query against a Google Ads account.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GoogleAdsService&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;gaql&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;format_rows&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The assistant writes better GAQL than I do. A question like "which search terms burned money without converting last month" becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;search_term_view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;search_term&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cost_micros&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conversions&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;search_term_view&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;segments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="n"&gt;DURING&lt;/span&gt; &lt;span class="n"&gt;LAST_30_DAYS&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cost_micros&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep wrappers for the genuinely complex flows (campaign creation has real guardrails in my server). For reading data, passthrough wins.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design choice 2: never trust a write you didn't re-read
&lt;/h2&gt;

&lt;p&gt;Reads are safe. Writes are where an agent quietly ruins your week.&lt;/p&gt;

&lt;p&gt;Early on, two of my mutation tools (changing a campaign's network settings and switching bidding strategy) reported success while changing absolutely nothing. The API accepted the request, the tool said "applied", and the account stayed exactly as it was. I only caught it because I happened to check the UI.&lt;/p&gt;

&lt;p&gt;Since then my server has one iron rule: every write is followed by a read of the same resource, and the assistant compares the two. If the follow-up query does not show the new value, the write failed, whatever the response said. This one habit has caught more silent failures than any amount of error handling.&lt;/p&gt;

&lt;p&gt;The second guardrail: destructive or expensive operations (pausing campaigns, touching budgets) stay behind explicit confirmation. The assistant proposes, I approve. Boring, and exactly how I want my ad spend handled.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design choice 3: a memory file for mistakes
&lt;/h2&gt;

&lt;p&gt;The most valuable file in the whole setup is not code. It is a plain markdown file called learned-errors.md. Every time a bug costs me more than ten minutes, the session ends by logging one line: date, context, what went wrong, and the rule that prevents it next time. The assistant reads that file at the start of every session.&lt;/p&gt;

&lt;p&gt;It sounds trivial. It compounds. My assistant no longer repeats the mistakes from March, because March is written down.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three things that bit me
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The name collision.&lt;/strong&gt; I had &lt;code&gt;import google_audit&lt;/code&gt; at the top of the file and a tool function named &lt;code&gt;google_audit&lt;/code&gt; below it. The function definition shadowed the module, and suddenly &lt;code&gt;google_audit.run_audit()&lt;/code&gt; failed with "'function' object has no attribute". Alias your imports when a tool shares the name.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No hot reload.&lt;/strong&gt; An MCP server loads its code once, when the client starts it. I lost an evening staring at a fix that "didn't work" because the old process was still running. The ritual now: quit the client fully, verify the module imports cleanly in the venv, then restart.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One bad import kills everything.&lt;/strong&gt; When I added new connectors without installing their packages in the venv, the whole server crashed on startup. Not just the new feature: all hundred tools gone. Now every dependency change ends with &lt;code&gt;venv/bin/python -c "import my_server"&lt;/code&gt; before I restart anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Should you build your own?
&lt;/h2&gt;

&lt;p&gt;If a maintained MCP server already covers your platform, use that one. Building your own makes sense when the workflow itself is your edge: my server encodes how I audit accounts, which n-grams I mine from search terms, what a report should look like before a client sees it.&lt;/p&gt;

&lt;p&gt;The payoff is real. Monthly reports that took an afternoon now draft themselves while I make coffee, and I stay the editor instead of the spreadsheet operator. For a one-person consultancy, that is the difference between billing hours and losing them.&lt;/p&gt;

&lt;p&gt;If you are curious about the marketing side of this setup, I write about that at &lt;a href="https://mindyourgrowth.be" rel="noopener noreferrer"&gt;mindyourgrowth.be&lt;/a&gt;. Questions about the server itself: ask away in the comments, I read them.&lt;/p&gt;

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