Skip to content

heidrian-eth/digital-ads-remote-mcp

Repository files navigation

Digital Ads Remote MCP

Remote deployment wrapper for digital ads MCP servers. Currently supports:

Features

  • HTTP JSON-RPC Transport: Cloud-native HTTP-based MCP server
  • Stateless Architecture: No session storage, scales to zero
  • API Key Authentication: Validate clients via API keys
  • Per-Request Credentials: Users provide their own platform-specific credentials (e.g., Google Ads developer tokens)
  • Multi-Cloud Ready: Deploy to any container platform
  • Auto-Updates: Uses git submodule to track upstream changes

Architecture

This repository wraps digital ads MCP servers (such as google-ads-mcp) as git submodules and adds:

  • Remote server transport (HTTP JSON-RPC)
  • Authentication layer (API keys)
  • Credential injection (per-request platform credentials)
  • Container deployment configuration

Local Development

# Clone with submodule
git clone --recurse-submodules <your-repo-url>
cd digital-ads-remote-mcp

# Build Docker image
docker build -t digital-ads-remote-mcp .

# Run locally for testing
docker run -p 8080:8080 \
  -e ALLOWED_API_KEYS="test-key-1,test-key-2" \
  -e GOOGLE_APPLICATION_CREDENTIALS="/path/to/credentials.json" \
  -v /path/to/credentials.json:/path/to/credentials.json:ro \
  digital-ads-remote-mcp

Deployment

Google Cloud Run

# Build and push to Google Container Registry
gcloud builds submit --tag gcr.io/PROJECT_ID/digital-ads-remote-mcp

# Deploy to Cloud Run
gcloud run deploy digital-ads-remote-mcp \
  --image=gcr.io/PROJECT_ID/digital-ads-remote-mcp \
  --platform=managed \
  --region=us-central1 \
  --allow-unauthenticated \
  --set-env-vars=ALLOWED_API_KEYS="key1,key2"

AWS Fargate

# Build and push to ECR
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com
docker build -t digital-ads-remote-mcp .
docker tag digital-ads-remote-mcp:latest ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com/digital-ads-remote-mcp:latest
docker push ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com/digital-ads-remote-mcp:latest

# Deploy via AWS CLI (ensure task definition and service exist)
aws ecs update-service \
  --cluster your-cluster \
  --service digital-ads-remote-mcp \
  --force-new-deployment

Azure Container Apps

# Build and push to Azure Container Registry
az acr build --registry YOUR_ACR_NAME --image digital-ads-remote-mcp:latest .

# Deploy to Azure Container Apps
az containerapp create \
  --name digital-ads-remote-mcp \
  --resource-group YOUR_RESOURCE_GROUP \
  --environment YOUR_CONTAINER_APP_ENV \
  --image YOUR_ACR_NAME.azurecr.io/digital-ads-remote-mcp:latest \
  --target-port 8080 \
  --ingress external \
  --env-vars ALLOWED_API_KEYS="key1,key2"

Encryption Setup

Environment variables can be encrypted using ECIES (Elliptic Curve Integrated Encryption Scheme) with secp256k1.

Generate Key Pair

# Install eciespy (uses coincurve under the hood)
pip install eciespy

# Generate a new key pair
python3 -c "
from coincurve import PrivateKey
import secrets

sk = PrivateKey(secrets.token_bytes(32))
print(f'Private key (keep secret): {sk.secret.hex()}')
print(f'Public key (for clients):  {sk.public_key.format(False).hex()}')
"

# Derive public key from an existing hex private key
python3 -c "
from coincurve import PrivateKey
private_key_hex = 'YOUR_PRIVATE_KEY_HEX'
sk = PrivateKey(bytes.fromhex(private_key_hex))
print(sk.public_key.format(False).hex())
"

Encrypt Values (Client-Side)

# Encrypt a string
python3 -c "
import ecies
import base64
import sys

public_key = 'YOUR_PUBLIC_KEY_HEX'
plaintext = sys.argv[1]

ciphertext = ecies.encrypt(public_key, plaintext.encode())
print(base64.urlsafe_b64encode(ciphertext).decode().rstrip('='))
" "your-secret-value"

# Encrypt a file
python3 -c "
import ecies
import base64
import sys

public_key = 'YOUR_PUBLIC_KEY_HEX'
with open(sys.argv[1], 'rb') as f:
    content = f.read()

ciphertext = ecies.encrypt(public_key, content)
print(base64.urlsafe_b64encode(ciphertext).decode().rstrip('='))
" credentials.json

Client Configuration

Environment variables are passed via query parameters with type prefixes:

Prefix Description Example
PLAIN_ Plaintext value ?PLAIN_FOO=barFOO=bar
FILE_ Content written to temp file ?FILE_CREDS={}CREDS=/tmp/xxx.json
ENC_ Encrypted value (base64url) ?ENC_TOKEN=abc...TOKEN=decrypted
ENCFILE_ Encrypted content to temp file ?ENCFILE_CREDS=abc...CREDS=/tmp/xxx.json

