Spaces:
Sleeping
Sleeping
| import type { FastMCP } from 'fastmcp'; | |
| import { UserError } from 'fastmcp'; | |
| import { z } from 'zod'; | |
| import { drive_v3, docs_v1 } from 'googleapis'; | |
| import { getDriveClient, getDocsClient } from '../../clients.js'; | |
| export function register(server: FastMCP) { | |
| server.addTool({ | |
| name: 'createDocumentFromTemplate', | |
| description: | |
| 'Creates a new document by copying an existing template and optionally replacing placeholder text. Provide key-value pairs in the replacements parameter to substitute template variables.', | |
| parameters: z.strictObject({ | |
| templateId: z.string().describe('ID of the template document to copy from.'), | |
| newTitle: z.string().min(1).describe('Title for the new document.'), | |
| parentFolderId: z | |
| .string() | |
| .optional() | |
| .describe( | |
| 'ID of folder where document should be created. If not provided, creates in Drive root.' | |
| ), | |
| replacements: z | |
| .record(z.string()) | |
| .optional() | |
| .describe( | |
| 'Key-value pairs for text replacements in the template (e.g., {"{{NAME}}": "John Doe", "{{DATE}}": "2024-01-01"}).' | |
| ), | |
| }), | |
| execute: async (args, { log }) => { | |
| const drive = await getDriveClient(); | |
| log.info(`Creating document from template ${args.templateId} with title "${args.newTitle}"`); | |
| try { | |
| // First copy the template | |
| const copyMetadata: drive_v3.Schema$File = { | |
| name: args.newTitle, | |
| }; | |
| if (args.parentFolderId) { | |
| copyMetadata.parents = [args.parentFolderId]; | |
| } | |
| const response = await drive.files.copy({ | |
| fileId: args.templateId, | |
| requestBody: copyMetadata, | |
| fields: 'id,name,webViewLink', | |
| supportsAllDrives: true, | |
| }); | |
| const document = response.data; | |
| let result = `Successfully created document "${document.name}" from template (ID: ${document.id})\nView Link: ${document.webViewLink}`; | |
| // Apply text replacements if provided | |
| if (args.replacements && Object.keys(args.replacements).length > 0) { | |
| try { | |
| const docs = await getDocsClient(); | |
| const requests: docs_v1.Schema$Request[] = []; | |
| // Create replace requests for each replacement | |
| for (const [searchText, replaceText] of Object.entries(args.replacements)) { | |
| requests.push({ | |
| replaceAllText: { | |
| containsText: { | |
| text: searchText, | |
| matchCase: false, | |
| }, | |
| replaceText: replaceText, | |
| }, | |
| }); | |
| } | |
| if (requests.length > 0) { | |
| await docs.documents.batchUpdate({ | |
| documentId: document.id!, | |
| requestBody: { requests }, | |
| }); | |
| const replacementCount = Object.keys(args.replacements).length; | |
| result += `\n\nApplied ${replacementCount} text replacement${replacementCount !== 1 ? 's' : ''} to the document.`; | |
| } | |
| } catch (replacementError: any) { | |
| log.warn( | |
| `Document created but failed to apply replacements: ${replacementError.message}` | |
| ); | |
| result += `\n\nDocument created but failed to apply text replacements. You can make changes manually.`; | |
| } | |
| } | |
| return result; | |
| } catch (error: any) { | |
| log.error(`Error creating document from template: ${error.message || error}`); | |
| if (error.code === 404) | |
| throw new UserError('Template document or parent folder not found. Check the IDs.'); | |
| if (error.code === 403) | |
| throw new UserError( | |
| 'Permission denied. Make sure you have read access to the template and write access to the destination folder.' | |
| ); | |
| throw new UserError( | |
| `Failed to create document from template: ${error.message || 'Unknown error'}` | |
| ); | |
| } | |
| }, | |
| }); | |
| } | |