gogcli-mcp 4.2.5 → 4.4.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.
Files changed (63) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/dist/index.js +1340 -262
  4. package/dist/lib.js +1381 -260
  5. package/manifest.json +9 -2
  6. package/package.json +2 -2
  7. package/server.json +2 -2
  8. package/src/arg-guard.ts +68 -0
  9. package/src/argv.ts +27 -0
  10. package/src/attachment-root.ts +111 -0
  11. package/src/attachments.ts +1 -0
  12. package/src/blob-upload.ts +4 -2
  13. package/src/dispatch-confirmation.ts +277 -0
  14. package/src/file-roots.ts +66 -0
  15. package/src/gmail-dispatch-guard.ts +70 -30
  16. package/src/gmail-results.ts +21 -2
  17. package/src/lib.ts +15 -0
  18. package/src/run-path-guard.ts +118 -0
  19. package/src/runner.ts +86 -18
  20. package/src/send-confirm-token.ts +167 -0
  21. package/src/tools/api.ts +60 -7
  22. package/src/tools/appscript.ts +12 -8
  23. package/src/tools/auth.ts +13 -3
  24. package/src/tools/calendar.ts +178 -15
  25. package/src/tools/chat.ts +94 -18
  26. package/src/tools/classroom.ts +110 -28
  27. package/src/tools/contacts.ts +5 -3
  28. package/src/tools/docs.ts +8 -6
  29. package/src/tools/drive.ts +96 -20
  30. package/src/tools/gmail.ts +175 -24
  31. package/src/tools/sheets.ts +10 -8
  32. package/src/tools/slides.ts +14 -9
  33. package/src/tools/tasks.ts +7 -5
  34. package/src/tools/utils.ts +52 -6
  35. package/tests/arg-guard.test.ts +80 -0
  36. package/tests/attachment-root.test.ts +130 -0
  37. package/tests/attachments.test.ts +8 -0
  38. package/tests/blob-upload.test.ts +4 -3
  39. package/tests/file-roots.test.ts +101 -0
  40. package/tests/gmail-dispatch-guard.test.ts +270 -11
  41. package/tests/gmail-results.test.ts +35 -1
  42. package/tests/run-path-guard.test.ts +142 -0
  43. package/tests/runner.test.ts +136 -0
  44. package/tests/send-confirm-token.test.ts +200 -0
  45. package/tests/tools/api.test.ts +74 -8
  46. package/tests/tools/appscript.test.ts +34 -8
  47. package/tests/tools/auth.test.ts +44 -17
  48. package/tests/tools/calendar.test.ts +33 -28
  49. package/tests/tools/chat.test.ts +50 -21
  50. package/tests/tools/classroom.test.ts +43 -39
  51. package/tests/tools/contacts.test.ts +3 -2
  52. package/tests/tools/dispatch-gates.test.ts +396 -0
  53. package/tests/tools/docs.test.ts +44 -15
  54. package/tests/tools/drive.test.ts +110 -20
  55. package/tests/tools/gmail-confirm-token.test.ts +274 -0
  56. package/tests/tools/gmail.test.ts +227 -29
  57. package/tests/tools/run-tool-examples.test.ts +69 -0
  58. package/tests/tools/run-vets.test.ts +131 -0
  59. package/tests/tools/sheets.test.ts +16 -15
  60. package/tests/tools/slides.test.ts +47 -11
  61. package/tests/tools/tasks.test.ts +7 -6
  62. package/tests/tools/utils.test.ts +32 -31
  63. package/vitest.config.ts +5 -0
@@ -2,10 +2,14 @@ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
2
  import { registerClassroomTools } from '../../src/tools/classroom.js';
3
3
  import * as runner from '../../src/runner.js';
4
4
  import { createTestHarness, type TestHarness } from '@chrischall/mcp-utils/test';
5
+ import { pos } from '../../src/argv.js';
5
6
 
6
7
  vi.mock('../../src/runner.js');
7
8
 
8
- const setupHandlers = () => createTestHarness(registerClassroomTools);
9
+ // The dispatch rail asks before guest-visible / outbound calls; this client accepts.
10
+ const setupHandlers = () => createTestHarness(registerClassroomTools, {
11
+ elicitation: async () => ({ action: 'accept', content: { confirmed: true } }),
12
+ });
9
13
 
10
14
  let harness: TestHarness;
