@channel47/google-ads-mcp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,166 @@
1
+ // Mutation keywords to block for read-only safety
2
+ const MUTATION_KEYWORDS = ['create', 'update', 'remove', 'mutate', 'delete'];
3
+
4
+ /**
5
+ * Validate that required parameters are present
6
+ * @param {Object} params - Parameters object
7
+ * @param {string[]} fields - Required field names
8
+ * @throws {Error} If any required field is missing
9
+ */
10
+ export function validateRequired(params, fields) {
11
+ const missing = fields.filter(field => {
12
+ const value = params[field];
13
+ return value === undefined || value === null || value === '';
14
+ });
15
+
16
+ if (missing.length > 0) {
17
+ throw new Error(`Missing required parameter${missing.length > 1 ? 's' : ''}: ${missing.join(', ')}`);
18
+ }
19
+ }
20
+
21
+ /**
22
+ * Validate that a value is one of the allowed enum values
23
+ * @param {string} value - Value to check
24
+ * @param {string[]} allowed - Allowed values
25
+ * @param {string} paramName - Parameter name for error message
26
+ * @throws {Error} If value is not in allowed list
27
+ */
28
+ export function validateEnum(value, allowed, paramName = 'value') {
29
+ if (!allowed.includes(value)) {
30
+ throw new Error(
31
+ `Invalid ${paramName}: ${value}. Allowed values: ${allowed.join(', ')}`
32
+ );
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Parse and validate date range
38
+ * @param {string} range - Predefined range or 'CUSTOM'
39
+ * @param {string} [startDate] - Start date for CUSTOM range (YYYY-MM-DD)
40
+ * @param {string} [endDate] - End date for CUSTOM range (YYYY-MM-DD)
41
+ * @returns {{ start: string, end: string }} Formatted dates (YYYY-MM-DD)
42
+ * @throws {Error} If range is invalid or CUSTOM dates are missing
43
+ */
44
+ export function validateDateRange(range, startDate, endDate) {
45
+ const now = new Date();
46
+ const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
47
+
48
+ /**
49
+ * Format date as YYYY-MM-DD
50
+ */
51
+ function formatDate(date) {
52
+ const year = date.getFullYear();
53
+ const month = String(date.getMonth() + 1).padStart(2, '0');
54
+ const day = String(date.getDate()).padStart(2, '0');
55
+ return `${year}-${month}-${day}`;
56
+ }
57
+
58
+ const ranges = {
59
+ 'TODAY': () => ({ start: today, end: today }),
60
+ 'YESTERDAY': () => {
61
+ const yesterday = new Date(today);
62
+ yesterday.setDate(yesterday.getDate() - 1);
63
+ return { start: yesterday, end: yesterday };
64
+ },
65
+ 'LAST_7_DAYS': () => {
66
+ const start = new Date(today);
67
+ start.setDate(start.getDate() - 7);
68
+ return { start, end: today };
69
+ },
70
+ 'LAST_14_DAYS': () => {
71
+ const start = new Date(today);
72
+ start.setDate(start.getDate() - 14);
73
+ return { start, end: today };
74
+ },
75
+ 'LAST_30_DAYS': () => {
76
+ const start = new Date(today);
77
+ start.setDate(start.getDate() - 30);
78
+ return { start, end: today };
79
+ },
80
+ 'LAST_90_DAYS': () => {
81
+ const start = new Date(today);
82
+ start.setDate(start.getDate() - 90);
83
+ return { start, end: today };
84
+ },
85
+ 'THIS_MONTH': () => {
86
+ const start = new Date(today.getFullYear(), today.getMonth(), 1);
87
+ return { start, end: today };
88
+ },
89
+ 'LAST_MONTH': () => {
90
+ const start = new Date(today.getFullYear(), today.getMonth() - 1, 1);
91
+ const end = new Date(today.getFullYear(), today.getMonth(), 0);
92
+ return { start, end };
93
+ },
94
+ 'CUSTOM': () => {
95
+ if (!startDate || !endDate) {
96
+ throw new Error('start_date and end_date required for CUSTOM range');
97
+ }
98
+ // Parse dates as local time to avoid timezone conversion issues
99
+ const [startYear, startMonth, startDay] = startDate.split('-').map(Number);
100
+ const [endYear, endMonth, endDay] = endDate.split('-').map(Number);
101
+ return {
102
+ start: new Date(startYear, startMonth - 1, startDay),
103
+ end: new Date(endYear, endMonth - 1, endDay)
104
+ };
105
+ }
106
+ };
107
+
108
+ if (!ranges[range]) {
109
+ throw new Error(
110
+ `Invalid date_range: ${range}. Allowed values: ${Object.keys(ranges).join(', ')}`
111
+ );
112
+ }
113
+
114
+ const { start, end } = ranges[range]();
115
+
116
+ return {
117
+ start: formatDate(start),
118
+ end: formatDate(end)
119
+ };
120
+ }
121
+
122
+ /**
123
+ * Get customer ID from params or environment, with validation
124
+ * Consolidates the common pattern used across all tools
125
+ * @param {Object} params - Parameters object with optional customer_id
126
+ * @returns {string} Valid customer ID
127
+ * @throws {Error} If no customer ID is available
128
+ */
129
+ export function getCustomerId(params = {}) {
130
+ const customerId = params.customer_id || process.env.GOOGLE_ADS_DEFAULT_CUSTOMER_ID;
131
+ if (!customerId) {
132
+ throw new Error('customer_id parameter or GOOGLE_ADS_DEFAULT_CUSTOMER_ID environment variable required');
133
+ }
134
+ return customerId;
135
+ }
136
+
137
+ /**
138
+ * Build common campaign ID filter clause
139
+ * @param {string[]} campaignIds - Array of campaign IDs to filter
140
+ * @returns {string} GAQL filter clause or empty string
141
+ */
142
+ export function buildCampaignFilter(campaignIds) {
143
+ if (!campaignIds || campaignIds.length === 0) {
144
+ return '';
145
+ }
146
+ const ids = campaignIds.join(', ');
147
+ return `AND campaign.id IN (${ids})`;
148
+ }
149
+
150
+ /**
151
+ * Check query for mutation keywords and block if found
152
+ * @param {string} query - GAQL query string
153
+ * @throws {Error} If query contains mutation keywords
154
+ */
155
+ export function blockMutations(query) {
156
+ const lowerQuery = query.toLowerCase();
157
+
158
+ for (const keyword of MUTATION_KEYWORDS) {
159
+ if (lowerQuery.includes(keyword)) {
160
+ throw new Error(
161
+ `Mutation operations not allowed in query tool. Query contains: "${keyword}". ` +
162
+ `Use the mutate tool for write operations.`
163
+ );
164
+ }
165
+ }
166
+ }