서버 애플리케이션에 Google Analytics MCP를 붙이려다 생각보다 오래 헤맸다. MCP 서버 설치 자체는 어렵지 않았다. 문제는 그 다음이었다. 서버 런타임이 어떤 credential을 봐야 하는지, 왜 브라우저에서 OAuth 앱이 막히는지, service account를 GA4 사용자로 넣으면 왜 안 되는지 계속 섞였다. 최종적으로 "내가 볼 수 있는 모든 GA property"를 서버 앱에서 MCP로 조회하려면 어떤 방식이 맞는지도 분명하지 않았다.
이전에 Growth OS의 GA fetch를 cron에서 self-healing cache로 바꾼 이유를 쓰면서 Google 권한을 세 문으로 나눠야 한다고 정리했다.
- token을 만들 수 있는가.
- GA4 property를 읽을 권한이 있는가.
- 런타임이 credential을 실제로 전달받는가.
이번 MCP 세팅도 결국 같은 문제였다. 다만 단순한 GA 수집 배치가 아니라 서버 앱에 MCP를 붙여 내가 접근 가능한 GA 계정과 property를 탐색하는 흐름이었다. 그래서 고정된 property를 반복 수집하는 service account보다, 내 Google 계정의 권한을 가진 OAuth ADC가 더 맞았다.
요약
- 문제: 서버 앱에서 Google Analytics MCP 서버는 뜨는데 실제 GA 계정 목록을 읽는 단계에서 scope 부족, OAuth consent, service account 권한 문제가 번갈아 나왔다.
- 판단: "Google 인증" 하나로 보지 않고 OAuth ADC와 service account를 분리해서 봐야 했다.
- 해결: 서버 앱이라도 목적이 개인 계정이 접근 가능한 GA 전체를 탐색하는 것이었으므로 OAuth ADC 방식으로 끝냈다.
- 배운 점: service account는 고정된 운영 자동화에 좋고, OAuth는 사람 계정의 기존 GA 접근권한을 서버 앱의 MCP 런타임에서 그대로 쓰고 싶을 때 맞다.
처음에는 MCP 설치 문제처럼 보였다
처음 목표는 단순했다. 서버 애플리케이션에 Google Analytics MCP를 붙이고, 내가 접근 가능한 GA property들을 질문으로 조회하고 싶었다.
공식 설정은 대략 이런 형태다.
[mcp_servers.analytics-mcp]
command = "pipx"
args = ["run", "analytics-mcp"]
[mcp_servers.analytics-mcp.env]
GOOGLE_APPLICATION_CREDENTIALS = "/path/to/credentials.json"
GOOGLE_PROJECT_ID = "your-google-cloud-project-id"pipx run analytics-mcp는 바로 실행됐다. MCP protocol도 정상적으로 응답했고, tool 목록도 나왔다.
get_account_summaries
get_property_details
run_report
run_realtime_report
run_funnel_report
run_conversions_report여기까지만 보면 세팅이 끝난 것처럼 보인다. 하지만 실제로 get_account_summaries를 호출하자 막혔다.
ACCESS_TOKEN_SCOPE_INSUFFICIENT이 에러가 중요한 갈림길이었다. credential 파일이 없다는 뜻이 아니었다. 기존 ADC가 있기는 했지만 Google Analytics를 읽을 scope로 발급된 credential이 아니었다.
ADC가 있다고 충분한 것은 아니었다
작업 환경에는 이미 application_default_credentials.json이 있었다. 그래서 처음에는 "이미 Google 로그인이 되어 있는데 왜 안 되지?"라고 생각했다.
하지만 ADC는 파일 존재 여부만 보면 안 된다. 어떤 OAuth client로 발급됐는지, 어떤 scope를 요청했는지에 따라 API 호출 결과가 달라진다.
Google Analytics MCP는 내부에서 Analytics read-only scope를 요구한다. 그러면 ADC도 이 scope를 포함해서 다시 발급해야 한다.
gcloud auth application-default login \
--scopes=https://www.googleapis.com/auth/analytics.readonly,https://www.googleapis.com/auth/cloud-platform \
--client-id-file=/path/to/oauth-client.json여기서 --client-id-file을 뺀 채 기본 Cloud SDK OAuth client로 진행하면 민감 scope 요청에서 차단될 수 있다. 실제 브라우저에는 이런 화면이 나왔다.
This app is blocked
그래서 Desktop OAuth client를 직접 만들고, 그 JSON을 --client-id-file로 넘겼다.
Desktop OAuth client는 Google Auth Platform의 Clients 화면에서 만든다. 이때 application type은 웹 애플리케이션이 아니라 데스크톱 앱을 골라야 한다.
OAuth consent 화면도 실패처럼 보였다
Desktop OAuth client를 만들고 다시 시도했는데, 이번에는 다른 화면이 나왔다.
Google hasn't verified this app처음 보면 실패처럼 보이지만 이 화면은 막힌 것이 아니다. OAuth consent app이 테스트 또는 미검증 상태일 때 나오는 경고다. 내가 만든 앱이고 테스트 사용자로 등록된 계정이라면 Advanced를 눌러 계속 진행할 수 있다.
반대로 이런 화면은 실제로 막힌 것이다.
Access blocked: app has not completed the Google verification process이 경우에는 현재 로그인한 Google 계정이 OAuth consent app의 test user가 아니거나 앱 설정이 아직 접근 가능한 상태가 아니다.
정리하면 OAuth에서 확인할 것은 세 가지였다.
- OAuth client가
Desktop app타입인가. - OAuth consent app에 현재 로그인 계정이 test user로 들어가 있는가.
- ADC를
analytics.readonlyscope로 다시 발급했는가.
이 세 가지가 맞아야 서버 앱의 MCP 런타임에서 내 Google 계정이 볼 수 있는 GA 계정 목록을 읽을 수 있다.
service account는 다른 문제였다
중간에 service account 방식도 시도했다. 운영 서버나 배치 작업이라면 이 방식이 더 자연스럽다. 사람이 브라우저에서 승인하지 않아도 되고 key 파일을 런타임에 전달하면 token을 만들 수 있다.
하지만 목적이 달랐다. 나는 서버 앱에 붙인 MCP에서 "내 Google 계정이 볼 수 있는 모든 GA property"를 보고 싶었다. service account는 내 Google 계정의 권한을 상속받지 않는다. 완전히 별도의 identity다.
그래도 service account 방식을 쓰려면 흐름은 이렇게 된다.
PROJECT_ID="your-google-cloud-project-id"
SA_NAME="analytics-mcp"
KEY_PATH="$HOME/Downloads/analytics-mcp-service-account.json"
gcloud config set project "$PROJECT_ID"
gcloud services enable \
analyticsadmin.googleapis.com \
analyticsdata.googleapis.com \
--project="$PROJECT_ID"
gcloud iam service-accounts create "$SA_NAME" \
--project="$PROJECT_ID" \
--display-name="Google Analytics MCP"
SA_EMAIL="$SA_NAME@$PROJECT_ID.iam.gserviceaccount.com"
gcloud iam service-accounts keys create "$KEY_PATH" \
--iam-account="$SA_EMAIL" \
--project="$PROJECT_ID"그 다음에는 GA4 쪽에서 이 service account 이메일에 Viewer 또는 Analyst 권한을 줘야 한다.
여기서도 함정이 있었다. GA4 UI의 Add users 화면은 일반 Google 계정 이메일만 받는 경우가 있다. service account 이메일을 넣으면 이런 에러가 날 수 있다.
이메일이 Google 계정과 일치하지 않습니다.이건 service account key가 틀렸다는 뜻이 아니다. GA4 UI의 사용자 추가 흐름과 service account identity가 맞지 않는 경우다. 이때는 GA Admin API의 access binding을 써야 할 수 있다.
GA4_PROPERTY_ID="your-ga4-property-id"
SA_EMAIL="analytics-mcp@your-google-cloud-project-id.iam.gserviceaccount.com"
ACCESS_TOKEN="$(gcloud auth print-access-token)"
curl -sS -X POST \
"https://analyticsadmin.googleapis.com/v1alpha/properties/${GA4_PROPERTY_ID}/accessBindings" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d "{
\"user\": \"${SA_EMAIL}\",
\"roles\": [\"predefinedRoles/viewer\"]
}"다만 이 방식은 access binding을 만드는 사용자에게 GA property의 Administrator 권한과 analytics.manage.users scope가 필요하다.
OAuth와 service account의 선택 기준
이번에 가장 크게 배운 것은 둘 중 하나가 더 좋은 방식은 아니라는 점이다. 목적이 다르다.
OAuth는 사람 계정의 권한을 쓴다. 내가 GA UI에서 볼 수 있는 account와 property를 서버 앱의 MCP에서도 보고 싶다면 OAuth가 맞다. 대신 OAuth consent, test user, scope, ADC 갱신을 제대로 통과해야 한다.
service account는 운영 자동화 identity를 쓴다. 서버 캐시, 배치, cron, 특정 property의 서버 지표 수집처럼 사람이 매번 승인하면 안 되는 작업에는 이 방식이 맞다. 대신 GA account 또는 property마다 service account에 명시적으로 권한을 줘야 한다.
그래서 선택 기준은 이렇게 잡는 편이 낫다.
- 서버 앱의 MCP에서 내가 접근 가능한 GA 전체를 탐색한다: OAuth ADC
- 특정 제품의 운영 지표를 서버가 반복해서 가져온다: service account
- 여러 사람이 같은 숫자를 봐야 한다: 서버 캐시와 service account
- 한 사람이 자기 권한으로 질문한다: OAuth
이 기준 없이 "Google credential"이라고만 부르면 디버깅이 길어진다.
최종 setup 흐름
내 경우 최종적으로는 OAuth ADC 방식으로 끝냈다.
먼저 Cloud project에서 Analytics API를 켠다.
gcloud services enable \
analyticsadmin.googleapis.com \
analyticsdata.googleapis.com \
--project="your-google-cloud-project-id"그다음 Google Auth Platform에서 Desktop OAuth client를 만든다. OAuth consent app이 테스트 상태라면 현재 로그인할 Google 계정을 test user에 추가한다.
다운로드한 OAuth client JSON으로 ADC를 다시 발급한다.
gcloud auth application-default login \
--scopes=https://www.googleapis.com/auth/analytics.readonly,https://www.googleapis.com/auth/cloud-platform \
--client-id-file=/path/to/oauth-client.json성공하면 아래 파일이 갱신된다.
~/.config/gcloud/application_default_credentials.json서버 앱에서 실행되는 MCP 설정은 이 ADC 파일을 보게 한다.
[mcp_servers.analytics-mcp]
command = "pipx"
args = ["run", "analytics-mcp"]
[mcp_servers.analytics-mcp.env]
GOOGLE_APPLICATION_CREDENTIALS = "/Users/you/.config/gcloud/application_default_credentials.json"
GOOGLE_PROJECT_ID = "your-google-cloud-project-id"
GOOGLE_CLOUD_PROJECT = "your-google-cloud-project-id"마지막으로 서버 앱의 MCP tool이 실제 GA 계정 목록을 읽는지 확인한다.
get_account_summaries여기서 계정과 property 목록이 나오면 끝이다. 빈 배열이면 해당 Google 계정이 볼 수 있는 GA 계정이 없거나 다른 계정으로 OAuth를 완료했을 가능성이 높다. scope 부족이면 ADC를 다시 발급해야 한다.
다음에는 이렇게 판단한다
Google Analytics API를 붙일 때는 "credential이 있나?"로 시작하지 않는다. 먼저 어떤 identity로 접근할지 정한다.
- 내 Google 계정으로 볼 것인가.
- 서버용 service account로 볼 것인가.
- 특정 property만 필요한가.
- 내가 접근 가능한 전체 GA 계정이 필요한가.
그 다음에 확인 순서를 나눈다.
- identity가 token을 만들 수 있는가.
- token에 필요한 scope가 있는가.
- 그 identity가 GA account 또는 property 권한을 갖고 있는가.
- MCP나 서버 런타임이 같은 credential 파일을 보고 있는가.
이번 MCP 세팅에서 오래 걸린 이유는 설치가 어려워서가 아니었다. OAuth와 service account를 같은 "Google 인증"으로 묶어 봤기 때문이다. 둘을 나누자 문제가 단순해졌다.
고정된 운영 자동화에는 service account가 맞고, 개인 계정의 GA 접근권한을 그대로 쓰는 서버 앱 MCP에는 OAuth가 맞다. 이 기준을 먼저 정하면 Google 인증 디버깅은 훨씬 짧아진다.
