Spaces:
Sleeping
Sleeping
| import type { FastMCP } from 'fastmcp'; | |
| import { UserError } from 'fastmcp'; | |
| import { z } from 'zod'; | |
| import { google } from 'googleapis'; | |
| import { getDocsClient, getAuthClient } from '../../../clients.js'; | |
| import { DocumentIdParameter } from '../../../types.js'; | |
| export function register(server: FastMCP) { | |
| server.addTool({ | |
| name: 'addComment', | |
| description: | |
| 'Adds a comment to the document at the specified text range. Use listComments to retrieve the comment ID after creation. Note: programmatically created comments appear in the comments panel but may not show as anchored highlights in the document UI.', | |
| parameters: DocumentIdParameter.extend({ | |
| startIndex: z | |
| .number() | |
| .int() | |
| .min(1) | |
| .describe('The starting index of the text range (inclusive, starts from 1).'), | |
| endIndex: z.number().int().min(1).describe('The ending index of the text range (exclusive).'), | |
| content: z.string().min(1).describe('The text content of the comment.'), | |
| }).refine((data) => data.endIndex > data.startIndex, { | |
| message: 'endIndex must be greater than startIndex', | |
| path: ['endIndex'], | |
| }), | |
| execute: async (args, { log }) => { | |
| log.info( | |
| `Adding comment to range ${args.startIndex}-${args.endIndex} in doc ${args.documentId}` | |
| ); | |
| try { | |
| // First, get the text content that will be quoted | |
| const docsClient = await getDocsClient(); | |
| const doc = await docsClient.documents.get({ documentId: args.documentId }); | |
| // Extract the quoted text from the document | |
| let quotedText = ''; | |
| const content = doc.data.body?.content || []; | |
| for (const element of content) { | |
| if (element.paragraph) { | |
| const elements = element.paragraph.elements || []; | |
| for (const textElement of elements) { | |
| if (textElement.textRun) { | |
| const elementStart = textElement.startIndex || 0; | |
| const elementEnd = textElement.endIndex || 0; | |
| // Check if this element overlaps with our range | |
| if (elementEnd > args.startIndex && elementStart < args.endIndex) { | |
| const text = textElement.textRun.content || ''; | |
| const startOffset = Math.max(0, args.startIndex - elementStart); | |
| const endOffset = Math.min(text.length, args.endIndex - elementStart); | |
| quotedText += text.substring(startOffset, endOffset); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| // Use Drive API v3 for comments | |
| const authClient = await getAuthClient(); | |
| const drive = google.drive({ version: 'v3', auth: authClient }); | |
| const response = await drive.comments.create({ | |
| fileId: args.documentId, | |
| fields: 'id,content,quotedFileContent,author,createdTime,resolved', | |
| requestBody: { | |
| content: args.content, | |
| quotedFileContent: { | |
| value: quotedText, | |
| mimeType: 'text/html', | |
| }, | |
| anchor: JSON.stringify({ | |
| r: args.documentId, | |
| a: [ | |
| { | |
| txt: { | |
| o: args.startIndex - 1, // Drive API uses 0-based indexing | |
| l: args.endIndex - args.startIndex, | |
| ml: args.endIndex - args.startIndex, | |
| }, | |
| }, | |
| ], | |
| }), | |
| }, | |
| }); | |
| return `Comment added successfully. Comment ID: ${response.data.id}`; | |
| } catch (error: any) { | |
| log.error(`Error adding comment: ${error.message || error}`); | |
| throw new UserError(`Failed to add comment: ${error.message || 'Unknown error'}`); | |
| } | |
| }, | |
| }); | |
| } | |