MCP Client Configuration

{
  "mcpServers": {
    "google-ads": {
      "url": "https://your-service-url/googleads/mcp?api_key=your-api-key&ENC_GOOGLE_ADS_DEVELOPER_TOKEN=abc123...&PLAIN_GOOGLE_ADS_LOGIN_CUSTOMER_ID=1234567890",
      "transport": "http"
    },
    "google-analytics": {
      "url": "https://your-service-url/analytics/mcp?api_key=your-api-key&ENCFILE_GOOGLE_APPLICATION_CREDENTIALS=xyz789...",
      "transport": "http"
    },
    "facebook-ads": {
      "url": "https://your-service-url/facebookads/mcp?api_key=your-api-key&ENC_FB_ACCESS_TOKEN=abc123...",
      "transport": "http"
    }
  }
}

Required Parameters

Google Ads MCP (/googleads/mcp):

  • GOOGLE_ADS_DEVELOPER_TOKEN: Your Google Ads API developer token
  • GOOGLE_ADS_LOGIN_CUSTOMER_ID: (Optional) Manager customer ID
  • GOOGLE_APPLICATION_CREDENTIALS: (Optional) Path to service account JSON (use FILE_ or ENCFILE_ prefix)

Google Analytics MCP (/analytics/mcp):

  • GOOGLE_APPLICATION_CREDENTIALS: Path to service account JSON (use FILE_ or ENCFILE_ prefix)
  • GOOGLE_PROJECT_ID: Google Cloud project ID
  • ANALYTICS_PROPERTY_ID: (Optional) Default GA4 property ID

Facebook Ads MCP (/facebookads/mcp):

  • FB_ACCESS_TOKEN: Facebook User Access Token with ads_read permission (required)

Keeping Up-to-Date

The MCP servers are included as git submodules. To update:

# Update all submodules to latest version
git submodule update --remote

# Commit the updates
git add google-ads-mcp google-analytics-mcp facebook-ads-mcp
git commit -m "Update MCP submodules to latest"

# Redeploy to your platform

Environment Variables

Server-side environment variables:

  • ALLOWED_API_KEYS: Comma-separated list of valid API keys
  • ECIES_PRIVATE_KEY: Hex-encoded secp256k1 private key for decrypting ENC_* and ENCFILE_* parameters

Security Considerations

  1. API Keys: Store in your platform's secret manager, rotate regularly
  2. Developer Tokens: Never stored server-side, passed per-request
  3. HTTPS Only: Ensure your platform enforces HTTPS
  4. Service Account: Use minimal permissions for Google Ads API access
  5. CORS: Configure allow_origins in production

Transport

The server speaks the MCP Streamable HTTP transport at POST /{service}/mcp. Session state is tracked via the Mcp-Session-Id response/request header:

  1. First request (initialize) is sent without a session header. Env-injection query params (PLAIN_*, FILE_*, ENC_*, ENCFILE_*) are read here and configure the spawned stdio subprocess.
  2. The server returns an Mcp-Session-Id header; all subsequent requests for that session MUST include it.
  3. DELETE /{service}/mcp with the header closes the session.

Responses are returned as text/event-stream; the final event carries the JSON-RPC response. Notification-only requests return 202 Accepted.

Testing

# Health check
curl https://your-service-url/health

# Initialize (captures the Mcp-Session-Id from response headers)
SID=$(curl -sD - -o /dev/null \
  -X POST "https://your-service-url/googleads/mcp?api_key=your-api-key&PLAIN_GOOGLE_ADS_DEVELOPER_TOKEN=your-dev-token" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"curl","version":"1"}}}' \
  | awk 'tolower($1)=="mcp-session-id:"{print $2}' | tr -d '\r')

# Subsequent calls reuse the session id via the header
curl -X POST "https://your-service-url/googleads/mcp?api_key=your-api-key" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "Mcp-Session-Id: $SID" \
  -d '{"jsonrpc":"2.0","method":"notifications/initialized"}'

curl -X POST "https://your-service-url/googleads/mcp?api_key=your-api-key" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "Mcp-Session-Id: $SID" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'

# Close the session
curl -X DELETE "https://your-service-url/googleads/mcp?api_key=your-api-key" \
  -H "Mcp-Session-Id: $SID"

Troubleshooting

  • 401 Unauthorized: Check api_key query param matches ALLOWED_API_KEYS
  • 400 Invalid params: Check encryption/decryption - ensure ECIES_PRIVATE_KEY is set
  • 500 Internal Error: Check container logs for MCP or API errors

License

Apache 2.0 / MIT (same as upstream MCP servers: google-ads-mcp, google-analytics-mcp, facebook-ads-mcp)

About

Remote MCPs wrapper over the official local MCPs from various digital ads tools

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors