Skip to main content
Glama

Google Ads MCP Server

by martechery
manageAuth.test.ts4.93 kB
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; vi.mock('../../src/tools/accounts.js', () => ({ listAccessibleCustomers: vi.fn().mockResolvedValue({ ok: true, status: 200, data: { resourceNames: ['customers/123'] } }), })); vi.mock('../../src/utils/exec.js', () => ({ execCmd: vi.fn(async () => ({ code: 0, stdout: 'ok', stderr: '' })), })); type ToolHandler = (input: any) => Promise<any> | any; class FakeServer { public tools: Record<string, ToolHandler> = {}; tool(def: any, handler: ToolHandler) { this.tools[def.name] = handler; } } describe('manage_auth tool', () => { const OLD_ENV = process.env; beforeEach(() => { vi.resetModules(); vi.restoreAllMocks(); process.env = { ...OLD_ENV, GOOGLE_ADS_ACCESS_TOKEN: 't', GOOGLE_ADS_DEVELOPER_TOKEN: 'd' }; }); afterEach(() => { process.env = OLD_ENV; }); it('status prints scope check and token presence', async () => { const { registerTools } = await import('../../src/server-tools.js'); const server = new FakeServer(); registerTools(server as any); const res = await server.tools['manage_auth']({ action: 'status' }); const text = res.content[0].text as string; expect(text).toContain('Google Ads Auth Status'); expect(text).toContain('Ads scope check: OK'); }); it('switch returns instructions when allow_subprocess=false', async () => { const { registerTools } = await import('../../src/server-tools.js'); const server = new FakeServer(); registerTools(server as any); const res = await server.tools['manage_auth']({ action: 'switch', config_name: 'work', allow_subprocess: false }); const text = res.content[0].text as string; expect(text).toContain('Planned action: switch gcloud configuration'); expect(text).toContain('gcloud config configurations activate work'); }); it('switch executes when allow_subprocess=true', async () => { const { registerTools } = await import('../../src/server-tools.js'); const server = new FakeServer(); registerTools(server as any); const res = await server.tools['manage_auth']({ action: 'switch', config_name: 'work', allow_subprocess: true }); const text = res.content[0].text as string; expect(text).toContain('gcloud switch (work) exit: 0'); expect(text).toContain('Next: refresh ADC credentials'); }); it('switch executes by default when allow_subprocess is omitted', async () => { const { registerTools } = await import('../../src/server-tools.js'); const server = new FakeServer(); registerTools(server as any); const res = await server.tools['manage_auth']({ action: 'switch', config_name: 'work' }); const text = res.content[0].text as string; expect(text).toContain('gcloud switch (work) exit: 0'); }); it('refresh returns instructions without subprocess', async () => { const { registerTools } = await import('../../src/server-tools.js'); const server = new FakeServer(); registerTools(server as any); const res = await server.tools['manage_auth']({ action: 'refresh', allow_subprocess: false }); const text = res.content[0].text as string; expect(text).toContain('Planned action: refresh ADC credentials'); expect(text).toContain('gcloud auth application-default login'); }); it('refresh runs subprocess and verifies scope', async () => { const { registerTools } = await import('../../src/server-tools.js'); const server = new FakeServer(); registerTools(server as any); const res = await server.tools['manage_auth']({ action: 'refresh', allow_subprocess: true }); const text = res.content[0].text as string; expect(text).toContain('refresh login exit: 0'); expect(text).toContain('Ads scope check after refresh: OK'); }); it('refresh executes by default when allow_subprocess is omitted', async () => { const { registerTools } = await import('../../src/server-tools.js'); const server = new FakeServer(); registerTools(server as any); const res = await server.tools['manage_auth']({ action: 'refresh' }); const text = res.content[0].text as string; expect(text).toContain('refresh login exit: 0'); }); it('oauth_login uses device flow helper and verifies scope', async () => { process.env.GOOGLE_OAUTH_CLIENT_ID = 'id'; process.env.GOOGLE_OAUTH_CLIENT_SECRET = 'secret'; vi.mock('../../src/tools/oauth.js', () => ({ runDeviceOAuthForAds: vi.fn(async () => ({ path: '/tmp/adc.json' })), })); const { registerTools } = await import('../../src/server-tools.js'); const server = new FakeServer(); registerTools(server as any); // ensure list accounts is OK (already mocked at top) const res = await server.tools['manage_auth']({ action: 'oauth_login' }); const text = res.content[0].text as string; expect(text).toContain('Saved ADC authorized_user JSON: /tmp/adc.json'); }); });

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