11
15
 
@@ -51,7 +55,7 @@ describe('gog_classroom_courses_list', () => {
51
55
  describe('gog_classroom_courses_get', () => {
52
56
  it('calls run with courseId', async () => {
53
57
  await harness.callTool('gog_classroom_courses_get', { courseId: 'c1' });
54
- expect(runner.run).toHaveBeenCalledWith(['classroom', 'courses', 'get', 'c1'], { account: undefined });
58
+ expect(runner.run).toHaveBeenCalledWith(['classroom', 'courses', 'get', pos('c1')], { account: undefined });
55
59
  });
56
60
 
57
61
  it('returns error text on failure', async () => {
@@ -64,20 +68,20 @@ describe('gog_classroom_courses_get', () => {
64
68
  describe('gog_classroom_students_list', () => {
65
69
  it('calls run with courseId only', async () => {
66
70
  await harness.callTool('gog_classroom_students_list', { courseId: 'c1' });
67
- expect(runner.run).toHaveBeenCalledWith(['classroom', 'students', 'list', 'c1'], { account: undefined });
71
+ expect(runner.run).toHaveBeenCalledWith(['classroom', 'students', 'list', pos('c1')], { account: undefined });
68
72
  });
69
73
 
70
74
  it('passes all listing flags', async () => {
71
75
  await harness.callTool('gog_classroom_students_list', { courseId: 'c1', max: 20, page: 'tok', all: true });
72
76
  expect(runner.run).toHaveBeenCalledWith(
73
- ['classroom', 'students', 'list', 'c1', '--max=20', '--page=tok', '--all'],
77
+ ['classroom', 'students', 'list', pos('c1'), '--max=20', '--page=tok', '--all'],
74
78
  { account: undefined },
75
79
  );
76
80
  });
77
81
 
78
82
  it('omits --all when false', async () => {
79
83
  await harness.callTool('gog_classroom_students_list', { courseId: 'c1', all: false });
80
- expect(runner.run).toHaveBeenCalledWith(['classroom', 'students', 'list', 'c1'], { account: undefined });
84
+ expect(runner.run).toHaveBeenCalledWith(['classroom', 'students', 'list', pos('c1')], { account: undefined });
81
85
  });
82
86
 
83
87
  it('returns error text on failure', async () => {
@@ -90,7 +94,7 @@ describe('gog_classroom_students_list', () => {
90
94
  describe('gog_classroom_students_get', () => {
91
95
  it('calls run with courseId and userId', async () => {
92
96
  await harness.callTool('gog_classroom_students_get', { courseId: 'c1', userId: 'u1' });
93
- expect(runner.run).toHaveBeenCalledWith(['classroom', 'students', 'get', 'c1', 'u1'], { account: undefined });
97
+ expect(runner.run).toHaveBeenCalledWith(['classroom', 'students', 'get', pos('c1'), pos('u1')], { account: undefined });
94
98
  });
95
99
 
96
100
  it('returns error text on failure', async () => {
@@ -103,20 +107,20 @@ describe('gog_classroom_students_get', () => {
103
107
  describe('gog_classroom_teachers_list', () => {
104
108
  it('calls run with courseId only', async () => {
105
109
  await harness.callTool('gog_classroom_teachers_list', { courseId: 'c1' });
106
- expect(runner.run).toHaveBeenCalledWith(['classroom', 'teachers', 'list', 'c1'], { account: undefined });
110
+ expect(runner.run).toHaveBeenCalledWith(['classroom', 'teachers', 'list', pos('c1')], { account: undefined });
107
111
  });
108
112
 
109
113
  it('passes all listing flags', async () => {
110
114
  await harness.callTool('gog_classroom_teachers_list', { courseId: 'c1', max: 20, page: 'tok', all: true });
111
115
  expect(runner.run).toHaveBeenCalledWith(
112
- ['classroom', 'teachers', 'list', 'c1', '--max=20', '--page=tok', '--all'],
116
+ ['classroom', 'teachers', 'list', pos('c1'), '--max=20', '--page=tok', '--all'],
113
117
  { account: undefined },
114
118
  );
115
119
  });
116
120
 
117
121
  it('omits --all when false', async () => {
118
122
  await harness.callTool('gog_classroom_teachers_list', { courseId: 'c1', all: false });
119
- expect(runner.run).toHaveBeenCalledWith(['classroom', 'teachers', 'list', 'c1'], { account: undefined });
123
+ expect(runner.run).toHaveBeenCalledWith(['classroom', 'teachers', 'list', pos('c1')], { account: undefined });
120
124
  });
121
125
 
122
126
  it('returns error text on failure', async () => {
@@ -129,7 +133,7 @@ describe('gog_classroom_teachers_list', () => {
129
133
  describe('gog_classroom_teachers_get', () => {
130
134
  it('calls run with courseId and userId', async () => {
131
135
  await harness.callTool('gog_classroom_teachers_get', { courseId: 'c1', userId: 'u1' });
132
- expect(runner.run).toHaveBeenCalledWith(['classroom', 'teachers', 'get', 'c1', 'u1'], { account: undefined });
136
+ expect(runner.run).toHaveBeenCalledWith(['classroom', 'teachers', 'get', pos('c1'), pos('u1')], { account: undefined });
133
137
  });
134
138
 
135
139
  it('returns error text on failure', async () => {
@@ -142,20 +146,20 @@ describe('gog_classroom_teachers_get', () => {
142
146
  describe('gog_classroom_roster', () => {
143
147
  it('calls run with courseId only', async () => {
144
148
  await harness.callTool('gog_classroom_roster', { courseId: 'c1' });
145
- expect(runner.run).toHaveBeenCalledWith(['classroom', 'roster', 'c1'], { account: undefined });
149
+ expect(runner.run).toHaveBeenCalledWith(['classroom', 'roster', pos('c1')], { account: undefined });
146
150
  });
147
151
 
148
152
  it('passes all flags', async () => {
149
153
  await harness.callTool('gog_classroom_roster', { courseId: 'c1', students: true, teachers: true, max: 50, page: 'tok', all: true });
150
154
  expect(runner.run).toHaveBeenCalledWith(
151
- ['classroom', 'roster', 'c1', '--students', '--teachers', '--max=50', '--page=tok', '--all'],
155
+ ['classroom', 'roster', pos('c1'), '--students', '--teachers', '--max=50', '--page=tok', '--all'],
152
156
  { account: undefined },
153
157
  );
154
158
  });
155
159
 
156
160
  it('omits boolean flags when false', async () => {
157
161
  await harness.callTool('gog_classroom_roster', { courseId: 'c1', students: false, teachers: false, all: false });
158
- expect(runner.run).toHaveBeenCalledWith(['classroom', 'roster', 'c1'], { account: undefined });
162
+ expect(runner.run).toHaveBeenCalledWith(['classroom', 'roster', pos('c1')], { account: undefined });
159
163
  });
160
164
 
161
165
  it('returns error text on failure', async () => {
@@ -168,7 +172,7 @@ describe('gog_classroom_roster', () => {
168
172
  describe('gog_classroom_coursework_list', () => {
169
173
  it('calls run with courseId only', async () => {
170
174
  await harness.callTool('gog_classroom_coursework_list', { courseId: 'c1' });
171
- expect(runner.run).toHaveBeenCalledWith(['classroom', 'coursework', 'list', 'c1'], { account: undefined });
175
+ expect(runner.run).toHaveBeenCalledWith(['classroom', 'coursework', 'list', pos('c1')], { account: undefined });
172
176
  });
173
177
 
174
178
  it('passes all listing flags', async () => {
@@ -183,14 +187,14 @@ describe('gog_classroom_coursework_list', () => {
183
187
  scanPages: 5,
184
188
  });
185
189
  expect(runner.run).toHaveBeenCalledWith(
186
- ['classroom', 'coursework', 'list', 'c1', '--state=PUBLISHED', '--topic=t1', '--order-by=updateTime desc', '--max=50', '--page=tok', '--all', '--scan-pages=5'],
190
+ ['classroom', 'coursework', 'list', pos('c1'), '--state=PUBLISHED', '--topic=t1', '--order-by=updateTime desc', '--max=50', '--page=tok', '--all', '--scan-pages=5'],
187
191
  { account: undefined },
188
192
  );
189
193
  });
190
194
 
191
195
  it('omits --all when false', async () => {
192
196
  await harness.callTool('gog_classroom_coursework_list', { courseId: 'c1', all: false });
193
- expect(runner.run).toHaveBeenCalledWith(['classroom', 'coursework', 'list', 'c1'], { account: undefined });
197
+ expect(runner.run).toHaveBeenCalledWith(['classroom', 'coursework', 'list', pos('c1')], { account: undefined });
194
198
  });
195
199
 
196
200
  it('returns error text on failure', async () => {
@@ -203,7 +207,7 @@ describe('gog_classroom_coursework_list', () => {
203
207
  describe('gog_classroom_coursework_get', () => {
204
208
  it('calls run with courseId and courseworkId', async () => {
205
209
  await harness.callTool('gog_classroom_coursework_get', { courseId: 'c1', courseworkId: 'w1' });
206
- expect(runner.run).toHaveBeenCalledWith(['classroom', 'coursework', 'get', 'c1', 'w1'], { account: undefined });
210
+ expect(runner.run).toHaveBeenCalledWith(['classroom', 'coursework', 'get', pos('c1'), pos('w1')], { account: undefined });
207
211
  });
208
212
 
209
213
  it('returns error text on failure', async () => {
@@ -216,7 +220,7 @@ describe('gog_classroom_coursework_get', () => {
216
220
  describe('gog_classroom_submissions_list', () => {
217
221
  it('calls run with ids only', async () => {
218
222
  await harness.callTool('gog_classroom_submissions_list', { courseId: 'c1', courseworkId: 'w1' });
219
- expect(runner.run).toHaveBeenCalledWith(['classroom', 'submissions', 'list', 'c1', 'w1'], { account: undefined });
223
+ expect(runner.run).toHaveBeenCalledWith(['classroom', 'submissions', 'list', pos('c1'), pos('w1')], { account: undefined });
220
224
  });
221
225
 
222
226
  it('passes all listing flags', async () => {
@@ -231,14 +235,14 @@ describe('gog_classroom_submissions_list', () => {
231
235
  all: true,
232
236
  });
233
237
  expect(runner.run).toHaveBeenCalledWith(
234
- ['classroom', 'submissions', 'list', 'c1', 'w1', '--state=TURNED_IN', '--late=late', '--user=u1', '--max=20', '--page=tok', '--all'],
238
+ ['classroom', 'submissions', 'list', pos('c1'), pos('w1'), '--state=TURNED_IN', '--late=late', '--user=u1', '--max=20', '--page=tok', '--all'],
235
239
  { account: undefined },
236
240
  );
237
241
  });
238
242
 
239
243
  it('omits --all when false', async () => {
240
244
  await harness.callTool('gog_classroom_submissions_list', { courseId: 'c1', courseworkId: 'w1', all: false });
241
- expect(runner.run).toHaveBeenCalledWith(['classroom', 'submissions', 'list', 'c1', 'w1'], { account: undefined });
245
+ expect(runner.run).toHaveBeenCalledWith(['classroom', 'submissions', 'list', pos('c1'), pos('w1')], { account: undefined });
242
246
  });
243
247
 
244
248
  it('returns error text on failure', async () => {
@@ -251,7 +255,7 @@ describe('gog_classroom_submissions_list', () => {
251
255
  describe('gog_classroom_submissions_get', () => {
252
256
  it('calls run with all three ids', async () => {
253
257
  await harness.callTool('gog_classroom_submissions_get', { courseId: 'c1', courseworkId: 'w1', submissionId: 's1' });
254
- expect(runner.run).toHaveBeenCalledWith(['classroom', 'submissions', 'get', 'c1', 'w1', 's1'], { account: undefined });
258
+ expect(runner.run).toHaveBeenCalledWith(['classroom', 'submissions', 'get', pos('c1'), pos('w1'), pos('s1')], { account: undefined });
255
259
  });
256
260
 
257
261
  it('returns error text on failure', async () => {
@@ -264,13 +268,13 @@ describe('gog_classroom_submissions_get', () => {
264
268
  describe('gog_classroom_submissions_grade', () => {
265
269
  it('calls run with ids only', async () => {
266
270
  await harness.callTool('gog_classroom_submissions_grade', { courseId: 'c1', courseworkId: 'w1', submissionId: 's1' });
267
- expect(runner.run).toHaveBeenCalledWith(['classroom', 'submissions', 'grade', 'c1', 'w1', 's1'], { account: undefined });
271
+ expect(runner.run).toHaveBeenCalledWith(['classroom', 'submissions', 'grade', pos('c1'), pos('w1'), pos('s1')], { account: undefined });
268
272
  });
269
273
 
270
274
  it('passes --draft and --assigned when provided', async () => {
271
275
  await harness.callTool('gog_classroom_submissions_grade', { courseId: 'c1', courseworkId: 'w1', submissionId: 's1', draft: '90', assigned: '95' });
272
276
  expect(runner.run).toHaveBeenCalledWith(
273
- ['classroom', 'submissions', 'grade', 'c1', 'w1', 's1', '--draft=90', '--assigned=95'],
277
+ ['classroom', 'submissions', 'grade', pos('c1'), pos('w1'), pos('s1'), '--draft=90', '--assigned=95'],
274
278
  { account: undefined },
275
279
  );
276
280
  });
@@ -285,7 +289,7 @@ describe('gog_classroom_submissions_grade', () => {
285
289
  describe('gog_classroom_submissions_return', () => {
286
290
  it('calls run with ids', async () => {
287
291
  await harness.callTool('gog_classroom_submissions_return', { courseId: 'c1', courseworkId: 'w1', submissionId: 's1' });
288
- expect(runner.run).toHaveBeenCalledWith(['classroom', 'submissions', 'return', 'c1', 'w1', 's1'], { account: undefined });
292
+ expect(runner.run).toHaveBeenCalledWith(['classroom', 'submissions', 'return', pos('c1'), pos('w1'), pos('s1')], { account: undefined });
289
293
  });
290
294
 
291
295
  it('returns error text on failure', async () => {
@@ -298,7 +302,7 @@ describe('gog_classroom_submissions_return', () => {
298
302
  describe('gog_classroom_submissions_turn_in', () => {
299
303
  it('calls run with ids', async () => {
300
304
  await harness.callTool('gog_classroom_submissions_turn_in', { courseId: 'c1', courseworkId: 'w1', submissionId: 's1' });
301
- expect(runner.run).toHaveBeenCalledWith(['classroom', 'submissions', 'turn-in', 'c1', 'w1', 's1'], { account: undefined });
305
+ expect(runner.run).toHaveBeenCalledWith(['classroom', 'submissions', 'turn-in', pos('c1'), pos('w1'), pos('s1')], { account: undefined });
302
306
  });
303
307
 
304
308
  it('returns error text on failure', async () => {
@@ -311,7 +315,7 @@ describe('gog_classroom_submissions_turn_in', () => {
311
315
  describe('gog_classroom_submissions_reclaim', () => {
312
316
  it('calls run with ids', async () => {
313
317
  await harness.callTool('gog_classroom_submissions_reclaim', { courseId: 'c1', courseworkId: 'w1', submissionId: 's1' });
314
- expect(runner.run).toHaveBeenCalledWith(['classroom', 'submissions', 'reclaim', 'c1', 'w1', 's1'], { account: undefined });
318
+ expect(runner.run).toHaveBeenCalledWith(['classroom', 'submissions', 'reclaim', pos('c1'), pos('w1'), pos('s1')], { account: undefined });
315
319
  });
316
320
 
317
321
  it('returns error text on failure', async () => {
@@ -324,20 +328,20 @@ describe('gog_classroom_submissions_reclaim', () => {
324
328
  describe('gog_classroom_announcements_list', () => {
325
329
  it('calls run with courseId only', async () => {
326
330
  await harness.callTool('gog_classroom_announcements_list', { courseId: 'c1' });
327
- expect(runner.run).toHaveBeenCalledWith(['classroom', 'announcements', 'list', 'c1'], { account: undefined });
331
+ expect(runner.run).toHaveBeenCalledWith(['classroom', 'announcements', 'list', pos('c1')], { account: undefined });
328
332
  });
329
333
 
330
334
  it('passes all listing flags', async () => {
331
335
  await harness.callTool('gog_classroom_announcements_list', { courseId: 'c1', state: 'PUBLISHED', orderBy: 'updateTime desc', max: 20, page: 'tok', all: true });
332
336
  expect(runner.run).toHaveBeenCalledWith(
333
- ['classroom', 'announcements', 'list', 'c1', '--state=PUBLISHED', '--order-by=updateTime desc', '--max=20', '--page=tok', '--all'],
337
+ ['classroom', 'announcements', 'list', pos('c1'), '--state=PUBLISHED', '--order-by=updateTime desc', '--max=20', '--page=tok', '--all'],
334
338
  { account: undefined },
335
339
  );
336
340
  });
337
341
 
338
342
  it('omits --all when false', async () => {
339
343
  await harness.callTool('gog_classroom_announcements_list', { courseId: 'c1', all: false });
340
- expect(runner.run).toHaveBeenCalledWith(['classroom', 'announcements', 'list', 'c1'], { account: undefined });
344
+ expect(runner.run).toHaveBeenCalledWith(['classroom', 'announcements', 'list', pos('c1')], { account: undefined });
341
345
  });
342
346
 
343
347
  it('returns error text on failure', async () => {
@@ -350,7 +354,7 @@ describe('gog_classroom_announcements_list', () => {
350
354
  describe('gog_classroom_announcements_get', () => {
351
355
  it('calls run with ids', async () => {
352
356
  await harness.callTool('gog_classroom_announcements_get', { courseId: 'c1', announcementId: 'a1' });
353
- expect(runner.run).toHaveBeenCalledWith(['classroom', 'announcements', 'get', 'c1', 'a1'], { account: undefined });
357
+ expect(runner.run).toHaveBeenCalledWith(['classroom', 'announcements', 'get', pos('c1'), pos('a1')], { account: undefined });
354
358
  });
355
359
 
356
360
  it('returns error text on failure', async () => {
@@ -364,7 +368,7 @@ describe('gog_classroom_announcements_create', () => {
364
368
  it('calls run with required text only', async () => {
365
369
  await harness.callTool('gog_classroom_announcements_create', { courseId: 'c1', text: 'Hi' });
366
370
  expect(runner.run).toHaveBeenCalledWith(
367
- ['classroom', 'announcements', 'create', 'c1', '--text=Hi'],
371
+ ['classroom', 'announcements', 'create', pos('c1'), '--text=Hi'],
368
372
  { account: undefined },
369
373
  );
370
374
  });
@@ -372,7 +376,7 @@ describe('gog_classroom_announcements_create', () => {
372
376
  it('passes all optional flags', async () => {
373
377
  await harness.callTool('gog_classroom_announcements_create', { courseId: 'c1', text: 'Hi', state: 'DRAFT', scheduled: '2026-05-01T12:00' });
374
378
  expect(runner.run).toHaveBeenCalledWith(
375
- ['classroom', 'announcements', 'create', 'c1', '--text=Hi', '--state=DRAFT', '--scheduled=2026-05-01T12:00'],
379
+ ['classroom', 'announcements', 'create', pos('c1'), '--text=Hi', '--state=DRAFT', '--scheduled=2026-05-01T12:00'],
376
380
  { account: undefined },
377
381
  );
378
382
  });
@@ -387,20 +391,20 @@ describe('gog_classroom_announcements_create', () => {
387
391
  describe('gog_classroom_topics_list', () => {
388
392
  it('calls run with courseId only', async () => {
389
393
  await harness.callTool('gog_classroom_topics_list', { courseId: 'c1' });
390
- expect(runner.run).toHaveBeenCalledWith(['classroom', 'topics', 'list', 'c1'], { account: undefined });
394
+ expect(runner.run).toHaveBeenCalledWith(['classroom', 'topics', 'list', pos('c1')], { account: undefined });
391
395
  });
392
396
 
393
397
  it('passes all listing flags', async () => {
394
398
  await harness.callTool('gog_classroom_topics_list', { courseId: 'c1', max: 20, page: 'tok', all: true });
395
399
  expect(runner.run).toHaveBeenCalledWith(
396
- ['classroom', 'topics', 'list', 'c1', '--max=20', '--page=tok', '--all'],
400
+ ['classroom', 'topics', 'list', pos('c1'), '--max=20', '--page=tok', '--all'],
397
401
  { account: undefined },
398
402
  );
399
403
  });
400
404
 
401
405
  it('omits --all when false', async () => {
402
406
  await harness.callTool('gog_classroom_topics_list', { courseId: 'c1', all: false });
403
- expect(runner.run).toHaveBeenCalledWith(['classroom', 'topics', 'list', 'c1'], { account: undefined });
407
+ expect(runner.run).toHaveBeenCalledWith(['classroom', 'topics', 'list', pos('c1')], { account: undefined });
404
408
  });
405
409
 
406
410
  it('returns error text on failure', async () => {
@@ -413,7 +417,7 @@ describe('gog_classroom_topics_list', () => {
413
417
  describe('gog_classroom_topics_get', () => {
414
418
  it('calls run with ids', async () => {
415
419
  await harness.callTool('gog_classroom_topics_get', { courseId: 'c1', topicId: 't1' });
416
- expect(runner.run).toHaveBeenCalledWith(['classroom', 'topics', 'get', 'c1', 't1'], { account: undefined });
420
+ expect(runner.run).toHaveBeenCalledWith(['classroom', 'topics', 'get', pos('c1'), pos('t1')], { account: undefined });
417
421
  });
418
422
 
419
423
  it('returns error text on failure', async () => {
@@ -452,7 +456,7 @@ describe('gog_classroom_invitations_list', () => {
452
456
  describe('gog_classroom_invitations_get', () => {
453
457
  it('calls run with invitationId', async () => {
454
458
  await harness.callTool('gog_classroom_invitations_get', { invitationId: 'i1' });
455
- expect(runner.run).toHaveBeenCalledWith(['classroom', 'invitations', 'get', 'i1'], { account: undefined });
459
+ expect(runner.run).toHaveBeenCalledWith(['classroom', 'invitations', 'get', pos('i1')], { account: undefined });
456
460
  });
457
461
 
458
462
  it('returns error text on failure', async () => {
@@ -465,7 +469,7 @@ describe('gog_classroom_invitations_get', () => {
465
469
  describe('gog_classroom_invitations_accept', () => {
466
470
  it('calls run with invitationId', async () => {
467
471
  await harness.callTool('gog_classroom_invitations_accept', { invitationId: 'i1' });
468
- expect(runner.run).toHaveBeenCalledWith(['classroom', 'invitations', 'accept', 'i1'], { account: undefined });
472
+ expect(runner.run).toHaveBeenCalledWith(['classroom', 'invitations', 'accept', pos('i1')], { account: undefined });
469
473
  });
470
474
 
471
475
  it('returns error text on failure', async () => {
@@ -483,7 +487,7 @@ describe('gog_classroom_profile_get', () => {
483
487
 
484
488
  it('passes userId when provided', async () => {
485
489
  await harness.callTool('gog_classroom_profile_get', { userId: 'u1' });
486
- expect(runner.run).toHaveBeenCalledWith(['classroom', 'profile', 'get', 'u1'], { account: undefined });
490
+ expect(runner.run).toHaveBeenCalledWith(['classroom', 'profile', 'get', pos('u1')], { account: undefined });
487
491
  });
488
492
 
489
493
  it('returns error text on failure', async () => {
@@ -2,6 +2,7 @@ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
2
  import { registerContactsTools } from '../../src/tools/contacts.js';
3
3
  import * as runner from '../../src/runner.js';
4
4
  import { createTestHarness } from '@chrischall/mcp-utils/test';
5
+ import { pos } from '../../src/argv.js';
5
6
 
6
7
  vi.mock('../../src/runner.js');
7
8
 
@@ -14,7 +15,7 @@ describe('gog_contacts_search', () => {
14
15
  vi.mocked(runner.run).mockResolvedValue('{"connections":[]}');
15
16
  const harness = await setupHandlers();
16
17
  await harness.callTool('gog_contacts_search', { query: 'alice' });
17
- expect(runner.run).toHaveBeenCalledWith(['contacts', 'search', 'alice'], { account: undefined });
18
+ expect(runner.run).toHaveBeenCalledWith(['contacts', 'search', pos('alice')], { account: undefined });
18
19
  });
19
20
 
20
21
  it('returns error text on failure', async () => {
@@ -46,7 +47,7 @@ describe('gog_contacts_get', () => {
46
47
  vi.mocked(runner.run).mockResolvedValue('{"resourceName":"people/c123"}');
47
48
  const harness = await setupHandlers();
48
49
  await harness.callTool('gog_contacts_get', { resourceName: 'people/c123' });
49
- expect(runner.run).toHaveBeenCalledWith(['contacts', 'get', 'people/c123'], { account: undefined });
50
+ expect(runner.run).toHaveBeenCalledWith(['contacts', 'get', pos('people/c123')], { account: undefined });
50
51
  });
51
52
 
52
53
  it('returns error text on failure', async () => {