Claude가 내 Google Ads 계정을 관리할 수 있도록 MCP 서버를 구축한 과정 (그리고 그 과정에서 발생한 문제들)
요약
마케터가 MCP(Model Context Protocol)를 활용해 Google Ads API 기반의 MCP 서버를 구축한 사례를 다룹니다. 래퍼 방식 대신 GAQL 쿼리를 직접 노출하는 패스스루 설계와 쓰기 작업의 무결성을 검증하는 검증 로직의 중요성을 설명합니다.
핵심 포인트
- MCP를 통해 AI 어시스턴트에게 직접적인 API 호출 권한 부여 가능
- 개별 도구 생성보다 GAQL 같은 쿼리 언어를 노출하는 패스스루 방식이 효율적
- 에이전트의 쓰기 작업 오류를 방지하기 위해 '쓰기 후 읽기' 검증 필수
저는 전문 개발자가 아닌 그로스 마케팅 (Growth Marketing) 컨설턴트입니다. 이커머스 브랜드의 Google Ads와 Meta 계정을 관리하고 있는데, 작년까지만 해도 제 보고 업무 루틴은 14개의 브라우저 탭, 스프레드시트 내보내기, 그리고 다시는 되찾을 수 없는 오후 시간의 낭비였습니다.
그러다 MCP가 등장했습니다. Model Context Protocol (MCP)을 사용하면 AI 어시스턴트에게 실제로 호출할 수 있는 일련의 도구들을 넘겨줄 수 있습니다. 단순히 "여기 내 데이터가 있으니 요약해줘"가 아니라, "직접 계정에 쿼리(Query)를 날려봐"라고 할 수 있는 것이죠. 그래서 저는 Google Ads API를 기반으로 직접 MCP 서버를 구축했고, 이후 Meta, GA4, Search Console로 확장했습니다. 현재 이 서버는 약 100개의 도구를 갖추고 있으며, 저의 월간 보고, 감사(Audit), 그리고 최적화 작업의 상당 부분을 수행합니다.
이 포스트는 중요했던 세 가지 설계 선택(Design choice)과 저를 괴롭혔던 세 가지 문제에 관한 것입니다. 코딩을 조금 할 줄 아는 마케터이거나, 데모 영역을 벗어난 실제 MCP의 모습이 궁금한 개발자라면 이 글이 도움이 될 것입니다.
설계 선택 1: 래퍼(Wrapper)보다 패스스루(Passthrough)가 낫다
저의 첫 번째 본능은 제가 필요한 모든 보고서에 대해 깔끔한 래퍼(Wrapper) 도구를 만드는 것이었습니다: get_campaigns, get_keywords, get_search_terms와 같은 식이죠. 하지만 그 방식은 일주일 만에 끝났습니다. 새로운 질문이 생길 때마다 새로운 도구가 필요했고, 어시스턴트는 항상 도구가 하나씩 부족했습니다.
해결책은 부끄러울 정도로 간단했습니다: 쿼리 언어(Query language) 자체를 노출하는 것이었습니다. Google Ads에는 거의 모든 질문에 답할 수 있는 SQL 스타일의 언어인 GAQL이 있습니다. 하나의 패스스루(Passthrough) 도구가 수십 개의 래퍼를 대체했습니다:
@mcp.tool()
def google_query(customer_id: str, gaql: str) -> str:
"""Google Ads 계정에 대해 원시 GAQL 쿼리를 실행합니다."""
...
어시스턴트는 저보다 GAQL을 더 잘 작성합니다. "지난달에 전환 없이 돈만 낭비한 검색어는 무엇인가?"와 같은 질문은 다음과 같이 변환됩니다:
SELECT search_term_view.search_term, metrics.cost_micros, metrics.conversions
FROM search_term_view
WHERE segments.date DURING LAST_30_DAYS
...
정말로 복잡한 흐름(제 서버에서 캠페인 생성은 실제 가드레일(Guardrails)이 작동합니다)을 위해서는 래퍼를 유지하세요. 하지만 데이터를 읽는 용도로는 패스스루가 승리합니다.
설계 선택 2: 다시 읽지 않은 쓰기(Write) 작업은 절대 믿지 마라
읽기(Read)는 안전합니다. 쓰기(Write)는 에이전트가 당신의 일주일을 조용히 망쳐놓을 수 있는 지점입니다.
초기에 제가 만든 두 가지 변이(mutation) 도구(캠페인의 네트워크 설정 변경 및 입찰 전략 전환)는 아무것도 변경하지 않았음에도 성공했다고 보고했습니다. API는 요청을 수락했고, 도구는 "적용됨(applied)"이라고 말했지만, 계정은 정확히 이전 상태 그대로 유지되었습니다. 제가 우연히 UI를 확인했기 때문에 겨우 잡아낼 수 있었습니다.
그 이후로 제 서버에는 하나의 철칙이 생겼습니다. 모든 쓰기 작업 뒤에는 동일한 리소스에 대한 읽기 작업이 뒤따르며, 어시스턴트(assistant)가 이 두 가지를 비교합니다. 만약 후속 쿼리에서 새로운 값이 나타나지 않는다면, 응답 내용이 무엇이었든 간에 해당 쓰기 작업은 실패한 것입니다. 이 하나의 습관이 그 어떤 에러 핸들링(error handling)보다 더 많은 침묵의 실패(silent failures)를 잡아냈습니다.
두 번째 안전장치: 파괴적이거나 비용이 많이 드는 작업(캠페인 일시 중지, 예산 수정)은 명시적인 확인 절차를 거치도록 유지합니다. 어시스턴트가 제안하면, 제가 승인합니다. 지루하지만, 제 광고비가 처리되기를 바라는 정확한 방식입니다.
설계 선택 3: 실수를 위한 메모리 파일
전체 설정에서 가장 가치 있는 파일은 코드가 아닙니다. 그것은 learned-errors.md라고 불리는 평범한 마크다운(markdown) 파일입니다. 버그로 인해 10분 이상의 시간이 낭비될 때마다, 세션은 날짜, 맥락, 무엇이 잘못되었는지, 그리고 다음번에 이를 방지할 규칙을 한 줄로 기록하며 종료됩니다. 어시스턴트는 매 세션이 시작될 때 그 파일을 읽습니다.
사소해 보일 수 있습니다. 하지만 이것이 복리로 쌓입니다. 제 어시스턴트는 더 이상 3월의 실수를 반복하지 않습니다. 3월의 기록이 적혀 있기 때문입니다.
저를 괴롭혔던 세 가지 문제
이름 충돌(Name collision). 파일 상단에 import google_audit가 있었고, 그 아래에 google_audit라는 이름의 도구 함수가 있었습니다. 함수 정의가 모듈을 가려버렸고(shadowed), 갑자기 google_audit.run_audit() 호출 시 "'function' object has no attribute" 오류가 발생하며 실패했습니다. 도구가 동일한 이름을 공유할 때는 임포트(import) 시 별칭(alias)을 사용하세요.
핫 리로드(Hot reload) 불가. MCP 서버는 클라이언트가 서버를 시작할 때 코드를 한 번 로드합니다. 이전 프로세스가 여전히 실행 중이었기 때문에, "작동하지 않는" 해결책을 붙잡고 저녁 시간을 통째로 날린 적이 있습니다. 이제는 다음과 같은 의식을 치릅니다: 클라이언트를 완전히 종료하고, 가상 환경(venv)에서 모듈 임포트(import)가 깔끔하게 되는지 확인한 다음, 다시 시작합니다.
단 하나의 잘못된 임포트가 모든 것을 망칩니다. 가상 환경(venv)에 패키지를 설치하지 않고 새로운 커넥터(connector)를 추가했을 때, 서버 전체가 시작 단계에서 충돌했습니다. 단순히 새로운 기능만 안 되는 것이 아니라, 수백 개의 도구(tool)가 모두 사라졌습니다. 이제는 무엇인가를 재시작하기 전에 항상 venv/bin/python -c "import my_server" 명령어로 의존성 변경 사항을 확인합니다.
직접 구축해야 할까요?
이미 유지 관리되고 있는 MCP 서버가 사용 중인 플랫폼을 지원한다면, 그것을 사용하세요. 직접 구축하는 것이 의미 있는 경우는 워크플로우(workflow) 자체가 본인만의 경쟁력일 때입니다. 제가 구축한 서버는 제가 계정을 감사(audit)하는 방식, 검색어에서 어떤 n-gram을 추출하는지, 그리고 고객이 보기 전에 보고서가 어떤 형태여야 하는지를 인코딩(encode)하고 있습니다.
그 보상은 확실합니다. 오후 내내 걸리던 월간 보고서가 이제는 제가 커피를 만드는 동안 스스로 초안을 작성합니다. 저는 스프레드시트 작업자가 아닌 편집자의 역할을 유지합니다. 1인 컨설팅 업체에게 이것은 수익을 창출하는 시간과 시간을 허비하는 것 사이의 차이입니다.
이 설정의 마케팅 측면에 대해 궁금하시다면, mindyourgrowth.be에서 관련 글을 쓰고 있습니다. 서버 자체에 대한 질문은 댓글로 남겨주세요. 모두 읽어보고 있습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기