From e94d4e433cbe9a807f8736f125e8e8d08f5d656d Mon Sep 17 00:00:00 2001 From: oskarcode Date: Thu, 12 Feb 2026 11:54:06 -0500 Subject: [PATCH 1/3] feat: add drive.copy tool for copying files/templates --- workspace-server/src/index.ts | 23 +++++++++ workspace-server/src/services/DriveService.ts | 49 +++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/workspace-server/src/index.ts b/workspace-server/src/index.ts index 674d678..f4066cb 100644 --- a/workspace-server/src/index.ts +++ b/workspace-server/src/index.ts @@ -532,6 +532,29 @@ async function main() { driveService.downloadFile, ); + server.registerTool( + 'drive.copy', + { + description: + 'Copies a file in Google Drive. Useful for creating documents from templates. Returns the new file ID and URL.', + inputSchema: { + fileId: z + .string() + .describe( + 'The ID of the file to copy. Can be extracted from a Google Drive URL.', + ), + name: z.string().describe('The name for the new copied file.'), + folderId: z + .string() + .optional() + .describe( + 'Optional folder ID to place the copy in. If not specified, the copy is placed in the same location as the original.', + ), + }, + }, + driveService.copyFile, + ); + server.registerTool( 'drive.moveFile', { diff --git a/workspace-server/src/services/DriveService.ts b/workspace-server/src/services/DriveService.ts index 8561b61..5527123 100644 --- a/workspace-server/src/services/DriveService.ts +++ b/workspace-server/src/services/DriveService.ts @@ -531,6 +531,55 @@ export class DriveService { } }; + public copyFile = async ({ + fileId, + name, + folderId, + }: { + fileId: string; + name: string; + folderId?: string; + }) => { + logToFile( + `Copying Drive file ${fileId} with new name: ${name}${folderId ? ` to folder: ${folderId}` : ''}`, + ); + try { + const drive = await this.getDriveClient(); + + const requestBody: drive_v3.Schema$File = { name }; + if (folderId) { + requestBody.parents = [folderId]; + } + + const copy = await drive.files.copy({ + fileId: fileId, + requestBody: requestBody, + fields: 'id, name, mimeType, webViewLink', + supportsAllDrives: true, + }); + + logToFile( + `Successfully copied file: ${copy.data.name} (${copy.data.id})`, + ); + + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify({ + id: copy.data.id, + name: copy.data.name, + mimeType: copy.data.mimeType, + webViewLink: copy.data.webViewLink, + }), + }, + ], + }; + } catch (error) { + return this.handleError('drive.copyFile', error); + } + }; + public downloadFile = async ({ fileId, localPath, From 012f1bb8a4257dd4b075f514e8cbbbce8a08d084 Mon Sep 17 00:00:00 2001 From: oskarcode Date: Fri, 13 Mar 2026 16:50:40 -0400 Subject: [PATCH 2/3] fix: use extractDocumentId for fileId in copyFile for URL support --- workspace-server/src/services/DriveService.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/workspace-server/src/services/DriveService.ts b/workspace-server/src/services/DriveService.ts index 5527123..e735919 100644 --- a/workspace-server/src/services/DriveService.ts +++ b/workspace-server/src/services/DriveService.ts @@ -59,6 +59,7 @@ export class DriveService { logToFile(`Searching for folder with name: ${folderName}`); try { const drive = await this.getDriveClient(); + const id = extractDocumentId(fileId); const query = `mimeType='application/vnd.google-apps.folder' and name = '${escapeQueryString(folderName)}'`; logToFile(`Executing Drive API query: ${query}`); const res = await drive.files.list({ @@ -98,6 +99,7 @@ export class DriveService { ); try { const drive = await this.getDriveClient(); + const id = extractDocumentId(fileId); const fileMetadata: drive_v3.Schema$File = { name: name, mimeType: 'application/vnd.google-apps.folder', @@ -157,6 +159,7 @@ export class DriveService { sharedWithMe?: boolean; }) => { const drive = await this.getDriveClient(); + const id = extractDocumentId(fileId); let q = query; let isProcessed = false; @@ -209,7 +212,7 @@ export class DriveService { logToFile(`Extracted File ID from URL: ${fileId}, using files.get`); try { const res = await drive.files.get({ - fileId: fileId, + fileId: id, fields: 'id, name, modifiedTime, viewedByMeTime, mimeType, parents', supportsAllDrives: true, @@ -361,6 +364,7 @@ export class DriveService { try { const drive = await this.getDriveClient(); const id = extractDocumentId(fileId); + const id = extractDocumentId(fileId); const file = await drive.files.update({ fileId: id, @@ -398,6 +402,7 @@ export class DriveService { try { const drive = await this.getDriveClient(); const id = extractDocumentId(fileId); + const id = extractDocumentId(fileId); const file = await drive.files.update({ fileId: id, @@ -428,6 +433,7 @@ export class DriveService { try { const drive = await this.getDriveClient(); const id = extractDocumentId(fileId); + const id = extractDocumentId(fileId); const res = await drive.comments.list({ fileId: id, fields: @@ -467,6 +473,7 @@ export class DriveService { try { const drive = await this.getDriveClient(); const id = extractDocumentId(fileId); + const id = extractDocumentId(fileId); let targetFolderId = folderId; @@ -545,6 +552,7 @@ export class DriveService { ); try { const drive = await this.getDriveClient(); + const id = extractDocumentId(fileId); const requestBody: drive_v3.Schema$File = { name }; if (folderId) { @@ -552,7 +560,7 @@ export class DriveService { } const copy = await drive.files.copy({ - fileId: fileId, + fileId: id, requestBody: requestBody, fields: 'id, name, mimeType, webViewLink', supportsAllDrives: true, @@ -591,6 +599,7 @@ export class DriveService { try { const drive = await this.getDriveClient(); const id = extractDocumentId(fileId); + const id = extractDocumentId(fileId); // 1. Check if it's a Google Doc (special handling required, export instead of download) const metadata = await drive.files.get({ From 113bdebe381d28df9e89e6dd3738254d0f7070f1 Mon Sep 17 00:00:00 2001 From: oskarcode Date: Fri, 13 Mar 2026 17:30:16 -0400 Subject: [PATCH 3/3] fix: remove erroneous extractDocumentId call in findFolder method --- workspace-server/src/services/DriveService.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/workspace-server/src/services/DriveService.ts b/workspace-server/src/services/DriveService.ts index e735919..1b051b8 100644 --- a/workspace-server/src/services/DriveService.ts +++ b/workspace-server/src/services/DriveService.ts @@ -59,7 +59,6 @@ export class DriveService { logToFile(`Searching for folder with name: ${folderName}`); try { const drive = await this.getDriveClient(); - const id = extractDocumentId(fileId); const query = `mimeType='application/vnd.google-apps.folder' and name = '${escapeQueryString(folderName)}'`; logToFile(`Executing Drive API query: ${query}`); const res = await drive.files.list({