File size: 3,781 Bytes
7dc28be
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
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'}`);
      }
    },
  });
}