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.
- package/LICENSE +21 -0
- package/README.md +273 -0
- package/dist/index.js +7573 -0
- package/package.json +83 -0
- package/scripts/setup-mcp-config.js +260 -0
- package/scripts/test-sheets-tools.js +416 -0
|
@@ -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();
|