gogcli-mcp 2.30.0 → 3.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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/dist/index.js +15416 -18013
- package/dist/lib.js +13476 -8827
- package/manifest.json +1 -1
- package/package.json +3 -3
- package/server.json +2 -2
- package/src/connector-auth.ts +22 -6
- package/src/connector-login.ts +87 -0
- package/src/connector-runtime.ts +33 -10
- package/src/gmail-dispatch-guard.ts +106 -0
- package/src/gmail-results.ts +1 -1
- package/src/lib.ts +10 -0
- package/src/pagination.ts +1 -1
- package/src/tools/api.ts +7 -7
- package/src/tools/appscript.ts +15 -15
- package/src/tools/auth.ts +11 -11
- package/src/tools/calendar.ts +13 -13
- package/src/tools/chat.ts +25 -25
- package/src/tools/classroom.ts +49 -49
- package/src/tools/contacts.ts +9 -9
- package/src/tools/docs.ts +13 -13
- package/src/tools/drive.ts +22 -22
- package/src/tools/gmail.ts +136 -23
- package/src/tools/sheets.ts +15 -15
- package/src/tools/slides.ts +13 -13
- package/src/tools/tasks.ts +13 -13
- package/src/tools/utils.ts +2 -3
- package/src/worker.ts +52 -64
- package/tests/connector-login.test.ts +151 -0
- package/tests/connector-runtime.test.ts +21 -1
- package/tests/gmail-dispatch-guard.test.ts +132 -0
- package/tests/sdk-single-copy.test.ts +18 -36
- package/tests/tools/appscript.test.ts +1 -1
- package/tests/tools/chat.test.ts +1 -1
- package/tests/tools/gmail.test.ts +242 -12
- package/tests/tools/sheets.test.ts +1 -1
- package/tests/zod-single-copy.test.ts +2 -2
- package/tsconfig.json +1 -3
- package/vitest.config.ts +3 -5
package/src/tools/calendar.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { McpServer } from '@modelcontextprotocol/
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/server';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { viewParam, resolveView } from '@chrischall/mcp-utils';
|
|
4
4
|
import { accountParam, runOrDiagnose, registerRunTool, pageTokenParam, pageAliasParam, resolvePageToken } from './utils.js';
|
|
@@ -82,7 +82,7 @@ export function registerCalendarTools(server: McpServer): void {
|
|
|
82
82
|
+ 'gog returns only 10 events by default, so a wide date range is USUALLY INCOMPLETE: raise max, or page with pageToken until the response carries no nextPageToken. '
|
|
83
83
|
+ 'A response carrying "truncated": true is an incomplete view — never conclude an event does not exist from one.',
|
|
84
84
|
annotations: { readOnlyHint: true },
|
|
85
|
-
inputSchema: {
|
|
85
|
+
inputSchema: z.object({
|
|
86
86
|
calendarId: z.string().optional().describe('Calendar ID (default: primary calendar)'),
|
|
87
87
|
from: z.string().optional().describe('Start time filter (RFC3339, date, or natural language)'),
|
|
88
88
|
to: z.string().optional().describe('End time filter (RFC3339, date, or natural language). Mutually exclusive with today and with days.'),
|
|
@@ -105,7 +105,7 @@ export function registerCalendarTools(server: McpServer): void {
|
|
|
105
105
|
+ 'listing\'s bytes — plus etag/iCalUID/kind. Ask for full when you need a body or a guest list.',
|
|
106
106
|
}),
|
|
107
107
|
account: accountParam,
|
|
108
|
-
},
|
|
108
|
+
}),
|
|
109
109
|
}, async ({ calendarId, from, to, days, today, query, max, pageToken, page, all, eventTypes, timezone, view, account }) => {
|
|
110
110
|
const args = ['calendar', 'events'];
|
|
111
111
|
if (calendarId) args.push(calendarId);
|
|
@@ -134,12 +134,12 @@ export function registerCalendarTools(server: McpServer): void {
|
|
|
134
134
|
server.registerTool('gog_calendar_get', {
|
|
135
135
|
description: 'Get a specific calendar event by ID.',
|
|
136
136
|
annotations: { readOnlyHint: true },
|
|
137
|
-
inputSchema: {
|
|
137
|
+
inputSchema: z.object({
|
|
138
138
|
calendarId: z.string().describe('Calendar ID'),
|
|
139
139
|
eventId: z.string().describe('Event ID'),
|
|
140
140
|
timezone: z.string().optional().describe('Display timezone for event times (IANA name, e.g. America/New_York, or "local" for the system timezone). Default: the event\'s timezone, then its calendar\'s timezone.'),
|
|
141
141
|
account: accountParam,
|
|
142
|
-
},
|
|
142
|
+
}),
|
|
143
143
|
}, async ({ calendarId, eventId, timezone, account }) => {
|
|
144
144
|
const args = ['calendar', 'event', calendarId, eventId];
|
|
145
145
|
if (timezone) args.push(`--timezone=${timezone}`);
|
|
@@ -149,7 +149,7 @@ export function registerCalendarTools(server: McpServer): void {
|
|
|
149
149
|
server.registerTool('gog_calendar_create', {
|
|
150
150
|
description: 'Create a calendar event. Set withZoom=true to attach a Zoom meeting (requires Zoom S2S OAuth setup via gog_zoom_auth_setup; the join URL + meeting ID + passcode are appended to the event description — Google rejects native conference card writes from non-Workspace-Marketplace OAuth clients).',
|
|
151
151
|
annotations: { destructiveHint: false },
|
|
152
|
-
inputSchema: {
|
|
152
|
+
inputSchema: z.object({
|
|
153
153
|
calendarId: z.string().describe('Calendar ID (use "primary" for the default calendar)'),
|
|
154
154
|
summary: z.string().describe('Event title'),
|
|
155
155
|
from: z.string().describe('Start time (RFC3339 or date for all-day events)'),
|
|
@@ -162,7 +162,7 @@ export function registerCalendarTools(server: McpServer): void {
|
|
|
162
162
|
withZoom: z.boolean().optional().describe('Create a Zoom video conference for this event (requires Zoom S2S OAuth setup)'),
|
|
163
163
|
...reminderParams,
|
|
164
164
|
account: accountParam,
|
|
165
|
-
},
|
|
165
|
+
}),
|
|
166
166
|
}, async ({ calendarId, summary, from, to, description, location, attendees, allDay, timezone, withZoom, reminders, noReminders, account }) => {
|
|
167
167
|
const args = ['calendar', 'create', calendarId, `--summary=${summary}`, `--from=${from}`, `--to=${to}`];
|
|
168
168
|
if (description) args.push(`--description=${description}`);
|
|
@@ -178,7 +178,7 @@ export function registerCalendarTools(server: McpServer): void {
|
|
|
178
178
|
server.registerTool('gog_calendar_update', {
|
|
179
179
|
description: 'Update an existing calendar event. Zoom: withZoom adds a Zoom meeting, regenerateZoom replaces the existing one, removeZoom strips it. removeMeet clears the event\'s Google Meet conference data (e.g. before attaching another provider). Conference flags are independent — use one per call.',
|
|
180
180
|
annotations: { destructiveHint: false },
|
|
181
|
-
inputSchema: {
|
|
181
|
+
inputSchema: z.object({
|
|
182
182
|
calendarId: z.string().describe('Calendar ID'),
|
|
183
183
|
eventId: z.string().describe('Event ID'),
|
|
184
184
|
summary: z.string().optional().describe('New event title'),
|
|
@@ -195,7 +195,7 @@ export function registerCalendarTools(server: McpServer): void {
|
|
|
195
195
|
removeMeet: z.boolean().optional().describe('Remove the event\'s Google Meet video conference (clears conference data only)'),
|
|
196
196
|
...reminderParams,
|
|
197
197
|
account: accountParam,
|
|
198
|
-
},
|
|
198
|
+
}),
|
|
199
199
|
}, async ({ calendarId, eventId, summary, from, to, description, location, attendees, addAttendees, attachments, withZoom, regenerateZoom, removeZoom, removeMeet, reminders, noReminders, account }) => {
|
|
200
200
|
const args = ['calendar', 'update', calendarId, eventId];
|
|
201
201
|
if (summary !== undefined) args.push(`--summary=${summary}`);
|
|
@@ -217,11 +217,11 @@ export function registerCalendarTools(server: McpServer): void {
|
|
|
217
217
|
server.registerTool('gog_calendar_delete', {
|
|
218
218
|
description: 'Delete a calendar event.',
|
|
219
219
|
annotations: { destructiveHint: true },
|
|
220
|
-
inputSchema: {
|
|
220
|
+
inputSchema: z.object({
|
|
221
221
|
calendarId: z.string().describe('Calendar ID'),
|
|
222
222
|
eventId: z.string().describe('Event ID'),
|
|
223
223
|
account: accountParam,
|
|
224
|
-
},
|
|
224
|
+
}),
|
|
225
225
|
}, async ({ calendarId, eventId, account }) => {
|
|
226
226
|
// gog gates this delete behind a confirmation; the runner injects
|
|
227
227
|
// --no-input, so without --force it refuses at runtime.
|
|
@@ -231,13 +231,13 @@ export function registerCalendarTools(server: McpServer): void {
|
|
|
231
231
|
server.registerTool('gog_calendar_respond', {
|
|
232
232
|
description: 'Respond to a calendar event invitation.',
|
|
233
233
|
annotations: { destructiveHint: true },
|
|
234
|
-
inputSchema: {
|
|
234
|
+
inputSchema: z.object({
|
|
235
235
|
calendarId: z.string().describe('Calendar ID'),
|
|
236
236
|
eventId: z.string().describe('Event ID'),
|
|
237
237
|
status: z.enum(['accepted', 'declined', 'tentative']).describe('Response status'),
|
|
238
238
|
comment: z.string().optional().describe('Optional comment to include with response'),
|
|
239
239
|
account: accountParam,
|
|
240
|
-
},
|
|
240
|
+
}),
|
|
241
241
|
}, async ({ calendarId, eventId, status, comment, account }) => {
|
|
242
242
|
const args = ['calendar', 'respond', calendarId, eventId, `--status=${status}`];
|
|
243
243
|
if (comment) args.push(`--comment=${comment}`);
|
package/src/tools/chat.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { McpServer } from '@modelcontextprotocol/
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/server';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import {
|
|
4
4
|
accountParam,
|
|
@@ -49,10 +49,10 @@ export function registerChatTools(server: McpServer): void {
|
|
|
49
49
|
+ 'Start here when you do not yet have a space name; gog_chat_spaces_find is faster when you know the room\'s title.'
|
|
50
50
|
+ workspaceOnlyNote,
|
|
51
51
|
annotations: { readOnlyHint: true },
|
|
52
|
-
inputSchema: {
|
|
52
|
+
inputSchema: z.object({
|
|
53
53
|
...paginationParams,
|
|
54
54
|
account: accountParam,
|
|
55
|
-
},
|
|
55
|
+
}),
|
|
56
56
|
}, async ({ max, pageToken, page, all, account }) => {
|
|
57
57
|
const args = ['chat', 'spaces', 'list'];
|
|
58
58
|
pushPaginationFlags(args, { max, pageToken, page, all });
|
|
@@ -65,12 +65,12 @@ export function registerChatTools(server: McpServer): void {
|
|
|
65
65
|
+ 'user names a room approximately ("the launch room"); pass exact=true to require the whole title. DMs have no '
|
|
66
66
|
+ 'display name — use gog_chat_dm_space to reach a person.' + workspaceOnlyNote,
|
|
67
67
|
annotations: { readOnlyHint: true },
|
|
68
|
-
inputSchema: {
|
|
68
|
+
inputSchema: z.object({
|
|
69
69
|
displayName: z.string().describe('Space display name, or part of one'),
|
|
70
70
|
exact: z.boolean().optional().describe('Require an exact (still case-insensitive) match on the whole display name'),
|
|
71
71
|
max: z.number().int().optional().describe('Max results per page'),
|
|
72
72
|
account: accountParam,
|
|
73
|
-
},
|
|
73
|
+
}),
|
|
74
74
|
}, async ({ displayName, exact, max, account }) => {
|
|
75
75
|
const args = ['chat', 'spaces', 'find', displayName];
|
|
76
76
|
if (exact) args.push('--exact');
|
|
@@ -82,11 +82,11 @@ export function registerChatTools(server: McpServer): void {
|
|
|
82
82
|
description:
|
|
83
83
|
'Create a named Chat space, optionally seeding its membership. Members are added immediately and are notified — this '
|
|
84
84
|
+ 'is visible to other people the moment it runs, so confirm the member list before calling it.' + workspaceOnlyNote,
|
|
85
|
-
inputSchema: {
|
|
85
|
+
inputSchema: z.object({
|
|
86
86
|
displayName: z.string().describe('Display name for the new space'),
|
|
87
87
|
members: z.array(z.string()).optional().describe('Initial members, as email addresses or "users/..." resource names'),
|
|
88
88
|
account: accountParam,
|
|
89
|
-
},
|
|
89
|
+
}),
|
|
90
90
|
}, async ({ displayName, members, account }) => {
|
|
91
91
|
const args = ['chat', 'spaces', 'create', displayName];
|
|
92
92
|
if (members) for (const member of members) args.push(`--member=${member}`);
|
|
@@ -98,11 +98,11 @@ export function registerChatTools(server: McpServer): void {
|
|
|
98
98
|
'List the threads in a space, so a reply can be targeted at an existing conversation rather than starting a new one. '
|
|
99
99
|
+ 'Pass a thread name from here as `thread` to gog_chat_messages_send.' + workspaceOnlyNote,
|
|
100
100
|
annotations: { readOnlyHint: true },
|
|
101
|
-
inputSchema: {
|
|
101
|
+
inputSchema: z.object({
|
|
102
102
|
space: spaceParam,
|
|
103
103
|
...paginationParams,
|
|
104
104
|
account: accountParam,
|
|
105
|
-
},
|
|
105
|
+
}),
|
|
106
106
|
}, async ({ space, max, pageToken, page, all, account }) => {
|
|
107
107
|
const args = ['chat', 'threads', 'list', space];
|
|
108
108
|
pushPaginationFlags(args, { max, pageToken, page, all });
|
|
@@ -117,7 +117,7 @@ export function registerChatTools(server: McpServer): void {
|
|
|
117
117
|
+ 'miss". Newest-first needs an explicit order="createTime desc"; Chat\'s own default is oldest-first.'
|
|
118
118
|
+ workspaceOnlyNote,
|
|
119
119
|
annotations: { readOnlyHint: true },
|
|
120
|
-
inputSchema: {
|
|
120
|
+
inputSchema: z.object({
|
|
121
121
|
space: spaceParam,
|
|
122
122
|
thread: threadParam,
|
|
123
123
|
unread: z.boolean().optional().describe('Only messages posted after the account last read this space'),
|
|
@@ -125,7 +125,7 @@ export function registerChatTools(server: McpServer): void {
|
|
|
125
125
|
.optional().describe('Sort order (Chat default: "createTime asc", i.e. OLDEST first — ask for "createTime desc" when you want the latest messages)'),
|
|
126
126
|
...paginationParams,
|
|
127
127
|
account: accountParam,
|
|
128
|
-
},
|
|
128
|
+
}),
|
|
129
129
|
}, async ({ space, thread, unread, order, max, pageToken, page, all, account }) => {
|
|
130
130
|
const args = ['chat', 'messages', 'list', space];
|
|
131
131
|
if (thread) args.push(`--thread=${thread}`);
|
|
@@ -146,7 +146,7 @@ export function registerChatTools(server: McpServer): void {
|
|
|
146
146
|
+ 'which gog\'s chat scope set does NOT request (re-auth with extraScopes to get it). Missing metadata is OMITTED '
|
|
147
147
|
+ 'rather than defaulted, so an absent `read` means unknown while an explicit false means unread.' + workspaceOnlyNote,
|
|
148
148
|
annotations: { readOnlyHint: true },
|
|
149
|
-
inputSchema: {
|
|
149
|
+
inputSchema: z.object({
|
|
150
150
|
query: z.string().describe('Google Chat filter-syntax query — keywords, or filters such as "from:alice@example.com budget"'),
|
|
151
151
|
order: z.enum(['create_time desc', 'relevance desc']).optional().describe(
|
|
152
152
|
'Sort order. NOTE the snake_case, which differs from gog_chat_messages_list\'s camelCase. "relevance desc" needs '
|
|
@@ -160,7 +160,7 @@ export function registerChatTools(server: McpServer): void {
|
|
|
160
160
|
...paginationParams,
|
|
161
161
|
max: z.number().int().min(1).max(100).optional().describe('Max results per page (1-100; Chat search caps a page at 100)'),
|
|
162
162
|
account: accountParam,
|
|
163
|
-
},
|
|
163
|
+
}),
|
|
164
164
|
}, async ({ query, order, view, markup, max, pageToken, page, all, account }) => {
|
|
165
165
|
const args = ['chat', 'messages', 'search', query];
|
|
166
166
|
if (order) args.push(`--order=${order}`);
|
|
@@ -176,7 +176,7 @@ export function registerChatTools(server: McpServer): void {
|
|
|
176
176
|
+ 'this tool, so treat it like sending mail, not like saving a draft. Pass `thread` to reply inside an existing '
|
|
177
177
|
+ 'conversation (from gog_chat_threads_list or a message\'s thread field); omit it to start a new one. Text supports '
|
|
178
178
|
+ 'Chat\'s markdown-ish formatting (*bold*, _italic_, `code`).' + workspaceOnlyNote,
|
|
179
|
-
inputSchema: {
|
|
179
|
+
inputSchema: z.object({
|
|
180
180
|
space: spaceParam,
|
|
181
181
|
text: z.string().optional().describe('Message text. Optional only when an attachment is supplied.'),
|
|
182
182
|
thread: threadParam,
|
|
@@ -186,7 +186,7 @@ export function registerChatTools(server: McpServer): void {
|
|
|
186
186
|
),
|
|
187
187
|
attachInline: attachInlineParam,
|
|
188
188
|
account: accountParam,
|
|
189
|
-
},
|
|
189
|
+
}),
|
|
190
190
|
}, async ({ space, text, thread, attach, attachInline, account }) => {
|
|
191
191
|
if (text === undefined && !attach?.length && !attachInline?.length) {
|
|
192
192
|
throw new Error('A Chat message needs text, an attachment, or both.');
|
|
@@ -207,12 +207,12 @@ export function registerChatTools(server: McpServer): void {
|
|
|
207
207
|
'Send a direct message to one person by email address, creating the DM space if this is the first message. Delivered '
|
|
208
208
|
+ 'immediately and cannot be unsent through this tool. For a room rather than a person, use gog_chat_messages_send.'
|
|
209
209
|
+ workspaceOnlyNote,
|
|
210
|
-
inputSchema: {
|
|
210
|
+
inputSchema: z.object({
|
|
211
211
|
email: z.string().describe('Recipient email address'),
|
|
212
212
|
text: z.string().describe('Message text'),
|
|
213
213
|
thread: threadParam,
|
|
214
214
|
account: accountParam,
|
|
215
|
-
},
|
|
215
|
+
}),
|
|
216
216
|
}, async ({ email, text, thread, account }) => {
|
|
217
217
|
const args = ['chat', 'dm', 'send', email, `--text=${text}`];
|
|
218
218
|
if (thread) args.push(`--thread=${thread}`);
|
|
@@ -223,10 +223,10 @@ export function registerChatTools(server: McpServer): void {
|
|
|
223
223
|
description:
|
|
224
224
|
'Resolve the DM space for an email address — the bridge from a person to the "spaces/..." name the message tools '
|
|
225
225
|
+ 'want. Creates the space if none exists yet, which is silent: it does not message the person.' + workspaceOnlyNote,
|
|
226
|
-
inputSchema: {
|
|
226
|
+
inputSchema: z.object({
|
|
227
227
|
email: z.string().describe('The other person\'s email address'),
|
|
228
228
|
account: accountParam,
|
|
229
|
-
},
|
|
229
|
+
}),
|
|
230
230
|
}, async ({ email, account }) => {
|
|
231
231
|
return runOrDiagnose(['chat', 'dm', 'space', email], { account });
|
|
232
232
|
});
|
|
@@ -237,12 +237,12 @@ export function registerChatTools(server: McpServer): void {
|
|
|
237
237
|
+ 'SUMMARY per message; come here when you need the individual reactors, or the reaction resource names that '
|
|
238
238
|
+ 'gog_chat_reactions_delete takes.' + workspaceOnlyNote,
|
|
239
239
|
annotations: { readOnlyHint: true },
|
|
240
|
-
inputSchema: {
|
|
240
|
+
inputSchema: z.object({
|
|
241
241
|
message: z.string().describe('Message resource name ("spaces/AAAA/messages/BBBB"), or a bare message ID together with `space`'),
|
|
242
242
|
space: z.string().optional().describe('Space resource name — required only when `message` is a bare ID'),
|
|
243
243
|
...paginationParams,
|
|
244
244
|
account: accountParam,
|
|
245
|
-
},
|
|
245
|
+
}),
|
|
246
246
|
}, async ({ message, space, max, pageToken, page, all, account }) => {
|
|
247
247
|
const args = ['chat', 'messages', 'reactions', 'list', message];
|
|
248
248
|
if (space) args.push(`--space=${space}`);
|
|
@@ -254,12 +254,12 @@ export function registerChatTools(server: McpServer): void {
|
|
|
254
254
|
description:
|
|
255
255
|
'React to a message with an emoji. Visible to the space immediately. Pass the emoji itself ("👍"), not a :shortcode:.'
|
|
256
256
|
+ workspaceOnlyNote,
|
|
257
|
-
inputSchema: {
|
|
257
|
+
inputSchema: z.object({
|
|
258
258
|
message: z.string().describe('Message resource name ("spaces/AAAA/messages/BBBB"), or a bare message ID together with `space`'),
|
|
259
259
|
emoji: z.string().describe('The emoji character to react with, e.g. "👍"'),
|
|
260
260
|
space: z.string().optional().describe('Space resource name — required only when `message` is a bare ID'),
|
|
261
261
|
account: accountParam,
|
|
262
|
-
},
|
|
262
|
+
}),
|
|
263
263
|
}, async ({ message, emoji, space, account }) => {
|
|
264
264
|
const args = ['chat', 'messages', 'reactions', 'create', message, emoji];
|
|
265
265
|
if (space) args.push(`--space=${space}`);
|
|
@@ -272,10 +272,10 @@ export function registerChatTools(server: McpServer): void {
|
|
|
272
272
|
+ 'the message\'s and not the emoji — get it from gog_chat_reactions_list. An account can only remove its own reaction.'
|
|
273
273
|
+ workspaceOnlyNote,
|
|
274
274
|
annotations: { destructiveHint: true },
|
|
275
|
-
inputSchema: {
|
|
275
|
+
inputSchema: z.object({
|
|
276
276
|
reaction: z.string().describe('Reaction resource name, e.g. "spaces/AAAA/messages/BBBB/reactions/CCCC"'),
|
|
277
277
|
account: accountParam,
|
|
278
|
-
},
|
|
278
|
+
}),
|
|
279
279
|
}, async ({ reaction, account }) => {
|
|
280
280
|
return runOrDiagnose(['chat', 'messages', 'reactions', 'delete', reaction], { account });
|
|
281
281
|
});
|
package/src/tools/classroom.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { McpServer } from '@modelcontextprotocol/
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/server';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { accountParam, runOrDiagnose, registerRunTool, pageTokenParam, pageAliasParam, resolvePageToken} from './utils.js';
|
|
4
4
|
|
|
@@ -6,7 +6,7 @@ export function registerClassroomTools(server: McpServer): void {
|
|
|
6
6
|
server.registerTool('gog_classroom_courses_list', {
|
|
7
7
|
description: 'List Google Classroom courses.',
|
|
8
8
|
annotations: { readOnlyHint: true },
|
|
9
|
-
inputSchema: {
|
|
9
|
+
inputSchema: z.object({
|
|
10
10
|
state: z.string().optional().describe('Comma-separated course states (ACTIVE, ARCHIVED, PROVISIONED, DECLINED, SUSPENDED)'),
|
|
11
11
|
teacher: z.string().optional().describe('Filter by teacher user ID'),
|
|
12
12
|
student: z.string().optional().describe('Filter by student user ID'),
|
|
@@ -15,7 +15,7 @@ export function registerClassroomTools(server: McpServer): void {
|
|
|
15
15
|
page: pageAliasParam,
|
|
16
16
|
all: z.boolean().optional().describe('Fetch all pages'),
|
|
17
17
|
account: accountParam,
|
|
18
|
-
},
|
|
18
|
+
}),
|
|
19
19
|
}, async ({ state, teacher, student, max, pageToken, page, all, account }) => {
|
|
20
20
|
const args = ['classroom', 'courses', 'list'];
|
|
21
21
|
if (state) args.push(`--state=${state}`);
|
|
@@ -31,10 +31,10 @@ export function registerClassroomTools(server: McpServer): void {
|
|
|
31
31
|
server.registerTool('gog_classroom_courses_get', {
|
|
32
32
|
description: 'Get a single Google Classroom course by ID.',
|
|
33
33
|
annotations: { readOnlyHint: true },
|
|
34
|
-
inputSchema: {
|
|
34
|
+
inputSchema: z.object({
|
|
35
35
|
courseId: z.string().describe('Course ID'),
|
|
36
36
|
account: accountParam,
|
|
37
|
-
},
|
|
37
|
+
}),
|
|
38
38
|
}, async ({ courseId, account }) => {
|
|
39
39
|
return runOrDiagnose(['classroom', 'courses', 'get', courseId], { account });
|
|
40
40
|
});
|
|
@@ -42,14 +42,14 @@ export function registerClassroomTools(server: McpServer): void {
|
|
|
42
42
|
server.registerTool('gog_classroom_students_list', {
|
|
43
43
|
description: 'List students enrolled in a Google Classroom course.',
|
|
44
44
|
annotations: { readOnlyHint: true },
|
|
45
|
-
inputSchema: {
|
|
45
|
+
inputSchema: z.object({
|
|
46
46
|
courseId: z.string().describe('Course ID'),
|
|
47
47
|
max: z.number().optional().describe('Max results per page'),
|
|
48
48
|
pageToken: pageTokenParam,
|
|
49
49
|
page: pageAliasParam,
|
|
50
50
|
all: z.boolean().optional().describe('Fetch all pages'),
|
|
51
51
|
account: accountParam,
|
|
52
|
-
},
|
|
52
|
+
}),
|
|
53
53
|
}, async ({ courseId, max, pageToken, page, all, account }) => {
|
|
54
54
|
const args = ['classroom', 'students', 'list', courseId];
|
|
55
55
|
if (max !== undefined) args.push(`--max=${max}`);
|
|
@@ -62,11 +62,11 @@ export function registerClassroomTools(server: McpServer): void {
|
|
|
62
62
|
server.registerTool('gog_classroom_students_get', {
|
|
63
63
|
description: 'Get a specific student enrolled in a course.',
|
|
64
64
|
annotations: { readOnlyHint: true },
|
|
65
|
-
inputSchema: {
|
|
65
|
+
inputSchema: z.object({
|
|
66
66
|
courseId: z.string().describe('Course ID'),
|
|
67
67
|
userId: z.string().describe('Student user ID'),
|
|
68
68
|
account: accountParam,
|
|
69
|
-
},
|
|
69
|
+
}),
|
|
70
70
|
}, async ({ courseId, userId, account }) => {
|
|
71
71
|
return runOrDiagnose(['classroom', 'students', 'get', courseId, userId], { account });
|
|
72
72
|
});
|
|
@@ -74,14 +74,14 @@ export function registerClassroomTools(server: McpServer): void {
|
|
|
74
74
|
server.registerTool('gog_classroom_teachers_list', {
|
|
75
75
|
description: 'List teachers in a Google Classroom course.',
|
|
76
76
|
annotations: { readOnlyHint: true },
|
|
77
|
-
inputSchema: {
|
|
77
|
+
inputSchema: z.object({
|
|
78
78
|
courseId: z.string().describe('Course ID'),
|
|
79
79
|
max: z.number().optional().describe('Max results per page'),
|
|
80
80
|
pageToken: pageTokenParam,
|
|
81
81
|
page: pageAliasParam,
|
|
82
82
|
all: z.boolean().optional().describe('Fetch all pages'),
|
|
83
83
|
account: accountParam,
|
|
84
|
-
},
|
|
84
|
+
}),
|
|
85
85
|
}, async ({ courseId, max, pageToken, page, all, account }) => {
|
|
86
86
|
const args = ['classroom', 'teachers', 'list', courseId];
|
|
87
87
|
if (max !== undefined) args.push(`--max=${max}`);
|
|
@@ -94,11 +94,11 @@ export function registerClassroomTools(server: McpServer): void {
|
|
|
94
94
|
server.registerTool('gog_classroom_teachers_get', {
|
|
95
95
|
description: 'Get a specific teacher in a course.',
|
|
96
96
|
annotations: { readOnlyHint: true },
|
|
97
|
-
inputSchema: {
|
|
97
|
+
inputSchema: z.object({
|
|
98
98
|
courseId: z.string().describe('Course ID'),
|
|
99
99
|
userId: z.string().describe('Teacher user ID'),
|
|
100
100
|
account: accountParam,
|
|
101
|
-
},
|
|
101
|
+
}),
|
|
102
102
|
}, async ({ courseId, userId, account }) => {
|
|
103
103
|
return runOrDiagnose(['classroom', 'teachers', 'get', courseId, userId], { account });
|
|
104
104
|
});
|
|
@@ -106,7 +106,7 @@ export function registerClassroomTools(server: McpServer): void {
|
|
|
106
106
|
server.registerTool('gog_classroom_roster', {
|
|
107
107
|
description: 'List the full roster (students and/or teachers) of a Google Classroom course. Omit both flags to return both groups.',
|
|
108
108
|
annotations: { readOnlyHint: true },
|
|
109
|
-
inputSchema: {
|
|
109
|
+
inputSchema: z.object({
|
|
110
110
|
courseId: z.string().describe('Course ID'),
|
|
111
111
|
students: z.boolean().optional().describe('Include students only'),
|
|
112
112
|
teachers: z.boolean().optional().describe('Include teachers only'),
|
|
@@ -115,7 +115,7 @@ export function registerClassroomTools(server: McpServer): void {
|
|
|
115
115
|
page: pageAliasParam,
|
|
116
116
|
all: z.boolean().optional().describe('Fetch all pages'),
|
|
117
117
|
account: accountParam,
|
|
118
|
-
},
|
|
118
|
+
}),
|
|
119
119
|
}, async ({ courseId, students, teachers, max, pageToken, page, all, account }) => {
|
|
120
120
|
const args = ['classroom', 'roster', courseId];
|
|
121
121
|
if (students) args.push('--students');
|
|
@@ -130,7 +130,7 @@ export function registerClassroomTools(server: McpServer): void {
|
|
|
130
130
|
server.registerTool('gog_classroom_coursework_list', {
|
|
131
131
|
description: 'List coursework (assignments, questions, materials) for a course.',
|
|
132
132
|
annotations: { readOnlyHint: true },
|
|
133
|
-
inputSchema: {
|
|
133
|
+
inputSchema: z.object({
|
|
134
134
|
courseId: z.string().describe('Course ID'),
|
|
135
135
|
state: z.string().optional().describe('Filter by coursework state (PUBLISHED, DRAFT, DELETED)'),
|
|
136
136
|
topic: z.string().optional().describe('Filter by topic ID'),
|
|
@@ -141,7 +141,7 @@ export function registerClassroomTools(server: McpServer): void {
|
|
|
141
141
|
all: z.boolean().optional().describe('Fetch all pages'),
|
|
142
142
|
scanPages: z.number().optional().describe('Max pages to scan when filtering'),
|
|
143
143
|
account: accountParam,
|
|
144
|
-
},
|
|
144
|
+
}),
|
|
145
145
|
}, async ({ courseId, state, topic, orderBy, max, pageToken, page, all, scanPages, account }) => {
|
|
146
146
|
const args = ['classroom', 'coursework', 'list', courseId];
|
|
147
147
|
if (state) args.push(`--state=${state}`);
|
|
@@ -158,11 +158,11 @@ export function registerClassroomTools(server: McpServer): void {
|
|
|
158
158
|
server.registerTool('gog_classroom_coursework_get', {
|
|
159
159
|
description: 'Get a single coursework item by ID.',
|
|
160
160
|
annotations: { readOnlyHint: true },
|
|
161
|
-
inputSchema: {
|
|
161
|
+
inputSchema: z.object({
|
|
162
162
|
courseId: z.string().describe('Course ID'),
|
|
163
163
|
courseworkId: z.string().describe('Coursework ID'),
|
|
164
164
|
account: accountParam,
|
|
165
|
-
},
|
|
165
|
+
}),
|
|
166
166
|
}, async ({ courseId, courseworkId, account }) => {
|
|
167
167
|
return runOrDiagnose(['classroom', 'coursework', 'get', courseId, courseworkId], { account });
|
|
168
168
|
});
|
|
@@ -170,7 +170,7 @@ export function registerClassroomTools(server: McpServer): void {
|
|
|
170
170
|
server.registerTool('gog_classroom_submissions_list', {
|
|
171
171
|
description: 'List student submissions for a coursework item.',
|
|
172
172
|
annotations: { readOnlyHint: true },
|
|
173
|
-
inputSchema: {
|
|
173
|
+
inputSchema: z.object({
|
|
174
174
|
courseId: z.string().describe('Course ID'),
|
|
175
175
|
courseworkId: z.string().describe('Coursework ID'),
|
|
176
176
|
state: z.string().optional().describe('Filter by submission state'),
|
|
@@ -181,7 +181,7 @@ export function registerClassroomTools(server: McpServer): void {
|
|
|
181
181
|
page: pageAliasParam,
|
|
182
182
|
all: z.boolean().optional().describe('Fetch all pages'),
|
|
183
183
|
account: accountParam,
|
|
184
|
-
},
|
|
184
|
+
}),
|
|
185
185
|
}, async ({ courseId, courseworkId, state, late, user, max, pageToken, page, all, account }) => {
|
|
186
186
|
const args = ['classroom', 'submissions', 'list', courseId, courseworkId];
|
|
187
187
|
if (state) args.push(`--state=${state}`);
|
|
@@ -197,12 +197,12 @@ export function registerClassroomTools(server: McpServer): void {
|
|
|
197
197
|
server.registerTool('gog_classroom_submissions_get', {
|
|
198
198
|
description: 'Get a single submission by ID.',
|
|
199
199
|
annotations: { readOnlyHint: true },
|
|
200
|
-
inputSchema: {
|
|
200
|
+
inputSchema: z.object({
|
|
201
201
|
courseId: z.string().describe('Course ID'),
|
|
202
202
|
courseworkId: z.string().describe('Coursework ID'),
|
|
203
203
|
submissionId: z.string().describe('Submission ID'),
|
|
204
204
|
account: accountParam,
|
|
205
|
-
},
|
|
205
|
+
}),
|
|
206
206
|
}, async ({ courseId, courseworkId, submissionId, account }) => {
|
|
207
207
|
return runOrDiagnose(['classroom', 'submissions', 'get', courseId, courseworkId, submissionId], { account });
|
|
208
208
|
});
|
|
@@ -210,14 +210,14 @@ export function registerClassroomTools(server: McpServer): void {
|
|
|
210
210
|
server.registerTool('gog_classroom_submissions_grade', {
|
|
211
211
|
description: 'Grade a student submission. Set draft and/or assigned grade values.',
|
|
212
212
|
annotations: { destructiveHint: true },
|
|
213
|
-
inputSchema: {
|
|
213
|
+
inputSchema: z.object({
|
|
214
214
|
courseId: z.string().describe('Course ID'),
|
|
215
215
|
courseworkId: z.string().describe('Coursework ID'),
|
|
216
216
|
submissionId: z.string().describe('Submission ID'),
|
|
217
217
|
draft: z.string().optional().describe('Draft grade value'),
|
|
218
218
|
assigned: z.string().optional().describe('Assigned (final) grade value'),
|
|
219
219
|
account: accountParam,
|
|
220
|
-
},
|
|
220
|
+
}),
|
|
221
221
|
}, async ({ courseId, courseworkId, submissionId, draft, assigned, account }) => {
|
|
222
222
|
const args = ['classroom', 'submissions', 'grade', courseId, courseworkId, submissionId];
|
|
223
223
|
if (draft) args.push(`--draft=${draft}`);
|
|
@@ -228,12 +228,12 @@ export function registerClassroomTools(server: McpServer): void {
|
|
|
228
228
|
server.registerTool('gog_classroom_submissions_return', {
|
|
229
229
|
description: 'Return a graded submission to the student.',
|
|
230
230
|
annotations: { destructiveHint: true },
|
|
231
|
-
inputSchema: {
|
|
231
|
+
inputSchema: z.object({
|
|
232
232
|
courseId: z.string().describe('Course ID'),
|
|
233
233
|
courseworkId: z.string().describe('Coursework ID'),
|
|
234
234
|
submissionId: z.string().describe('Submission ID'),
|
|
235
235
|
account: accountParam,
|
|
236
|
-
},
|
|
236
|
+
}),
|
|
237
237
|
}, async ({ courseId, courseworkId, submissionId, account }) => {
|
|
238
238
|
return runOrDiagnose(['classroom', 'submissions', 'return', courseId, courseworkId, submissionId], { account });
|
|
239
239
|
});
|
|
@@ -241,12 +241,12 @@ export function registerClassroomTools(server: McpServer): void {
|
|
|
241
241
|
server.registerTool('gog_classroom_submissions_turn_in', {
|
|
242
242
|
description: 'Turn in a student submission (student action).',
|
|
243
243
|
annotations: { destructiveHint: true },
|
|
244
|
-
inputSchema: {
|
|
244
|
+
inputSchema: z.object({
|
|
245
245
|
courseId: z.string().describe('Course ID'),
|
|
246
246
|
courseworkId: z.string().describe('Coursework ID'),
|
|
247
247
|
submissionId: z.string().describe('Submission ID'),
|
|
248
248
|
account: accountParam,
|
|
249
|
-
},
|
|
249
|
+
}),
|
|
250
250
|
}, async ({ courseId, courseworkId, submissionId, account }) => {
|
|
251
251
|
return runOrDiagnose(['classroom', 'submissions', 'turn-in', courseId, courseworkId, submissionId], { account });
|
|
252
252
|
});
|
|
@@ -254,12 +254,12 @@ export function registerClassroomTools(server: McpServer): void {
|
|
|
254
254
|
server.registerTool('gog_classroom_submissions_reclaim', {
|
|
255
255
|
description: 'Reclaim a turned-in submission (student action to edit a submission).',
|
|
256
256
|
annotations: { destructiveHint: true },
|
|
257
|
-
inputSchema: {
|
|
257
|
+
inputSchema: z.object({
|
|
258
258
|
courseId: z.string().describe('Course ID'),
|
|
259
259
|
courseworkId: z.string().describe('Coursework ID'),
|
|
260
260
|
submissionId: z.string().describe('Submission ID'),
|
|
261
261
|
account: accountParam,
|
|
262
|
-
},
|
|
262
|
+
}),
|
|
263
263
|
}, async ({ courseId, courseworkId, submissionId, account }) => {
|
|
264
264
|
return runOrDiagnose(['classroom', 'submissions', 'reclaim', courseId, courseworkId, submissionId], { account });
|
|
265
265
|
});
|
|
@@ -267,7 +267,7 @@ export function registerClassroomTools(server: McpServer): void {
|
|
|
267
267
|
server.registerTool('gog_classroom_announcements_list', {
|
|
268
268
|
description: 'List announcements in a Google Classroom course.',
|
|
269
269
|
annotations: { readOnlyHint: true },
|
|
270
|
-
inputSchema: {
|
|
270
|
+
inputSchema: z.object({
|
|
271
271
|
courseId: z.string().describe('Course ID'),
|
|
272
272
|
state: z.string().optional().describe('Filter by announcement state'),
|
|
273
273
|
orderBy: z.string().optional().describe('Sort order'),
|
|
@@ -276,7 +276,7 @@ export function registerClassroomTools(server: McpServer): void {
|
|
|
276
276
|
page: pageAliasParam,
|
|
277
277
|
all: z.boolean().optional().describe('Fetch all pages'),
|
|
278
278
|
account: accountParam,
|
|
279
|
-
},
|
|
279
|
+
}),
|
|
280
280
|
}, async ({ courseId, state, orderBy, max, pageToken, page, all, account }) => {
|
|
281
281
|
const args = ['classroom', 'announcements', 'list', courseId];
|
|
282
282
|
if (state) args.push(`--state=${state}`);
|
|
@@ -291,24 +291,24 @@ export function registerClassroomTools(server: McpServer): void {
|
|
|
291
291
|
server.registerTool('gog_classroom_announcements_get', {
|
|
292
292
|
description: 'Get a single announcement by ID.',
|
|
293
293
|
annotations: { readOnlyHint: true },
|
|
294
|
-
inputSchema: {
|
|
294
|
+
inputSchema: z.object({
|
|
295
295
|
courseId: z.string().describe('Course ID'),
|
|
296
296
|
announcementId: z.string().describe('Announcement ID'),
|
|
297
297
|
account: accountParam,
|
|
298
|
-
},
|
|
298
|
+
}),
|
|
299
299
|
}, async ({ courseId, announcementId, account }) => {
|
|
300
300
|
return runOrDiagnose(['classroom', 'announcements', 'get', courseId, announcementId], { account });
|
|
301
301
|
});
|
|
302
302
|
|
|
303
303
|
server.registerTool('gog_classroom_announcements_create', {
|
|
304
304
|
description: 'Create an announcement in a Google Classroom course.',
|
|
305
|
-
inputSchema: {
|
|
305
|
+
inputSchema: z.object({
|
|
306
306
|
courseId: z.string().describe('Course ID'),
|
|
307
307
|
text: z.string().describe('Announcement text'),
|
|
308
308
|
state: z.enum(['PUBLISHED', 'DRAFT']).optional().describe('State'),
|
|
309
309
|
scheduled: z.string().optional().describe('Scheduled publish time'),
|
|
310
310
|
account: accountParam,
|
|
311
|
-
},
|
|
311
|
+
}),
|
|
312
312
|
}, async ({ courseId, text, state, scheduled, account }) => {
|
|
313
313
|
const args = ['classroom', 'announcements', 'create', courseId, `--text=${text}`];
|
|
314
314
|
if (state) args.push(`--state=${state}`);
|
|
@@ -319,14 +319,14 @@ export function registerClassroomTools(server: McpServer): void {
|
|
|
319
319
|
server.registerTool('gog_classroom_topics_list', {
|
|
320
320
|
description: 'List topics in a Google Classroom course.',
|
|
321
321
|
annotations: { readOnlyHint: true },
|
|
322
|
-
inputSchema: {
|
|
322
|
+
inputSchema: z.object({
|
|
323
323
|
courseId: z.string().describe('Course ID'),
|
|
324
324
|
max: z.number().optional().describe('Max results per page'),
|
|
325
325
|
pageToken: pageTokenParam,
|
|
326
326
|
page: pageAliasParam,
|
|
327
327
|
all: z.boolean().optional().describe('Fetch all pages'),
|
|
328
328
|
account: accountParam,
|
|
329
|
-
},
|
|
329
|
+
}),
|
|
330
330
|
}, async ({ courseId, max, pageToken, page, all, account }) => {
|
|
331
331
|
const args = ['classroom', 'topics', 'list', courseId];
|
|
332
332
|
if (max !== undefined) args.push(`--max=${max}`);
|
|
@@ -339,11 +339,11 @@ export function registerClassroomTools(server: McpServer): void {
|
|
|
339
339
|
server.registerTool('gog_classroom_topics_get', {
|
|
340
340
|
description: 'Get a single topic by ID.',
|
|
341
341
|
annotations: { readOnlyHint: true },
|
|
342
|
-
inputSchema: {
|
|
342
|
+
inputSchema: z.object({
|
|
343
343
|
courseId: z.string().describe('Course ID'),
|
|
344
344
|
topicId: z.string().describe('Topic ID'),
|
|
345
345
|
account: accountParam,
|
|
346
|
-
},
|
|
346
|
+
}),
|
|
347
347
|
}, async ({ courseId, topicId, account }) => {
|
|
348
348
|
return runOrDiagnose(['classroom', 'topics', 'get', courseId, topicId], { account });
|
|
349
349
|
});
|
|
@@ -351,7 +351,7 @@ export function registerClassroomTools(server: McpServer): void {
|
|
|
351
351
|
server.registerTool('gog_classroom_invitations_list', {
|
|
352
352
|
description: 'List Google Classroom invitations.',
|
|
353
353
|
annotations: { readOnlyHint: true },
|
|
354
|
-
inputSchema: {
|
|
354
|
+
inputSchema: z.object({
|
|
355
355
|
course: z.string().optional().describe('Filter by course ID'),
|
|
356
356
|
user: z.string().optional().describe('Filter by user ID'),
|
|
357
357
|
max: z.number().optional().describe('Max results per page'),
|
|
@@ -359,7 +359,7 @@ export function registerClassroomTools(server: McpServer): void {
|
|
|
359
359
|
page: pageAliasParam,
|
|
360
360
|
all: z.boolean().optional().describe('Fetch all pages'),
|
|
361
361
|
account: accountParam,
|
|
362
|
-
},
|
|
362
|
+
}),
|
|
363
363
|
}, async ({ course, user, max, pageToken, page, all, account }) => {
|
|
364
364
|
const args = ['classroom', 'invitations', 'list'];
|
|
365
365
|
if (course) args.push(`--course=${course}`);
|
|
@@ -374,20 +374,20 @@ export function registerClassroomTools(server: McpServer): void {
|
|
|
374
374
|
server.registerTool('gog_classroom_invitations_get', {
|
|
375
375
|
description: 'Get a single invitation by ID.',
|
|
376
376
|
annotations: { readOnlyHint: true },
|
|
377
|
-
inputSchema: {
|
|
377
|
+
inputSchema: z.object({
|
|
378
378
|
invitationId: z.string().describe('Invitation ID'),
|
|
379
379
|
account: accountParam,
|
|
380
|
-
},
|
|
380
|
+
}),
|
|
381
381
|
}, async ({ invitationId, account }) => {
|
|
382
382
|
return runOrDiagnose(['classroom', 'invitations', 'get', invitationId], { account });
|
|
383
383
|
});
|
|
384
384
|
|
|
385
385
|
server.registerTool('gog_classroom_invitations_accept', {
|
|
386
386
|
description: 'Accept a Google Classroom invitation.',
|
|
387
|
-
inputSchema: {
|
|
387
|
+
inputSchema: z.object({
|
|
388
388
|
invitationId: z.string().describe('Invitation ID'),
|
|
389
389
|
account: accountParam,
|
|
390
|
-
},
|
|
390
|
+
}),
|
|
391
391
|
}, async ({ invitationId, account }) => {
|
|
392
392
|
return runOrDiagnose(['classroom', 'invitations', 'accept', invitationId], { account });
|
|
393
393
|
});
|
|
@@ -395,10 +395,10 @@ export function registerClassroomTools(server: McpServer): void {
|
|
|
395
395
|
server.registerTool('gog_classroom_profile_get', {
|
|
396
396
|
description: 'Get a Google Classroom user profile. Omit userId to fetch the authenticated user.',
|
|
397
397
|
annotations: { readOnlyHint: true },
|
|
398
|
-
inputSchema: {
|
|
398
|
+
inputSchema: z.object({
|
|
399
399
|
userId: z.string().optional().describe('User ID (omit for self)'),
|
|
400
400
|
account: accountParam,
|
|
401
|
-
},
|
|
401
|
+
}),
|
|
402
402
|
}, async ({ userId, account }) => {
|
|
403
403
|
const args = ['classroom', 'profile', 'get'];
|
|
404
404
|
if (userId) args.push(userId);
|