Skip to main content
Glama
UnopndJude

gam-mcp

by UnopndJude

gam-mcp

A security-first MCP server for Google Ad Manager — least-privilege by default, gated writes, and a path to per-user OAuth that existing GAM MCP servers don't offer.

(한국어 설명은 아래에 있습니다 / Korean version below.)


English

What this is

gam-mcp is a self-hosted Model Context Protocol server that lets an AI client (Claude Code, Claude Desktop, etc.) read and — with explicit confirmation — write Google Ad Manager data through the Ad Manager API (Beta) REST.

It is built for an enterprise posture where billions in ad spend are at stake: the service-account key and access tokens stay inside the server process and are never exposed to the model, reads and writes are separated by OAuth scope, and every mutation requires a deliberate two-step confirmation.

Why another GAM MCP server? (the gap)

Ad Manager MCP servers already exist, but they share one architectural choice that doesn't fit a security-sensitive, multi-user org:

Project

Auth model

Transport

Gap for enterprise use

MatiousCorp/google-ad-manager-mcp

Single shared service account (GAM_CREDENTIALS_PATH), HTTP bearer token

stdio + HTTP

One "master key" for the whole network; every action attributes to the service account, so per-user accountability is lost

OrbiAds/Orbiads-GAM-MCP

Vendor-hosted managed proxy

Hosted HTTP

Credentials are entrusted to a third-party vendor

googleads/google-ads-mcp (official)

Per-user OAuth, on-behalf-of

stdio + Cloud Run

For Google Ads (advertiser), not Google Ad Manager (publisher) — different product/API

In short: the per-user OAuth on-behalf-of pattern — where each call runs against the caller's own GAM permissions and stays fully auditable — exists for Google Ads but not for Google Ad Manager. Every off-the-shelf GAM MCP server is a shared-service-account ("master key") design.

gam-mcp fills that gap. It starts as a least-privilege single-user server and is deliberately structured (transport- and auth-agnostic) to grow into per-user OAuth on-behalf-of for central deployment — the model the existing GAM servers don't provide.

Security model

Layer

Control

Where

1

Least-privilege token — the read-only OAuth scope (admanager.readonly) is requested when GAM_READ_ONLY=true, so a bug can't mutate GAM at Google's side.

src/gam/client.ts

2

Read-only kill-switch — write tools aren't even registered.

src/index.ts

3

Network allowlist — every tool re-checks the network code against GAM_NETWORK_ALLOWLIST.

src/config.ts

4

Propose → confirm gatepropose_* returns a short-lived, single-use token plus a before/after preview; nothing mutates until confirm_write.

src/security.ts

5

Append-only audit log — every call is recorded with redacted args before the result returns.

src/audit.ts

The service-account key and access tokens live in this process only and are never placed in a tool result. Claude Code's own permission prompt gates each tool call as a separate, outer layer.

Authentication modes

The client is auth-agnostic (AuthStrategy), so the same tools work under any of:

  • service_account — one shared identity. Simplest for a local, single-operator setup. This is the "master key" model every off-the-shelf GAM MCP server uses.

  • user_oauthper-user identity. Each operator runs npm run login once to authorize with their own Google account; the server then calls GAM as that user, so their own GAM role bounds every action and the audit trail attributes to a real person. Works over stdio today. Only a refresh token (never an access token) is cached, at GAM_TOKEN_STORE.

  • OBO (roadmap) — central multi-tenant on-behalf-of, where an incoming authenticated user maps to their stored per-user credential. Requires the HTTP transport; the AuthStrategy interface is the drop-in point.

Set the mode with GAM_AUTH_MODE. For user_oauth:

GAM_AUTH_MODE=user_oauth
GAM_OAUTH_CLIENT_ID=xxxx.apps.googleusercontent.com
GAM_OAUTH_CLIENT_SECRET=xxxx
npm run build && npm run login   # opens a browser, caches the refresh token

Tools

Read (always on): get_network, list_orders, get_order, list_line_items, get_line_item, run_report.

Write (only when GAM_READ_ONLY=false): propose_update_line_itemconfirm_write.

Prerequisites (one-time, in the GAM + GCP consoles)

