Skip to main content
Glama
martechery

Google Ads MCP Server

by martechery

set_session_credentials

Establish a session with Google Ads credentials in multi-tenant mode to enable API interactions for managing campaigns, retrieving performance data, and executing queries.

Instructions

Establish a session with Google Ads credentials (multi-tenant mode only).

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
session_keyYesUUID v4 session key
google_credentialsYes

Implementation Reference

  • Executes the tool logic: checks multi-tenant mode, validates session_key, establishes session with credentials via establishSession helper, optionally verifies Ads API scope, logs telemetry events, returns JSON success or structured error.
    const startTs = Date.now(); if (process.env.ENABLE_RUNTIME_CREDENTIALS !== 'true') { const out = { content: [{ type: 'text', text: 'Multi-tenant mode not enabled' }] }; logEvent('set_session_credentials', startTs, { requestId: input?.request_id, error: { code: 'ERR_NOT_ENABLED', message: 'Multi-tenant mode not enabled' } }); return out; } try { validateSessionKey(String(input?.session_key || '')); } catch (e: any) { const msg = e?.message || String(e); logEvent('set_session_credentials', startTs, { sessionKey: input?.session_key, requestId: input?.request_id, error: { code: 'ERR_INPUT', message: String(msg) } }); return { content: [{ type: 'text', text: `Error: ${msg}` }] }; } try { const out = establishSession(String(input.session_key), input.google_credentials); // Optional: verify Ads scope if enabled if (process.env.VERIFY_TOKEN_SCOPE === 'true') { try { await verifyTokenScopeForSession(String(input.session_key)); } catch (e: any) { const msg = String(e?.message || e); if (msg.startsWith('ERR_INSUFFICIENT_SCOPE')) { logEvent('set_session_credentials', startTs, { sessionKey: input?.session_key, requestId: input?.request_id, error: { code: 'ERR_INSUFFICIENT_SCOPE', message: 'Missing adwords scope' } }); return { content: [{ type: 'text', text: JSON.stringify({ error: { code: 'ERR_INSUFFICIENT_SCOPE', message: 'Access token lacks required Google Ads scope (adwords). Please re-authenticate with the correct scope.' } }) }] }; } logEvent('set_session_credentials', startTs, { sessionKey: input?.session_key, requestId: input?.request_id, error: { code: 'ERR_SCOPE_VERIFY_FAILED', message: msg } }); return { content: [{ type: 'text', text: JSON.stringify({ error: { code: 'ERR_SCOPE_VERIFY_FAILED', message: msg } }) }] }; } } const resp = { content: [{ type: 'text', text: JSON.stringify({ status: 'success', ...out }) }] }; logEvent('set_session_credentials', startTs, { sessionKey: input?.session_key, requestId: input?.request_id }); return resp; } catch (e: any) { const msg = e?.message || String(e); const code = msg.startsWith('ERR_IMMUTABLE_AUTH') ? 'ERR_IMMUTABLE_AUTH' : 'ERR_ESTABLISH'; logEvent('set_session_credentials', startTs, { sessionKey: input?.session_key, requestId: input?.request_id, error: { code, message: String(msg) } }); return { content: [{ type: 'text', text: JSON.stringify({ error: { code, message: msg } }) }] }; } }
  • Zod schema defining input parameters: session_key (UUID) and google_credentials object containing access_token, optional refresh_token, required developer_token, and optional fields.
    export const SetSessionCredentialsZ = z.object({ session_key: z.string().describe('UUID v4 session key'), google_credentials: z.object({ access_token: z.string(), refresh_token: z.string().optional(), developer_token: z.string(), login_customer_id: z.string().optional(), quota_project_id: z.string().optional(), expires_at: z.number().optional(), }), });
  • Registers the tool on the MCP server via addTool(server, name, description, schema, handler), with inline anonymous handler function.
    addTool( server, 'set_session_credentials', 'Establish a session with Google Ads credentials (multi-tenant mode only).', SetSessionCredentialsZ, async (input: any) => { const startTs = Date.now(); if (process.env.ENABLE_RUNTIME_CREDENTIALS !== 'true') { const out = { content: [{ type: 'text', text: 'Multi-tenant mode not enabled' }] }; logEvent('set_session_credentials', startTs, { requestId: input?.request_id, error: { code: 'ERR_NOT_ENABLED', message: 'Multi-tenant mode not enabled' } }); return out; } try { validateSessionKey(String(input?.session_key || '')); } catch (e: any) { const msg = e?.message || String(e); logEvent('set_session_credentials', startTs, { sessionKey: input?.session_key, requestId: input?.request_id, error: { code: 'ERR_INPUT', message: String(msg) } }); return { content: [{ type: 'text', text: `Error: ${msg}` }] }; } try { const out = establishSession(String(input.session_key), input.google_credentials); // Optional: verify Ads scope if enabled if (process.env.VERIFY_TOKEN_SCOPE === 'true') { try { await verifyTokenScopeForSession(String(input.session_key)); } catch (e: any) { const msg = String(e?.message || e); if (msg.startsWith('ERR_INSUFFICIENT_SCOPE')) { logEvent('set_session_credentials', startTs, { sessionKey: input?.session_key, requestId: input?.request_id, error: { code: 'ERR_INSUFFICIENT_SCOPE', message: 'Missing adwords scope' } }); return { content: [{ type: 'text', text: JSON.stringify({ error: { code: 'ERR_INSUFFICIENT_SCOPE', message: 'Access token lacks required Google Ads scope (adwords). Please re-authenticate with the correct scope.' } }) }] }; } logEvent('set_session_credentials', startTs, { sessionKey: input?.session_key, requestId: input?.request_id, error: { code: 'ERR_SCOPE_VERIFY_FAILED', message: msg } }); return { content: [{ type: 'text', text: JSON.stringify({ error: { code: 'ERR_SCOPE_VERIFY_FAILED', message: msg } }) }] }; } } const resp = { content: [{ type: 'text', text: JSON.stringify({ status: 'success', ...out }) }] }; logEvent('set_session_credentials', startTs, { sessionKey: input?.session_key, requestId: input?.request_id }); return resp; } catch (e: any) { const msg = e?.message || String(e); const code = msg.startsWith('ERR_IMMUTABLE_AUTH') ? 'ERR_IMMUTABLE_AUTH' : 'ERR_ESTABLISH'; logEvent('set_session_credentials', startTs, { sessionKey: input?.session_key, requestId: input?.request_id, error: { code, message: String(msg) } }); return { content: [{ type: 'text', text: JSON.stringify({ error: { code, message: msg } }) }] }; } } );
  • Key helper called by the handler: stores/replaces session credentials in a Map-based store, configures per-session customer allowlist and rate limiter, handles overwrite policy, starts idle sweeper, emits telemetry, returns TTL info.
    export function establishSession(sessionKey: string, credentials: GoogleCredential): { session_key: string; expires_in: number; overwritten?: boolean } { if (!isMultiTenantEnabled()) { throw new Error('Multi-tenant mode not enabled'); } validateSessionKey(sessionKey); if (!credentials.developer_token) { throw new Error('Developer token required in multi-tenant mode'); } const allowedIds = process.env.ALLOWED_CUSTOMER_IDS ? new Set( process.env.ALLOWED_CUSTOMER_IDS .split(',') .map((s) => s.trim()) .filter(Boolean) .map((id) => formatCustomerId(id)) ) : undefined; const now = Date.now(); const existed = connections.has(sessionKey); if (existed) { if (process.env.STRICT_IMMUTABLE_AUTH === 'true') { throw new Error('ERR_IMMUTABLE_AUTH: Authentication cannot be modified for this session'); } // Non-strict: allow overwrite (for recovery) and log via caller const ctx = connections.get(sessionKey)!; ctx.credentials = credentials; ctx.lastActivityAt = now; ctx.allowedCustomerIds = allowedIds; // best-effort log try { emitMcpEvent({ timestamp: nowIso(), tool: 'session_established', session_key: sessionKey, response_time_ms: 0, overwritten: true }); } catch (e) { void e; } } else { const rl = isRateLimitingEnabled() ? new TokenBucket( parseInt(process.env.RATE_LIMIT_BURST || '20', 10), parseFloat(process.env.REQUESTS_PER_SECOND || '10') ) : undefined; connections.set(sessionKey, { session_key: sessionKey, credentials, establishedAt: now, lastActivityAt: now, allowedCustomerIds: allowedIds, rateLimiter: rl, }); totalSessionCount++; try { emitMcpEvent({ timestamp: nowIso(), tool: 'session_established', session_key: sessionKey, response_time_ms: 0, overwritten: false }); } catch (e) { void e; } } startConnectionSweeper(); const ttlSec = parseInt(process.env.RUNTIME_CREDENTIAL_TTL || '3600', 10); return { session_key: sessionKey, expires_in: ttlSec, overwritten: existed }; }

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/martechery/mcp-google-ads-ts'

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