google-tools-mcp 1.2.11 → 2.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 -21
- package/README.md +555 -276
- package/dist/cachedToolsList.js +52 -52
- package/dist/googleDocsApiHelpers.js +76 -12
- package/dist/helpers.js +572 -252
- package/dist/httpAuth.js +296 -0
- package/dist/index.js +162 -11
- package/dist/logger.js +87 -87
- package/dist/markdown-transformer/docsToMarkdown.js +222 -21
- package/dist/markdown-transformer/index.js +19 -9
- package/dist/markdown-transformer/markdownToDocs.js +458 -28
- package/dist/readTracker.js +136 -17
- package/dist/sessionContext.js +37 -0
- package/dist/setup.js +187 -9
- package/dist/tools/calendar/getBusy.js +64 -64
- package/dist/tools/calendar/getEvents.js +141 -141
- package/dist/tools/calendar/getFree.js +225 -225
- package/dist/tools/calendar/index.js +19 -19
- package/dist/tools/calendar/listCalendars.js +38 -38
- package/dist/tools/calendar/listRecurringInstances.js +83 -83
- package/dist/tools/calendar/manageCalendar.js +121 -121
- package/dist/tools/calendar/manageEvent.js +258 -258
- package/dist/tools/calendar/moveEvent.js +66 -66
- package/dist/tools/docs/addTab.js +10 -12
- package/dist/tools/docs/appendToGoogleDoc.js +9 -4
- package/dist/tools/docs/deleteRange.js +9 -4
- package/dist/tools/docs/findAndReplace.js +9 -4
- package/dist/tools/docs/formatting/applyParagraphStyle.js +4 -1
- package/dist/tools/docs/formatting/getFormatting.js +171 -171
- package/dist/tools/docs/insertImage.js +15 -2
- package/dist/tools/docs/insertPageBreak.js +4 -1
- package/dist/tools/docs/insertTable.js +4 -1
- package/dist/tools/docs/insertTableWithData.js +4 -1
- package/dist/tools/docs/modifyText.js +14 -4
- package/dist/tools/docs/modifyText.test.js +84 -84
- package/dist/tools/docs/readGoogleDoc.js +71 -11
- package/dist/tools/docs/renameTab.js +12 -14
- package/dist/tools/drafts.js +165 -165
- package/dist/tools/drive/createDocument.js +13 -1
- package/dist/tools/drive/downloadFile.js +268 -268
- package/dist/tools/drive/getFileInfo.js +48 -48
- package/dist/tools/drive/getFilePath.js +50 -50
- package/dist/tools/drive/listDriveFiles.js +112 -112
- package/dist/tools/drive/listSharedDrives.js +52 -52
- package/dist/tools/drive/listSharedWithMe.js +97 -97
- package/dist/tools/drive/uploadFile.js +111 -111
- package/dist/tools/extras/index.js +9 -9
- package/dist/tools/extras/readDriveFile.js +212 -206
- package/dist/tools/extras/readFile.js +84 -84
- package/dist/tools/extras/searchFileContents.js +81 -81
- package/dist/tools/forms/batchUpdateForm.js +81 -81
- package/dist/tools/forms/createForm.js +54 -54
- package/dist/tools/forms/getForm.js +118 -118
- package/dist/tools/forms/getFormResponse.js +45 -45
- package/dist/tools/forms/index.js +15 -15
- package/dist/tools/forms/listFormResponses.js +56 -56
- package/dist/tools/forms/setPublishSettings.js +59 -59
- package/dist/tools/gmail/drafts.js +165 -165
- package/dist/tools/gmail/labels.js +54 -83
- package/dist/tools/gmail/messages.js +444 -441
- package/dist/tools/gmail/settings.js +358 -528
- package/dist/tools/gmail/threads.js +286 -164
- package/dist/tools/index.js +511 -473
- package/dist/tools/labels.js +103 -103
- package/dist/tools/legacyAliases.js +426 -0
- package/dist/tools/maps/directions.js +72 -0
- package/dist/tools/maps/geocode.js +16 -0
- package/dist/tools/maps/index.js +15 -0
- package/dist/tools/maps/mapsClient.js +83 -0
- package/dist/tools/maps/placeDetails.js +16 -0
- package/dist/tools/maps/reverseGeocode.js +19 -0
- package/dist/tools/maps/searchNearby.js +54 -0
- package/dist/tools/maps/searchPlaces.js +24 -0
- package/dist/tools/messages.js +448 -448
- package/dist/tools/settings.js +528 -528
- package/dist/tools/slides/createPresentation.js +104 -104
- package/dist/tools/slides/createShape.js +92 -92
- package/dist/tools/slides/createTextBox.js +82 -82
- package/dist/tools/slides/deleteSlide.js +30 -30
- package/dist/tools/slides/duplicateSlide.js +37 -37
- package/dist/tools/slides/exportThumbnail.js +42 -42
- package/dist/tools/slides/formatParagraph.js +72 -72
- package/dist/tools/slides/formatText.js +84 -84
- package/dist/tools/slides/getPresentation.js +87 -87
- package/dist/tools/slides/index.js +33 -33
- package/dist/tools/slides/reorderSlides.js +41 -41
- package/dist/tools/slides/replaceAllText.js +46 -46
- package/dist/tools/slides/setBackground.js +58 -58
- package/dist/tools/slides/speakerNotes.js +102 -102
- package/dist/tools/slides/styleShape.js +111 -111
- package/dist/tools/slides/updatePresentation.js +128 -128
- package/dist/tools/threads.js +145 -145
- package/dist/tools/utils/appendMarkdownToGoogleDoc.js +31 -7
- package/dist/tools/utils/replaceDocumentWithMarkdown.js +72 -9
- package/dist/updateCheck.js +202 -0
- package/dist/workspace.js +122 -0
- package/package.json +82 -80
|
@@ -1,118 +1,118 @@
|
|
|
1
|
-
import { UserError } from 'fastmcp';
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
import { getFormsClient } from '../../clients.js';
|
|
4
|
-
|
|
5
|
-
function extractOptionValues(options) {
|
|
6
|
-
if (!options) return [];
|
|
7
|
-
return options.filter((o) => o.value).map((o) => {
|
|
8
|
-
const opt = { value: o.value };
|
|
9
|
-
if (o.isOther) opt.isOther = true;
|
|
10
|
-
if (o.image) opt.image = o.image;
|
|
11
|
-
if (o.goToAction) opt.goToAction = o.goToAction;
|
|
12
|
-
if (o.goToSectionId) opt.goToSectionId = o.goToSectionId;
|
|
13
|
-
return opt;
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function getQuestionType(question) {
|
|
18
|
-
if (question.choiceQuestion) return question.choiceQuestion.type || 'RADIO';
|
|
19
|
-
if (question.textQuestion) {
|
|
20
|
-
return question.textQuestion.paragraph ? 'PARAGRAPH' : 'TEXT';
|
|
21
|
-
}
|
|
22
|
-
if (question.scaleQuestion) return 'SCALE';
|
|
23
|
-
if (question.dateQuestion) return 'DATE';
|
|
24
|
-
if (question.timeQuestion) return 'TIME';
|
|
25
|
-
if (question.fileUploadQuestion) return 'FILE_UPLOAD';
|
|
26
|
-
if (question.ratingQuestion) return 'RATING';
|
|
27
|
-
if (question.rowQuestion) return 'GRID_ROW';
|
|
28
|
-
return 'QUESTION';
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function serializeFormItem(item, index) {
|
|
32
|
-
const result = {
|
|
33
|
-
index,
|
|
34
|
-
itemId: item.itemId,
|
|
35
|
-
title: item.title || '',
|
|
36
|
-
description: item.description || '',
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
if (item.questionItem) {
|
|
40
|
-
const q = item.questionItem.question;
|
|
41
|
-
result.type = getQuestionType(q);
|
|
42
|
-
result.required = q.required || false;
|
|
43
|
-
result.questionId = q.questionId || null;
|
|
44
|
-
if (q.choiceQuestion) {
|
|
45
|
-
result.options = extractOptionValues(q.choiceQuestion.options);
|
|
46
|
-
}
|
|
47
|
-
} else if (item.questionGroupItem) {
|
|
48
|
-
const group = item.questionGroupItem;
|
|
49
|
-
result.type = 'GRID';
|
|
50
|
-
result.grid = {
|
|
51
|
-
columns: group.grid?.columns?.options
|
|
52
|
-
? extractOptionValues(group.grid.columns.options)
|
|
53
|
-
: [],
|
|
54
|
-
};
|
|
55
|
-
if (group.questions) {
|
|
56
|
-
result.grid.rows = group.questions.map((q) => ({
|
|
57
|
-
questionId: q.questionId,
|
|
58
|
-
title: q.rowQuestion?.title || '',
|
|
59
|
-
required: q.required || false,
|
|
60
|
-
}));
|
|
61
|
-
}
|
|
62
|
-
} else if (item.pageBreakItem !== undefined) {
|
|
63
|
-
result.type = 'PAGE_BREAK';
|
|
64
|
-
} else if (item.textItem !== undefined) {
|
|
65
|
-
result.type = 'TEXT_ITEM';
|
|
66
|
-
} else if (item.imageItem) {
|
|
67
|
-
result.type = 'IMAGE';
|
|
68
|
-
} else if (item.videoItem) {
|
|
69
|
-
result.type = 'VIDEO';
|
|
70
|
-
} else {
|
|
71
|
-
result.type = 'UNKNOWN';
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return result;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export function register(server) {
|
|
78
|
-
server.addTool({
|
|
79
|
-
name: 'get_form',
|
|
80
|
-
description:
|
|
81
|
-
'Retrieves a Google Form by ID. Returns the form title, description, URLs, and all items (questions, sections, images, etc.) with their types, options, and metadata.',
|
|
82
|
-
parameters: z.object({
|
|
83
|
-
formId: z.string().describe('The ID of the Google Form to retrieve'),
|
|
84
|
-
}),
|
|
85
|
-
execute: async (args, { log }) => {
|
|
86
|
-
const forms = await getFormsClient();
|
|
87
|
-
log.info(`Getting form: ${args.formId}`);
|
|
88
|
-
|
|
89
|
-
try {
|
|
90
|
-
const response = await forms.forms.get({ formId: args.formId });
|
|
91
|
-
const form = response.data;
|
|
92
|
-
const items = (form.items || []).map((item, i) => serializeFormItem(item, i));
|
|
93
|
-
|
|
94
|
-
return JSON.stringify(
|
|
95
|
-
{
|
|
96
|
-
formId: form.formId,
|
|
97
|
-
title: form.info?.title || '',
|
|
98
|
-
description: form.info?.description || '',
|
|
99
|
-
documentTitle: form.info?.documentTitle || '',
|
|
100
|
-
editUrl: `https://docs.google.com/forms/d/${form.formId}/edit`,
|
|
101
|
-
responderUrl:
|
|
102
|
-
form.responderUri || `https://docs.google.com/forms/d/${form.formId}/viewform`,
|
|
103
|
-
itemCount: items.length,
|
|
104
|
-
items,
|
|
105
|
-
},
|
|
106
|
-
null,
|
|
107
|
-
2,
|
|
108
|
-
);
|
|
109
|
-
} catch (error) {
|
|
110
|
-
log.error(`Error getting form: ${error.message || error}`);
|
|
111
|
-
if (error.code === 401)
|
|
112
|
-
throw new UserError('Authentication failed. Try logging out and re-authenticating.');
|
|
113
|
-
if (error.code === 404) throw new UserError(`Form not found: ${args.formId}`);
|
|
114
|
-
throw new UserError(`Failed to get form: ${error.message || 'Unknown error'}`);
|
|
115
|
-
}
|
|
116
|
-
},
|
|
117
|
-
});
|
|
118
|
-
}
|
|
1
|
+
import { UserError } from 'fastmcp';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { getFormsClient } from '../../clients.js';
|
|
4
|
+
|
|
5
|
+
function extractOptionValues(options) {
|
|
6
|
+
if (!options) return [];
|
|
7
|
+
return options.filter((o) => o.value).map((o) => {
|
|
8
|
+
const opt = { value: o.value };
|
|
9
|
+
if (o.isOther) opt.isOther = true;
|
|
10
|
+
if (o.image) opt.image = o.image;
|
|
11
|
+
if (o.goToAction) opt.goToAction = o.goToAction;
|
|
12
|
+
if (o.goToSectionId) opt.goToSectionId = o.goToSectionId;
|
|
13
|
+
return opt;
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function getQuestionType(question) {
|
|
18
|
+
if (question.choiceQuestion) return question.choiceQuestion.type || 'RADIO';
|
|
19
|
+
if (question.textQuestion) {
|
|
20
|
+
return question.textQuestion.paragraph ? 'PARAGRAPH' : 'TEXT';
|
|
21
|
+
}
|
|
22
|
+
if (question.scaleQuestion) return 'SCALE';
|
|
23
|
+
if (question.dateQuestion) return 'DATE';
|
|
24
|
+
if (question.timeQuestion) return 'TIME';
|
|
25
|
+
if (question.fileUploadQuestion) return 'FILE_UPLOAD';
|
|
26
|
+
if (question.ratingQuestion) return 'RATING';
|
|
27
|
+
if (question.rowQuestion) return 'GRID_ROW';
|
|
28
|
+
return 'QUESTION';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function serializeFormItem(item, index) {
|
|
32
|
+
const result = {
|
|
33
|
+
index,
|
|
34
|
+
itemId: item.itemId,
|
|
35
|
+
title: item.title || '',
|
|
36
|
+
description: item.description || '',
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
if (item.questionItem) {
|
|
40
|
+
const q = item.questionItem.question;
|
|
41
|
+
result.type = getQuestionType(q);
|
|
42
|
+
result.required = q.required || false;
|
|
43
|
+
result.questionId = q.questionId || null;
|
|
44
|
+
if (q.choiceQuestion) {
|
|
45
|
+
result.options = extractOptionValues(q.choiceQuestion.options);
|
|
46
|
+
}
|
|
47
|
+
} else if (item.questionGroupItem) {
|
|
48
|
+
const group = item.questionGroupItem;
|
|
49
|
+
result.type = 'GRID';
|
|
50
|
+
result.grid = {
|
|
51
|
+
columns: group.grid?.columns?.options
|
|
52
|
+
? extractOptionValues(group.grid.columns.options)
|
|
53
|
+
: [],
|
|
54
|
+
};
|
|
55
|
+
if (group.questions) {
|
|
56
|
+
result.grid.rows = group.questions.map((q) => ({
|
|
57
|
+
questionId: q.questionId,
|
|
58
|
+
title: q.rowQuestion?.title || '',
|
|
59
|
+
required: q.required || false,
|
|
60
|
+
}));
|
|
61
|
+
}
|
|
62
|
+
} else if (item.pageBreakItem !== undefined) {
|
|
63
|
+
result.type = 'PAGE_BREAK';
|
|
64
|
+
} else if (item.textItem !== undefined) {
|
|
65
|
+
result.type = 'TEXT_ITEM';
|
|
66
|
+
} else if (item.imageItem) {
|
|
67
|
+
result.type = 'IMAGE';
|
|
68
|
+
} else if (item.videoItem) {
|
|
69
|
+
result.type = 'VIDEO';
|
|
70
|
+
} else {
|
|
71
|
+
result.type = 'UNKNOWN';
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function register(server) {
|
|
78
|
+
server.addTool({
|
|
79
|
+
name: 'get_form',
|
|
80
|
+
description:
|
|
81
|
+
'Retrieves a Google Form by ID. Returns the form title, description, URLs, and all items (questions, sections, images, etc.) with their types, options, and metadata.',
|
|
82
|
+
parameters: z.object({
|
|
83
|
+
formId: z.string().describe('The ID of the Google Form to retrieve'),
|
|
84
|
+
}),
|
|
85
|
+
execute: async (args, { log }) => {
|
|
86
|
+
const forms = await getFormsClient();
|
|
87
|
+
log.info(`Getting form: ${args.formId}`);
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
const response = await forms.forms.get({ formId: args.formId });
|
|
91
|
+
const form = response.data;
|
|
92
|
+
const items = (form.items || []).map((item, i) => serializeFormItem(item, i));
|
|
93
|
+
|
|
94
|
+
return JSON.stringify(
|
|
95
|
+
{
|
|
96
|
+
formId: form.formId,
|
|
97
|
+
title: form.info?.title || '',
|
|
98
|
+
description: form.info?.description || '',
|
|
99
|
+
documentTitle: form.info?.documentTitle || '',
|
|
100
|
+
editUrl: `https://docs.google.com/forms/d/${form.formId}/edit`,
|
|
101
|
+
responderUrl:
|
|
102
|
+
form.responderUri || `https://docs.google.com/forms/d/${form.formId}/viewform`,
|
|
103
|
+
itemCount: items.length,
|
|
104
|
+
items,
|
|
105
|
+
},
|
|
106
|
+
null,
|
|
107
|
+
2,
|
|
108
|
+
);
|
|
109
|
+
} catch (error) {
|
|
110
|
+
log.error(`Error getting form: ${error.message || error}`);
|
|
111
|
+
if (error.code === 401)
|
|
112
|
+
throw new UserError('Authentication failed. Try logging out and re-authenticating.');
|
|
113
|
+
if (error.code === 404) throw new UserError(`Form not found: ${args.formId}`);
|
|
114
|
+
throw new UserError(`Failed to get form: ${error.message || 'Unknown error'}`);
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
}
|
|
@@ -1,45 +1,45 @@
|
|
|
1
|
-
import { UserError } from 'fastmcp';
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
import { getFormsClient } from '../../clients.js';
|
|
4
|
-
|
|
5
|
-
export function register(server) {
|
|
6
|
-
server.addTool({
|
|
7
|
-
name: 'get_form_response',
|
|
8
|
-
description:
|
|
9
|
-
'Retrieves a single response from a Google Form by response ID. Returns the response timestamps and all answers keyed by question ID.',
|
|
10
|
-
parameters: z.object({
|
|
11
|
-
formId: z.string().describe('The ID of the Google Form'),
|
|
12
|
-
responseId: z.string().describe('The ID of the specific response to retrieve'),
|
|
13
|
-
}),
|
|
14
|
-
execute: async (args, { log }) => {
|
|
15
|
-
const forms = await getFormsClient();
|
|
16
|
-
log.info(`Getting response ${args.responseId} for form: ${args.formId}`);
|
|
17
|
-
|
|
18
|
-
try {
|
|
19
|
-
const response = await forms.forms.responses.get({
|
|
20
|
-
formId: args.formId,
|
|
21
|
-
responseId: args.responseId,
|
|
22
|
-
});
|
|
23
|
-
const r = response.data;
|
|
24
|
-
|
|
25
|
-
return JSON.stringify(
|
|
26
|
-
{
|
|
27
|
-
responseId: r.responseId,
|
|
28
|
-
createTime: r.createTime,
|
|
29
|
-
lastSubmittedTime: r.lastSubmittedTime,
|
|
30
|
-
answers: r.answers || {},
|
|
31
|
-
},
|
|
32
|
-
null,
|
|
33
|
-
2,
|
|
34
|
-
);
|
|
35
|
-
} catch (error) {
|
|
36
|
-
log.error(`Error getting form response: ${error.message || error}`);
|
|
37
|
-
if (error.code === 401)
|
|
38
|
-
throw new UserError('Authentication failed. Try logging out and re-authenticating.');
|
|
39
|
-
if (error.code === 404)
|
|
40
|
-
throw new UserError(`Form or response not found: ${args.formId} / ${args.responseId}`);
|
|
41
|
-
throw new UserError(`Failed to get form response: ${error.message || 'Unknown error'}`);
|
|
42
|
-
}
|
|
43
|
-
},
|
|
44
|
-
});
|
|
45
|
-
}
|
|
1
|
+
import { UserError } from 'fastmcp';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { getFormsClient } from '../../clients.js';
|
|
4
|
+
|
|
5
|
+
export function register(server) {
|
|
6
|
+
server.addTool({
|
|
7
|
+
name: 'get_form_response',
|
|
8
|
+
description:
|
|
9
|
+
'Retrieves a single response from a Google Form by response ID. Returns the response timestamps and all answers keyed by question ID.',
|
|
10
|
+
parameters: z.object({
|
|
11
|
+
formId: z.string().describe('The ID of the Google Form'),
|
|
12
|
+
responseId: z.string().describe('The ID of the specific response to retrieve'),
|
|
13
|
+
}),
|
|
14
|
+
execute: async (args, { log }) => {
|
|
15
|
+
const forms = await getFormsClient();
|
|
16
|
+
log.info(`Getting response ${args.responseId} for form: ${args.formId}`);
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
const response = await forms.forms.responses.get({
|
|
20
|
+
formId: args.formId,
|
|
21
|
+
responseId: args.responseId,
|
|
22
|
+
});
|
|
23
|
+
const r = response.data;
|
|
24
|
+
|
|
25
|
+
return JSON.stringify(
|
|
26
|
+
{
|
|
27
|
+
responseId: r.responseId,
|
|
28
|
+
createTime: r.createTime,
|
|
29
|
+
lastSubmittedTime: r.lastSubmittedTime,
|
|
30
|
+
answers: r.answers || {},
|
|
31
|
+
},
|
|
32
|
+
null,
|
|
33
|
+
2,
|
|
34
|
+
);
|
|
35
|
+
} catch (error) {
|
|
36
|
+
log.error(`Error getting form response: ${error.message || error}`);
|
|
37
|
+
if (error.code === 401)
|
|
38
|
+
throw new UserError('Authentication failed. Try logging out and re-authenticating.');
|
|
39
|
+
if (error.code === 404)
|
|
40
|
+
throw new UserError(`Form or response not found: ${args.formId} / ${args.responseId}`);
|
|
41
|
+
throw new UserError(`Failed to get form response: ${error.message || 'Unknown error'}`);
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { register as createForm } from './createForm.js';
|
|
2
|
-
import { register as getForm } from './getForm.js';
|
|
3
|
-
import { register as listFormResponses } from './listFormResponses.js';
|
|
4
|
-
import { register as getFormResponse } from './getFormResponse.js';
|
|
5
|
-
import { register as batchUpdateForm } from './batchUpdateForm.js';
|
|
6
|
-
import { register as setPublishSettings } from './setPublishSettings.js';
|
|
7
|
-
|
|
8
|
-
export function registerFormsTools(server) {
|
|
9
|
-
createForm(server);
|
|
10
|
-
getForm(server);
|
|
11
|
-
listFormResponses(server);
|
|
12
|
-
getFormResponse(server);
|
|
13
|
-
batchUpdateForm(server);
|
|
14
|
-
setPublishSettings(server);
|
|
15
|
-
}
|
|
1
|
+
import { register as createForm } from './createForm.js';
|
|
2
|
+
import { register as getForm } from './getForm.js';
|
|
3
|
+
import { register as listFormResponses } from './listFormResponses.js';
|
|
4
|
+
import { register as getFormResponse } from './getFormResponse.js';
|
|
5
|
+
import { register as batchUpdateForm } from './batchUpdateForm.js';
|
|
6
|
+
import { register as setPublishSettings } from './setPublishSettings.js';
|
|
7
|
+
|
|
8
|
+
export function registerFormsTools(server) {
|
|
9
|
+
createForm(server);
|
|
10
|
+
getForm(server);
|
|
11
|
+
listFormResponses(server);
|
|
12
|
+
getFormResponse(server);
|
|
13
|
+
batchUpdateForm(server);
|
|
14
|
+
setPublishSettings(server);
|
|
15
|
+
}
|
|
@@ -1,56 +1,56 @@
|
|
|
1
|
-
import { UserError } from 'fastmcp';
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
import { getFormsClient } from '../../clients.js';
|
|
4
|
-
|
|
5
|
-
export function register(server) {
|
|
6
|
-
server.addTool({
|
|
7
|
-
name: 'list_form_responses',
|
|
8
|
-
description:
|
|
9
|
-
'Lists responses submitted to a Google Form. Returns a paginated list of responses with timestamps and answers.',
|
|
10
|
-
parameters: z.object({
|
|
11
|
-
formId: z.string().describe('The ID of the Google Form'),
|
|
12
|
-
pageSize: z
|
|
13
|
-
.number()
|
|
14
|
-
.min(1)
|
|
15
|
-
.max(5000)
|
|
16
|
-
.optional()
|
|
17
|
-
.describe('Number of responses to return per page (default 10, max 5000)'),
|
|
18
|
-
pageToken: z.string().optional().describe('Token for fetching the next page of results'),
|
|
19
|
-
}),
|
|
20
|
-
execute: async (args, { log }) => {
|
|
21
|
-
const forms = await getFormsClient();
|
|
22
|
-
log.info(`Listing responses for form: ${args.formId}`);
|
|
23
|
-
|
|
24
|
-
const params = { formId: args.formId };
|
|
25
|
-
if (args.pageSize) params.pageSize = args.pageSize;
|
|
26
|
-
if (args.pageToken) params.pageToken = args.pageToken;
|
|
27
|
-
|
|
28
|
-
try {
|
|
29
|
-
const response = await forms.forms.responses.list(params);
|
|
30
|
-
const data = response.data;
|
|
31
|
-
const responses = (data.responses || []).map((r) => ({
|
|
32
|
-
responseId: r.responseId,
|
|
33
|
-
createTime: r.createTime,
|
|
34
|
-
lastSubmittedTime: r.lastSubmittedTime,
|
|
35
|
-
answers: r.answers || {},
|
|
36
|
-
}));
|
|
37
|
-
|
|
38
|
-
return JSON.stringify(
|
|
39
|
-
{
|
|
40
|
-
totalResponses: responses.length,
|
|
41
|
-
nextPageToken: data.nextPageToken || null,
|
|
42
|
-
responses,
|
|
43
|
-
},
|
|
44
|
-
null,
|
|
45
|
-
2,
|
|
46
|
-
);
|
|
47
|
-
} catch (error) {
|
|
48
|
-
log.error(`Error listing form responses: ${error.message || error}`);
|
|
49
|
-
if (error.code === 401)
|
|
50
|
-
throw new UserError('Authentication failed. Try logging out and re-authenticating.');
|
|
51
|
-
if (error.code === 404) throw new UserError(`Form not found: ${args.formId}`);
|
|
52
|
-
throw new UserError(`Failed to list form responses: ${error.message || 'Unknown error'}`);
|
|
53
|
-
}
|
|
54
|
-
},
|
|
55
|
-
});
|
|
56
|
-
}
|
|
1
|
+
import { UserError } from 'fastmcp';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { getFormsClient } from '../../clients.js';
|
|
4
|
+
|
|
5
|
+
export function register(server) {
|
|
6
|
+
server.addTool({
|
|
7
|
+
name: 'list_form_responses',
|
|
8
|
+
description:
|
|
9
|
+
'Lists responses submitted to a Google Form. Returns a paginated list of responses with timestamps and answers.',
|
|
10
|
+
parameters: z.object({
|
|
11
|
+
formId: z.string().describe('The ID of the Google Form'),
|
|
12
|
+
pageSize: z
|
|
13
|
+
.number()
|
|
14
|
+
.min(1)
|
|
15
|
+
.max(5000)
|
|
16
|
+
.optional()
|
|
17
|
+
.describe('Number of responses to return per page (default 10, max 5000)'),
|
|
18
|
+
pageToken: z.string().optional().describe('Token for fetching the next page of results'),
|
|
19
|
+
}),
|
|
20
|
+
execute: async (args, { log }) => {
|
|
21
|
+
const forms = await getFormsClient();
|
|
22
|
+
log.info(`Listing responses for form: ${args.formId}`);
|
|
23
|
+
|
|
24
|
+
const params = { formId: args.formId };
|
|
25
|
+
if (args.pageSize) params.pageSize = args.pageSize;
|
|
26
|
+
if (args.pageToken) params.pageToken = args.pageToken;
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const response = await forms.forms.responses.list(params);
|
|
30
|
+
const data = response.data;
|
|
31
|
+
const responses = (data.responses || []).map((r) => ({
|
|
32
|
+
responseId: r.responseId,
|
|
33
|
+
createTime: r.createTime,
|
|
34
|
+
lastSubmittedTime: r.lastSubmittedTime,
|
|
35
|
+
answers: r.answers || {},
|
|
36
|
+
}));
|
|
37
|
+
|
|
38
|
+
return JSON.stringify(
|
|
39
|
+
{
|
|
40
|
+
totalResponses: responses.length,
|
|
41
|
+
nextPageToken: data.nextPageToken || null,
|
|
42
|
+
responses,
|
|
43
|
+
},
|
|
44
|
+
null,
|
|
45
|
+
2,
|
|
46
|
+
);
|
|
47
|
+
} catch (error) {
|
|
48
|
+
log.error(`Error listing form responses: ${error.message || error}`);
|
|
49
|
+
if (error.code === 401)
|
|
50
|
+
throw new UserError('Authentication failed. Try logging out and re-authenticating.');
|
|
51
|
+
if (error.code === 404) throw new UserError(`Form not found: ${args.formId}`);
|
|
52
|
+
throw new UserError(`Failed to list form responses: ${error.message || 'Unknown error'}`);
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
}
|
|
@@ -1,59 +1,59 @@
|
|
|
1
|
-
import { UserError } from 'fastmcp';
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
import { getFormsClient } from '../../clients.js';
|
|
4
|
-
|
|
5
|
-
export function register(server) {
|
|
6
|
-
server.addTool({
|
|
7
|
-
name: 'set_publish_settings',
|
|
8
|
-
description:
|
|
9
|
-
'Updates the publish settings of a Google Form. Controls whether the form is published as a template and whether respondents must be authenticated.',
|
|
10
|
-
parameters: z.object({
|
|
11
|
-
formId: z.string().describe('The ID of the Google Form'),
|
|
12
|
-
publishAsTemplate: z
|
|
13
|
-
.boolean()
|
|
14
|
-
.optional()
|
|
15
|
-
.describe('Whether to publish the form as a reusable template'),
|
|
16
|
-
requireAuthentication: z
|
|
17
|
-
.boolean()
|
|
18
|
-
.optional()
|
|
19
|
-
.describe('Whether respondents must sign in with a Google account to submit the form'),
|
|
20
|
-
}),
|
|
21
|
-
execute: async (args, { log }) => {
|
|
22
|
-
const forms = await getFormsClient();
|
|
23
|
-
log.info(`Setting publish settings for form: ${args.formId}`);
|
|
24
|
-
|
|
25
|
-
const body = {};
|
|
26
|
-
if (args.publishAsTemplate !== undefined) {
|
|
27
|
-
body.publishAsTemplate = args.publishAsTemplate;
|
|
28
|
-
}
|
|
29
|
-
if (args.requireAuthentication !== undefined) {
|
|
30
|
-
body.requireAuthentication = args.requireAuthentication;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (Object.keys(body).length === 0) {
|
|
34
|
-
throw new UserError('At least one setting (publishAsTemplate or requireAuthentication) must be provided.');
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
try {
|
|
38
|
-
await forms.forms.setPublishSettings({
|
|
39
|
-
formId: args.formId,
|
|
40
|
-
requestBody: body,
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
return JSON.stringify({
|
|
44
|
-
url: `https://docs.google.com/forms/d/${args.formId}/edit`,
|
|
45
|
-
success: true,
|
|
46
|
-
formId: args.formId,
|
|
47
|
-
message: 'Publish settings updated successfully.',
|
|
48
|
-
appliedSettings: body,
|
|
49
|
-
}, null, 2);
|
|
50
|
-
} catch (error) {
|
|
51
|
-
log.error(`Error setting publish settings: ${error.message || error}`);
|
|
52
|
-
if (error.code === 401)
|
|
53
|
-
throw new UserError('Authentication failed. Try logging out and re-authenticating.');
|
|
54
|
-
if (error.code === 404) throw new UserError(`Form not found: ${args.formId}`);
|
|
55
|
-
throw new UserError(`Failed to set publish settings: ${error.message || 'Unknown error'}`);
|
|
56
|
-
}
|
|
57
|
-
},
|
|
58
|
-
});
|
|
59
|
-
}
|
|
1
|
+
import { UserError } from 'fastmcp';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { getFormsClient } from '../../clients.js';
|
|
4
|
+
|
|
5
|
+
export function register(server) {
|
|
6
|
+
server.addTool({
|
|
7
|
+
name: 'set_publish_settings',
|
|
8
|
+
description:
|
|
9
|
+
'Updates the publish settings of a Google Form. Controls whether the form is published as a template and whether respondents must be authenticated.',
|
|
10
|
+
parameters: z.object({
|
|
11
|
+
formId: z.string().describe('The ID of the Google Form'),
|
|
12
|
+
publishAsTemplate: z
|
|
13
|
+
.boolean()
|
|
14
|
+
.optional()
|
|
15
|
+
.describe('Whether to publish the form as a reusable template'),
|
|
16
|
+
requireAuthentication: z
|
|
17
|
+
.boolean()
|
|
18
|
+
.optional()
|
|
19
|
+
.describe('Whether respondents must sign in with a Google account to submit the form'),
|
|
20
|
+
}),
|
|
21
|
+
execute: async (args, { log }) => {
|
|
22
|
+
const forms = await getFormsClient();
|
|
23
|
+
log.info(`Setting publish settings for form: ${args.formId}`);
|
|
24
|
+
|
|
25
|
+
const body = {};
|
|
26
|
+
if (args.publishAsTemplate !== undefined) {
|
|
27
|
+
body.publishAsTemplate = args.publishAsTemplate;
|
|
28
|
+
}
|
|
29
|
+
if (args.requireAuthentication !== undefined) {
|
|
30
|
+
body.requireAuthentication = args.requireAuthentication;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (Object.keys(body).length === 0) {
|
|
34
|
+
throw new UserError('At least one setting (publishAsTemplate or requireAuthentication) must be provided.');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
await forms.forms.setPublishSettings({
|
|
39
|
+
formId: args.formId,
|
|
40
|
+
requestBody: body,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
return JSON.stringify({
|
|
44
|
+
url: `https://docs.google.com/forms/d/${args.formId}/edit`,
|
|
45
|
+
success: true,
|
|
46
|
+
formId: args.formId,
|
|
47
|
+
message: 'Publish settings updated successfully.',
|
|
48
|
+
appliedSettings: body,
|
|
49
|
+
}, null, 2);
|
|
50
|
+
} catch (error) {
|
|
51
|
+
log.error(`Error setting publish settings: ${error.message || error}`);
|
|
52
|
+
if (error.code === 401)
|
|
53
|
+
throw new UserError('Authentication failed. Try logging out and re-authenticating.');
|
|
54
|
+
if (error.code === 404) throw new UserError(`Form not found: ${args.formId}`);
|
|
55
|
+
throw new UserError(`Failed to set publish settings: ${error.message || 'Unknown error'}`);
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
}
|