Shared, both modes:

  1. Enable the API — GAM → Admin → Global settings → Network settings → turn on API access. Note your network code (the number in admanager.google.com/<code>#home).

  2. GCP project — enable the Google Ad Manager API.

Then, depending on GAM_AUTH_MODE:

  • service_account — create a service account, download a JSON key (never commit it), and add the service-account email as a GAM user with a least-privilege role (start read-only).

  • user_oauth — create an OAuth 2.0 Client ID (type: Desktop app) and configure the consent screen. No key file and no extra GAM user needed — each operator just has to already be a GAM user. Run npm run login to authorize.

Install

npm install
cp .env.example .env      # fill in credentials path + network code(s)
npm run build

Configure (.env)

GOOGLE_APPLICATION_CREDENTIALS=/absolute/path/to/service-account.key.json
GAM_NETWORK_ALLOWLIST=1234567
GAM_READ_ONLY=true        # flip to false only after validating reads

Register with Claude Code / Claude Desktop

{
  "mcpServers": {
    "gam": {
      "command": "node",
      "args": ["/absolute/path/to/gam-mcp/dist/index.js"],
      "env": {
        "GOOGLE_APPLICATION_CREDENTIALS": "/absolute/path/to/service-account.key.json",
        "GAM_NETWORK_ALLOWLIST": "1234567",
        "GAM_READ_ONLY": "true"
      }
    }
  }
}

API surface & gaps

Uses the Ad Manager API (Beta) REST (https://admanager.googleapis.com/v1). Not every mutating operation exists in Beta yet — some remain SOAP-only. Where a write endpoint isn't live, confirm_write surfaces the GAM error verbatim; a SOAP fallback is future work.

Roadmap

  • Per-user OAuth identity for local/stdio (GAM_AUTH_MODE=user_oauth)

  • Central multi-tenant on-behalf-of (needs HTTP transport + IdP)

  • Streamable HTTP transport

  • SOAP fallback for operations not yet in Beta REST

  • Rate limiting and budget/impression caps on writes


Related MCP server: Google MCP Server

한국어

개요

gam-mcp는 AI 클라이언트(Claude Code, Claude Desktop 등)가 Ad Manager API (Beta) REST를 통해 Google Ad Manager 데이터를 읽고, 명시적 확인을 거쳐 쓰도록 해주는 self-hosted MCP 서버입니다.

수십억 규모의 광고 집행을 다루는 보안 민감 환경을 전제로 설계했습니다 — 서비스 계정 키와 액세스 토큰은 서버 프로세스 안에만 존재하며 모델에 노출되지 않고, 읽기/쓰기는 OAuth scope로 분리되며, 모든 변경은 2단계 확인을 거쳐야 합니다.

왜 또 다른 GAM MCP 서버인가? (채우는 공백)

Ad Manager용 MCP 서버는 이미 있지만, 전부 보안 민감·다중 사용자 조직에 맞지 않는 하나의 설계를 공유합니다:

프로젝트

인증 모델

전송

엔터프라이즈 관점의 공백

MatiousCorp/google-ad-manager-mcp

단일 공유 서비스 계정 + HTTP bearer

stdio + HTTP

네트워크 전체가 하나의 "마스터 키" → 모든 액션이 서비스 계정에 귀속되어 사용자별 추적성 상실

OrbiAds/Orbiads-GAM-MCP

벤더 호스팅 관리형 프록시

호스티드 HTTP

자격증명을 외부 벤더에 위탁

googleads/google-ads-mcp (공식)

per-user OAuth, on-behalf-of

stdio + Cloud Run

Google Ads(광고주) 용, Ad Manager(퍼블리셔) 아님 — 다른 제품/API

요약하면, 각 호출이 호출자 본인의 GAM 권한으로 실행되고 완전히 감사 가능한 per-user OAuth on-behalf-of 패턴은 Google Ads에는 있지만 Google Ad Manager에는 없습니다. 기성 GAM MCP 서버는 전부 공유 서비스 계정("마스터 키") 방식입니다.

gam-mcp가 그 공백을 메웁니다. 최소 권한 단일 사용자 서버로 시작하되, 중앙 배포용 per-user OAuth on-behalf-of로 확장할 수 있도록 transport·auth-agnostic하게 설계했습니다 — 기존 GAM 서버가 제공하지 않는 모델입니다.

보안 모델

계층

통제

위치

1

최소 권한 토큰GAM_READ_ONLY=true면 읽기 전용 scope(admanager.readonly)만 발급 → 버그가 있어도 Google 쪽에서 변경 불가

src/gam/client.ts

2

읽기 전용 kill-switch — 쓰기 도구를 아예 등록하지 않음

src/index.ts

3

네트워크 allowlist — 모든 도구가 네트워크 코드를 재검증

src/config.ts

4

propose → confirm 게이트propose_*는 단회성·만료 토큰 + before/after 미리보기만 반환, confirm_write 전엔 변경 없음

src/security.ts

5

append-only 감사 로그 — 모든 호출을 redaction 후 기록

src/audit.ts

키·토큰은 서버 프로세스에만 존재하고 도구 결과에 절대 담기지 않습니다. Claude Code 자체 permission 프롬프트가 별도의 바깥 계층으로 각 호출을 게이트합니다.

인증 모드

클라이언트가 auth-agnostic(AuthStrategy)이라 아래 어느 방식이든 같은 도구가 동작합니다:

  • service_account — 단일 공유 신원. 로컬 단일 운영자에게 가장 단순. 기성 GAM MCP가 모두 쓰는 "마스터 키" 모델.

  • user_oauth사용자별 신원. 각 운영자가 npm run login으로 자기 Google 계정을 1회 인증하면, 서버가 그 사용자로서 GAM을 호출 → 본인의 GAM role이 모든 액션의 한도가 되고 감사 로그가 실제 사람에게 귀속됩니다. 지금 stdio에서 동작하며, refresh token만(액세스 토큰은 절대 저장 안 함) GAM_TOKEN_STORE에 캐시됩니다.

  • OBO (로드맵) — 중앙 멀티테넌트 on-behalf-of. 인증된 사용자를 그의 저장된 자격증명에 매핑. HTTP transport 필요하며, AuthStrategy 인터페이스가 그 확장 지점입니다.

GAM_AUTH_MODE로 선택. user_oauth 예:

GAM_AUTH_MODE=user_oauth
GAM_OAUTH_CLIENT_ID=xxxx.apps.googleusercontent.com
GAM_OAUTH_CLIENT_SECRET=xxxx
npm run build && npm run login   # 브라우저 인증 → refresh token 캐시

도구

읽기(상시): get_network, list_orders, get_order, list_line_items, get_line_item, run_report.

쓰기(GAM_READ_ONLY=false일 때만): propose_update_line_itemconfirm_write.

사전 준비 (GAM + GCP 콘솔, 1회)

공통 (두 모드 모두):

  1. API 활성화 — GAM → Admin → Global settings → Network settings → API access 켜기. 네트워크 코드(admanager.google.com/<코드>#home의 숫자) 확인.

  2. GCP 프로젝트Google Ad Manager API 활성화.

그다음 GAM_AUTH_MODE에 따라:

  • service_account — 서비스 계정 생성 → JSON 키 다운로드(커밋 금지) → 서비스 계정 이메일을 GAM 사용자로 추가하고 최소 권한 role 부여(읽기 전용으로 시작).

  • user_oauthOAuth 2.0 Client ID(유형: 데스크톱 앱) 생성 + consent 화면 설정. 키 파일·추가 GAM 사용자 불필요 — 각 운영자가 이미 GAM 사용자이기만 하면 됨. npm run login으로 인증.

설치 · 설정 · 등록

위 English 섹션의 명령/설정과 동일합니다.

API 범위와 한계

Ad Manager API (Beta) REST(https://admanager.googleapis.com/v1)를 사용합니다. 일부 변경 작업은 아직 Beta에 없고 SOAP 전용입니다. REST 엔드포인트가 없는 경우 confirm_write가 GAM 오류를 그대로 전달하며, SOAP 폴백은 향후 과제입니다.


License

MIT

A
license - permissive license
-
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/UnopndJude/gam-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server