Skip to content

Commit a11cd41

Browse files
feat: remove slides.find and sheets.find in favor of drive.search (#271)
* feat: remove slides.find and sheets.find in favor of drive.search Closes #268 Remove the redundant find methods from SlidesService and SheetsService, consolidating all file-finding through drive.search. This also drops the Drive API dependency (drive_v3, getDriveClient, buildDriveSearchQuery, MIME_TYPES) from both services entirely. Changes: - Remove slides.find and sheets.find tool registrations from index.ts - Remove find methods and Drive API deps from SlidesService and SheetsService - Remove associated tests and Drive API mocks - Update WORKSPACE-Context.md with MIME type filter examples for all types - Fix stale docs.find/docs.move references in Docs skill - Create new Sheets and Slides skills with drive.search guidance * refactor: move Sheets/Slides details from WORKSPACE-Context to skills Remove the Docs/Sheets/Slides section (format selection, content handling) since these are now covered by individual skills. Replace inline Sheets nuances with a skill cross-reference, add Slides cross-reference. * refactor: remove unused buildDriveSearchQuery and MIME_TYPES Address PR review feedback: - Remove buildDriveSearchQuery and MIME_TYPES from DriveQueryBuilder.ts (no longer used after removing slides.find and sheets.find) - Keep only escapeQueryString (still used by DriveService) - Rewrite DriveQueryBuilder tests to cover escapeQueryString only - Add fullText contains note to Docs skill for consistency * style: run prettier formatting
1 parent 320b13c commit a11cd41

11 files changed

Lines changed: 159 additions & 465 deletions

File tree

skills/docs/SKILL.md

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -216,12 +216,21 @@ All write tools (`writeText`, `replaceText`, `formatText`) accept an optional
216216

217217
### Finding Documents
218218

219-
Use `docs.find` to search by title. Supports pagination with `pageToken`.
219+
Use `drive.search` with a document MIME type filter to find Google Docs:
220+
221+
```
222+
drive.search({
223+
query: "mimeType='application/vnd.google-apps.document' and name contains 'Weekly Report'"
224+
})
225+
```
226+
227+
For full-text search across document content, use `fullText contains` instead of
228+
`name contains`.
220229

221230
### Moving Documents
222231

223-
Use `docs.move` to move a document to a named folder. If multiple folders share
224-
the same name, the first match is used.
232+
Use `drive.moveFile` to move a document to a different folder. You can specify
233+
the destination by folder ID or folder name.
225234

226235
## Comments & Suggestions
227236

skills/sheets/SKILL.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
---
2+
name: sheets
3+
description: >
4+
Activate this skill when the user wants to find, read, or analyze Google
5+
Sheets spreadsheets. Contains guidance on searching for spreadsheets, output
6+
formats, and range-based operations.
7+
---
8+
9+
# Google Sheets Expert
10+
11+
You are an expert at working with Google Sheets spreadsheets through the
12+
Workspace Extension tools. Follow these guidelines when helping users with
13+
spreadsheet tasks.
14+
15+
## Finding Spreadsheets
16+
17+
Use `drive.search` with a Sheets MIME type filter to find spreadsheets:
18+
19+
```
20+
drive.search({
21+
query: "mimeType='application/vnd.google-apps.spreadsheet' and name contains 'Budget'"
22+
})
23+
```
24+
25+
For full-text search across spreadsheet content, use `fullText contains` instead
26+
of `name contains`.
27+
28+
## Reading Data
29+
30+
### Full Spreadsheet
31+
32+
Use `sheets.getText` to read all sheets in a spreadsheet. Choose the output
33+
format based on the use case:
34+
35+
- **text** (default): Human-readable with pipe-separated columns — good for
36+
quick review
37+
- **csv**: Standard CSV format — good for data export and analysis
38+
- **json**: Structured JSON keyed by sheet name — good for programmatic
39+
processing
40+
41+
### Specific Range
42+
43+
Use `sheets.getRange` with A1 notation to read a specific cell range:
44+
45+
```
46+
sheets.getRange({
47+
spreadsheetId: "spreadsheet-id",
48+
range: "Sheet1!A1:D10"
49+
})
50+
```
51+
52+
### Metadata
53+
54+
Use `sheets.getMetadata` to get spreadsheet structure without reading data —
55+
includes sheet names, row/column counts, locale, and timezone.
56+
57+
## ID Handling
58+
59+
- All tools accept Google Drive URLs directly — no manual ID extraction needed
60+
- IDs and URLs are interchangeable in all `spreadsheetId` parameters

skills/slides/SKILL.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
---
2+
name: slides
3+
description: >
4+
Activate this skill when the user wants to find, read, or extract content from
5+
Google Slides presentations. Contains guidance on searching for presentations,
6+
reading text, downloading images, and getting thumbnails.
7+
---
8+
9+
# Google Slides Expert
10+
11+
You are an expert at working with Google Slides presentations through the
12+
Workspace Extension tools. Follow these guidelines when helping users with
13+
presentation tasks.
14+
15+
## Finding Presentations
16+
17+
Use `drive.search` with a Slides MIME type filter to find presentations:
18+
19+
```
20+
drive.search({
21+
query: "mimeType='application/vnd.google-apps.presentation' and name contains 'Quarterly Review'"
22+
})
23+
```
24+
25+
For full-text search across presentation content, use `fullText contains`
26+
instead of `name contains`.
27+
28+
## Reading Content
29+
30+
### Text Extraction
31+
32+
Use `slides.getText` to extract all text content from a presentation. Text is
33+
organized by slide with clear separators.
34+
35+
### Metadata
36+
37+
Use `slides.getMetadata` to get presentation structure — includes slide count,
38+
object IDs, page size, and layout information. Slide object IDs from metadata
39+
can be used with `slides.getSlideThumbnail`.
40+
41+
## Downloading Images
42+
43+
### All Images
44+
45+
Use `slides.getImages` to download all embedded images from a presentation to a
46+
local directory. Requires an **absolute path** for the output directory.
47+
48+
### Slide Thumbnails
49+
50+
Use `slides.getSlideThumbnail` to download a thumbnail of a specific slide.
51+
Requires the slide's `objectId` (from `slides.getMetadata` or `slides.getText`)
52+
and an **absolute path** for the output file.
53+
54+
## ID Handling
55+
56+
- All tools accept Google Drive URLs directly — no manual ID extraction needed
57+
- IDs and URLs are interchangeable in all `presentationId` parameters

workspace-server/WORKSPACE-Context.md

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -82,24 +82,15 @@ When creating documents in specific folders:
8282
2. Move it to the target folder with `drive.moveFile`
8383
3. Confirm successful completion
8484

85-
To find Google Docs, use `drive.search` with a document MIME type filter rather
86-
than searching by name alone.
85+
To find Google Docs, Sheets, or Slides, use `drive.search` with a MIME type
86+
filter rather than searching by name alone. Example MIME type queries:
8787

88-
## 📄 Docs, Sheets, and Slides
89-
90-
### Format Selection (Sheets)
91-
92-
Choose output format based on use case:
93-
94-
- **text**: Human-readable, good for quick review
95-
- **csv**: Data export, analysis in other tools
96-
- **json**: Programmatic processing, structured data
97-
98-
### Content Handling
99-
100-
- Docs/Sheets/Slides tools accept URLs directly - no ID extraction needed
101-
- Use markdown for initial document creation when appropriate
102-
- Preserve formatting when reading/modifying content
88+
- Docs:
89+
`mimeType='application/vnd.google-apps.document' and name contains 'query'`
90+
- Sheets:
91+
`mimeType='application/vnd.google-apps.spreadsheet' and name contains 'query'`
92+
- Slides:
93+
`mimeType='application/vnd.google-apps.presentation' and name contains 'query'`
10394

10495
## 🚫 Common Pitfalls to Avoid
10596

@@ -186,9 +177,13 @@ Choose output format based on use case:
186177

187178
### Google Sheets
188179

189-
- Multiple output formats available
190-
- Range-based operations with A1 notation
191-
- Metadata includes sheet structure information
180+
- See the **Sheets skill** for detailed guidance on finding spreadsheets, output
181+
format selection, and range-based operations.
182+
183+
### Google Slides
184+
185+
- See the **Slides skill** for detailed guidance on finding presentations, text
186+
extraction, image downloads, and slide thumbnails.
192187

193188
### Google Calendar
194189

workspace-server/src/__tests__/services/SheetsService.test.ts

Lines changed: 0 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ describe('SheetsService', () => {
2424
let sheetsService: SheetsService;
2525
let mockAuthManager: jest.Mocked<AuthManager>;
2626
let mockSheetsAPI: any;
27-
let mockDriveAPI: any;
2827

2928
beforeEach(() => {
3029
// Clear all mocks before each test
@@ -45,15 +44,8 @@ describe('SheetsService', () => {
4544
},
4645
};
4746

48-
mockDriveAPI = {
49-
files: {
50-
list: jest.fn(),
51-
},
52-
};
53-
5447
// Mock the google constructors
5548
(google.sheets as jest.Mock) = jest.fn().mockReturnValue(mockSheetsAPI);
56-
(google.drive as jest.Mock) = jest.fn().mockReturnValue(mockDriveAPI);
5749

5850
// Create SheetsService instance
5951
sheetsService = new SheetsService(mockAuthManager);
@@ -306,60 +298,6 @@ describe('SheetsService', () => {
306298
});
307299
});
308300

309-
describe('find', () => {
310-
it('should find spreadsheets by query', async () => {
311-
const mockResponse = {
312-
data: {
313-
files: [
314-
{ id: 'sheet1', name: 'Spreadsheet 1' },
315-
{ id: 'sheet2', name: 'Spreadsheet 2' },
316-
],
317-
nextPageToken: 'next-token',
318-
},
319-
};
320-
321-
mockDriveAPI.files.list.mockResolvedValue(mockResponse);
322-
323-
const result = await sheetsService.find({ query: 'budget' });
324-
const response = JSON.parse(result.content[0].text);
325-
326-
expect(mockDriveAPI.files.list).toHaveBeenCalledWith({
327-
pageSize: 10,
328-
fields: 'nextPageToken, files(id, name)',
329-
q: "mimeType='application/vnd.google-apps.spreadsheet' and fullText contains 'budget'",
330-
pageToken: undefined,
331-
supportsAllDrives: true,
332-
includeItemsFromAllDrives: true,
333-
});
334-
335-
expect(response.files).toHaveLength(2);
336-
expect(response.files[0].name).toBe('Spreadsheet 1');
337-
expect(response.nextPageToken).toBe('next-token');
338-
});
339-
340-
it('should handle title-specific searches', async () => {
341-
const mockResponse = {
342-
data: {
343-
files: [{ id: 'sheet1', name: 'Q4 Budget' }],
344-
},
345-
};
346-
347-
mockDriveAPI.files.list.mockResolvedValue(mockResponse);
348-
349-
const result = await sheetsService.find({ query: 'title:"Q4 Budget"' });
350-
const response = JSON.parse(result.content[0].text);
351-
352-
expect(mockDriveAPI.files.list).toHaveBeenCalledWith(
353-
expect.objectContaining({
354-
q: "mimeType='application/vnd.google-apps.spreadsheet' and name contains 'Q4 Budget'",
355-
}),
356-
);
357-
358-
expect(response.files).toHaveLength(1);
359-
expect(response.files[0].name).toBe('Q4 Budget');
360-
});
361-
});
362-
363301
describe('getMetadata', () => {
364302
it('should retrieve spreadsheet metadata', async () => {
365303
const mockSpreadsheet = {

workspace-server/src/__tests__/services/SlidesService.test.ts

Lines changed: 0 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ describe('SlidesService', () => {
4444
let slidesService: SlidesService;
4545
let mockAuthManager: jest.Mocked<AuthManager>;
4646
let mockSlidesAPI: any;
47-
let mockDriveAPI: any;
4847

4948
beforeEach(() => {
5049
// Clear all mocks before each test
@@ -62,15 +61,8 @@ describe('SlidesService', () => {
6261
},
6362
};
6463

65-
mockDriveAPI = {
66-
files: {
67-
list: jest.fn(),
68-
},
69-
};
70-
7164
// Mock the google constructors
7265
(google.slides as jest.Mock) = jest.fn().mockReturnValue(mockSlidesAPI);
73-
(google.drive as jest.Mock) = jest.fn().mockReturnValue(mockDriveAPI);
7466

7567
// Create SlidesService instance
7668
slidesService = new SlidesService(mockAuthManager);
@@ -195,62 +187,6 @@ describe('SlidesService', () => {
195187
});
196188
});
197189

198-
describe('find', () => {
199-
it('should find presentations by query', async () => {
200-
const mockResponse = {
201-
data: {
202-
files: [
203-
{ id: 'pres1', name: 'Presentation 1' },
204-
{ id: 'pres2', name: 'Presentation 2' },
205-
],
206-
nextPageToken: 'next-token',
207-
},
208-
};
209-
210-
mockDriveAPI.files.list.mockResolvedValue(mockResponse);
211-
212-
const result = await slidesService.find({ query: 'test query' });
213-
const response = JSON.parse(result.content[0].text);
214-
215-
expect(mockDriveAPI.files.list).toHaveBeenCalledWith({
216-
pageSize: 10,
217-
fields: 'nextPageToken, files(id, name)',
218-
q: "mimeType='application/vnd.google-apps.presentation' and fullText contains 'test query'",
219-
pageToken: undefined,
220-
supportsAllDrives: true,
221-
includeItemsFromAllDrives: true,
222-
});
223-
224-
expect(response.files).toHaveLength(2);
225-
expect(response.files[0].name).toBe('Presentation 1');
226-
expect(response.nextPageToken).toBe('next-token');
227-
});
228-
229-
it('should handle title-specific searches', async () => {
230-
const mockResponse = {
231-
data: {
232-
files: [{ id: 'pres1', name: 'Specific Title' }],
233-
},
234-
};
235-
236-
mockDriveAPI.files.list.mockResolvedValue(mockResponse);
237-
238-
const result = await slidesService.find({
239-
query: 'title:"Specific Title"',
240-
});
241-
const response = JSON.parse(result.content[0].text);
242-
243-
expect(mockDriveAPI.files.list).toHaveBeenCalledWith(
244-
expect.objectContaining({
245-
q: "mimeType='application/vnd.google-apps.presentation' and name contains 'Specific Title'",
246-
}),
247-
);
248-
249-
expect(response.files).toHaveLength(1);
250-
expect(response.files[0].name).toBe('Specific Title');
251-
});
252-
});
253-
254190
describe('getMetadata', () => {
255191
it('should retrieve presentation metadata', async () => {
256192
const mockPresentation = {

0 commit comments

Comments
 (0)