mcp-gsheets 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,416 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { config } from 'dotenv';
4
+ import { google } from 'googleapis';
5
+ import { readFileSync } from 'fs';
6
+ import path from 'path';
7
+ import { fileURLToPath } from 'url';
8
+
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = path.dirname(__filename);
11
+
12
+ // Load environment variables
13
+ config({ path: path.join(__dirname, '..', '.env') });
14
+
15
+ const SPREADSHEET_ID = process.env.TEST_SPREADSHEET_ID;
16
+ const CREDENTIALS_PATH = process.env.GOOGLE_APPLICATION_CREDENTIALS;
17
+
18
+ if (!SPREADSHEET_ID || !CREDENTIALS_PATH) {
19
+ console.error('โŒ Missing required environment variables:');
20
+ if (!SPREADSHEET_ID) console.error(' - TEST_SPREADSHEET_ID');
21
+ if (!CREDENTIALS_PATH) console.error(' - GOOGLE_APPLICATION_CREDENTIALS');
22
+ process.exit(1);
23
+ }
24
+
25
+ // Initialize Google Sheets client
26
+ const auth = new google.auth.GoogleAuth({
27
+ keyFile: CREDENTIALS_PATH,
28
+ scopes: ['https://www.googleapis.com/auth/spreadsheets'],
29
+ });
30
+
31
+ const sheets = google.sheets({ version: 'v4', auth });
32
+
33
+ // Test data
34
+ const TEST_DATA = [
35
+ ['Product', 'Price', 'Quantity', 'Total'],
36
+ ['Apple', 1.99, 10, 19.90],
37
+ ['Banana', 0.99, 15, 14.85],
38
+ ['Orange', 2.49, 8, 19.92],
39
+ ['Grape', 3.99, 5, 19.95],
40
+ ['Total', '', '', '=SUM(D2:D5)'],
41
+ ];
42
+
43
+ async function clearSheet() {
44
+ console.log('๐Ÿงน Clearing sheet...');
45
+ try {
46
+ await sheets.spreadsheets.values.clear({
47
+ spreadsheetId: SPREADSHEET_ID,
48
+ range: 'A1:Z100',
49
+ });
50
+ console.log('โœ… Sheet cleared');
51
+ } catch (error) {
52
+ console.error('โŒ Error clearing sheet:', error.message);
53
+ }
54
+ }
55
+
56
+ async function populateTestData() {
57
+ console.log('๐Ÿ“ Populating test data...');
58
+ try {
59
+ await sheets.spreadsheets.values.update({
60
+ spreadsheetId: SPREADSHEET_ID,
61
+ range: 'A1:D6',
62
+ valueInputOption: 'USER_ENTERED',
63
+ requestBody: {
64
+ values: TEST_DATA,
65
+ },
66
+ });
67
+ console.log('โœ… Test data populated');
68
+ } catch (error) {
69
+ console.error('โŒ Error populating data:', error.message);
70
+ }
71
+ }
72
+
73
+ async function testFormatCells() {
74
+ console.log('\n๐ŸŽจ Testing cell formatting...');
75
+
76
+ try {
77
+ // Get sheet ID
78
+ const metadata = await sheets.spreadsheets.get({
79
+ spreadsheetId: SPREADSHEET_ID,
80
+ fields: 'sheets.properties',
81
+ });
82
+ const sheetId = metadata.data.sheets[0].properties.sheetId;
83
+
84
+ // Format header row
85
+ console.log(' - Formatting header row (bold, blue background)...');
86
+ await sheets.spreadsheets.batchUpdate({
87
+ spreadsheetId: SPREADSHEET_ID,
88
+ requestBody: {
89
+ requests: [{
90
+ repeatCell: {
91
+ range: {
92
+ sheetId: sheetId,
93
+ startRowIndex: 0,
94
+ endRowIndex: 1,
95
+ startColumnIndex: 0,
96
+ endColumnIndex: 4,
97
+ },
98
+ cell: {
99
+ userEnteredFormat: {
100
+ backgroundColor: { red: 0.2, green: 0.5, blue: 0.9 },
101
+ textFormat: {
102
+ bold: true,
103
+ foregroundColor: { red: 1, green: 1, blue: 1 },
104
+ fontSize: 12,
105
+ },
106
+ horizontalAlignment: 'CENTER',
107
+ },
108
+ },
109
+ fields: 'userEnteredFormat',
110
+ },
111
+ }],
112
+ },
113
+ });
114
+
115
+ // Format price column as currency
116
+ console.log(' - Formatting price column as currency...');
117
+ await sheets.spreadsheets.batchUpdate({
118
+ spreadsheetId: SPREADSHEET_ID,
119
+ requestBody: {
120
+ requests: [{
121
+ repeatCell: {
122
+ range: {
123
+ sheetId: sheetId,
124
+ startRowIndex: 1,
125
+ endRowIndex: 5,
126
+ startColumnIndex: 1,
127
+ endColumnIndex: 2,
128
+ },
129
+ cell: {
130
+ userEnteredFormat: {
131
+ numberFormat: {
132
+ type: 'CURRENCY',
133
+ pattern: '$#,##0.00',
134
+ },
135
+ },
136
+ },
137
+ fields: 'userEnteredFormat.numberFormat',
138
+ },
139
+ }],
140
+ },
141
+ });
142
+
143
+ // Format total row
144
+ console.log(' - Formatting total row (bold, yellow background)...');
145
+ await sheets.spreadsheets.batchUpdate({
146
+ spreadsheetId: SPREADSHEET_ID,
147
+ requestBody: {
148
+ requests: [{
149
+ repeatCell: {
150
+ range: {
151
+ sheetId: sheetId,
152
+ startRowIndex: 5,
153
+ endRowIndex: 6,
154
+ startColumnIndex: 0,
155
+ endColumnIndex: 4,
156
+ },
157
+ cell: {
158
+ userEnteredFormat: {
159
+ backgroundColor: { red: 1, green: 0.95, blue: 0.6 },
160
+ textFormat: {
161
+ bold: true,
162
+ },
163
+ },
164
+ },
165
+ fields: 'userEnteredFormat',
166
+ },
167
+ }],
168
+ },
169
+ });
170
+
171
+ console.log('โœ… Cell formatting completed');
172
+ } catch (error) {
173
+ console.error('โŒ Error formatting cells:', error.message);
174
+ }
175
+ }
176
+
177
+ async function testBorders() {
178
+ console.log('\n๐Ÿ”ฒ Testing borders...');
179
+
180
+ try {
181
+ const metadata = await sheets.spreadsheets.get({
182
+ spreadsheetId: SPREADSHEET_ID,
183
+ fields: 'sheets.properties',
184
+ });
185
+ const sheetId = metadata.data.sheets[0].properties.sheetId;
186
+
187
+ console.log(' - Adding borders to the entire table...');
188
+ await sheets.spreadsheets.batchUpdate({
189
+ spreadsheetId: SPREADSHEET_ID,
190
+ requestBody: {
191
+ requests: [{
192
+ updateBorders: {
193
+ range: {
194
+ sheetId: sheetId,
195
+ startRowIndex: 0,
196
+ endRowIndex: 6,
197
+ startColumnIndex: 0,
198
+ endColumnIndex: 4,
199
+ },
200
+ top: {
201
+ style: 'SOLID_MEDIUM',
202
+ color: { red: 0, green: 0, blue: 0 },
203
+ },
204
+ bottom: {
205
+ style: 'SOLID_MEDIUM',
206
+ color: { red: 0, green: 0, blue: 0 },
207
+ },
208
+ left: {
209
+ style: 'SOLID_MEDIUM',
210
+ color: { red: 0, green: 0, blue: 0 },
211
+ },
212
+ right: {
213
+ style: 'SOLID_MEDIUM',
214
+ color: { red: 0, green: 0, blue: 0 },
215
+ },
216
+ innerHorizontal: {
217
+ style: 'SOLID',
218
+ color: { red: 0.5, green: 0.5, blue: 0.5 },
219
+ },
220
+ innerVertical: {
221
+ style: 'SOLID',
222
+ color: { red: 0.5, green: 0.5, blue: 0.5 },
223
+ },
224
+ },
225
+ }],
226
+ },
227
+ });
228
+
229
+ console.log('โœ… Borders added successfully');
230
+ } catch (error) {
231
+ console.error('โŒ Error adding borders:', error.message);
232
+ }
233
+ }
234
+
235
+ async function testMergeCells() {
236
+ console.log('\n๐Ÿ”— Testing cell merging...');
237
+
238
+ try {
239
+ const metadata = await sheets.spreadsheets.get({
240
+ spreadsheetId: SPREADSHEET_ID,
241
+ fields: 'sheets.properties',
242
+ });
243
+ const sheetId = metadata.data.sheets[0].properties.sheetId;
244
+
245
+ // Add a title row
246
+ console.log(' - Adding title row...');
247
+ await sheets.spreadsheets.values.update({
248
+ spreadsheetId: SPREADSHEET_ID,
249
+ range: 'A8',
250
+ valueInputOption: 'USER_ENTERED',
251
+ requestBody: {
252
+ values: [['Product Sales Report']],
253
+ },
254
+ });
255
+
256
+ // Merge cells for title
257
+ console.log(' - Merging cells A8:D8 for title...');
258
+ await sheets.spreadsheets.batchUpdate({
259
+ spreadsheetId: SPREADSHEET_ID,
260
+ requestBody: {
261
+ requests: [{
262
+ mergeCells: {
263
+ range: {
264
+ sheetId: sheetId,
265
+ startRowIndex: 7,
266
+ endRowIndex: 8,
267
+ startColumnIndex: 0,
268
+ endColumnIndex: 4,
269
+ },
270
+ mergeType: 'MERGE_ALL',
271
+ },
272
+ }],
273
+ },
274
+ });
275
+
276
+ // Format merged title
277
+ console.log(' - Formatting merged title...');
278
+ await sheets.spreadsheets.batchUpdate({
279
+ spreadsheetId: SPREADSHEET_ID,
280
+ requestBody: {
281
+ requests: [{
282
+ repeatCell: {
283
+ range: {
284
+ sheetId: sheetId,
285
+ startRowIndex: 7,
286
+ endRowIndex: 8,
287
+ startColumnIndex: 0,
288
+ endColumnIndex: 4,
289
+ },
290
+ cell: {
291
+ userEnteredFormat: {
292
+ backgroundColor: { red: 0.1, green: 0.3, blue: 0.6 },
293
+ textFormat: {
294
+ bold: true,
295
+ foregroundColor: { red: 1, green: 1, blue: 1 },
296
+ fontSize: 16,
297
+ },
298
+ horizontalAlignment: 'CENTER',
299
+ verticalAlignment: 'MIDDLE',
300
+ },
301
+ },
302
+ fields: 'userEnteredFormat',
303
+ },
304
+ }],
305
+ },
306
+ });
307
+
308
+ console.log('โœ… Cell merging completed');
309
+ } catch (error) {
310
+ console.error('โŒ Error merging cells:', error.message);
311
+ }
312
+ }
313
+
314
+ async function testConditionalFormatting() {
315
+ console.log('\n๐ŸŽฏ Testing conditional formatting...');
316
+
317
+ try {
318
+ const metadata = await sheets.spreadsheets.get({
319
+ spreadsheetId: SPREADSHEET_ID,
320
+ fields: 'sheets.properties',
321
+ });
322
+ const sheetId = metadata.data.sheets[0].properties.sheetId;
323
+
324
+ // Test with text contains condition first
325
+ console.log(' - Adding conditional formatting for product names containing "a"...');
326
+ await sheets.spreadsheets.batchUpdate({
327
+ spreadsheetId: SPREADSHEET_ID,
328
+ requestBody: {
329
+ requests: [{
330
+ addConditionalFormatRule: {
331
+ rule: {
332
+ ranges: [{
333
+ sheetId: sheetId,
334
+ startRowIndex: 1,
335
+ endRowIndex: 5,
336
+ startColumnIndex: 0,
337
+ endColumnIndex: 1,
338
+ }],
339
+ booleanRule: {
340
+ condition: {
341
+ type: 'TEXT_CONTAINS',
342
+ values: [{
343
+ userEnteredValue: 'a',
344
+ }],
345
+ },
346
+ format: {
347
+ backgroundColor: { red: 0.9, green: 0.9, blue: 1 },
348
+ textFormat: {
349
+ italic: true,
350
+ },
351
+ },
352
+ },
353
+ },
354
+ },
355
+ }],
356
+ },
357
+ });
358
+
359
+ console.log(' - Adding gradient formatting for quantity column...');
360
+ await sheets.spreadsheets.batchUpdate({
361
+ spreadsheetId: SPREADSHEET_ID,
362
+ requestBody: {
363
+ requests: [{
364
+ addConditionalFormatRule: {
365
+ rule: {
366
+ ranges: [{
367
+ sheetId: sheetId,
368
+ startRowIndex: 1,
369
+ endRowIndex: 5,
370
+ startColumnIndex: 2,
371
+ endColumnIndex: 3,
372
+ }],
373
+ gradientRule: {
374
+ minpoint: {
375
+ color: { red: 1, green: 0.9, blue: 0.9 },
376
+ type: 'MIN',
377
+ },
378
+ maxpoint: {
379
+ color: { red: 0.2, green: 0.8, blue: 0.2 },
380
+ type: 'MAX',
381
+ },
382
+ },
383
+ },
384
+ },
385
+ }],
386
+ },
387
+ });
388
+
389
+ console.log('โœ… Conditional formatting completed');
390
+ } catch (error) {
391
+ console.error('โŒ Error adding conditional formatting:', error.message);
392
+ }
393
+ }
394
+
395
+ async function testAllFormattingTools() {
396
+ console.log('๐Ÿš€ Starting formatting tools test...');
397
+ console.log(`๐Ÿ“Š Using spreadsheet: ${SPREADSHEET_ID}`);
398
+ console.log(`๐Ÿ”‘ Using credentials: ${CREDENTIALS_PATH}`);
399
+
400
+ try {
401
+ await clearSheet();
402
+ await populateTestData();
403
+ await testFormatCells();
404
+ await testBorders();
405
+ await testMergeCells();
406
+ await testConditionalFormatting();
407
+
408
+ console.log('\nโœจ All formatting tests completed successfully!');
409
+ console.log(`๐Ÿ”— View your formatted spreadsheet: https://docs.google.com/spreadsheets/d/${SPREADSHEET_ID}`);
410
+ } catch (error) {
411
+ console.error('\nโŒ Test failed:', error);
412
+ process.exit(1);
413
+ }
414
+ }
415
+
416
+ testAllFormattingTools();