File size: 5,591 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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import type { FastMCP } from 'fastmcp';
import { UserError } from 'fastmcp';
import { getDocsClient } from '../../../clients.js';
import {
  ApplyParagraphStyleToolParameters,
  ApplyParagraphStyleToolArgs,
  NotImplementedError,
} from '../../../types.js';
import * as GDocsHelpers from '../../../googleDocsApiHelpers.js';

export function register(server: FastMCP) {
  server.addTool({
    name: 'applyParagraphStyle',
    description:
      'Applies paragraph-level formatting (alignment, spacing, heading styles) to paragraphs identified by a character range or by searching for text. Use namedStyleType to set heading levels (HEADING_1 through HEADING_6).',
    parameters: ApplyParagraphStyleToolParameters,
    execute: async (args: ApplyParagraphStyleToolArgs, { log }) => {
      const docs = await getDocsClient();
      let startIndex: number | undefined;
      let endIndex: number | undefined;

      log.info(
        `Applying paragraph style to document ${args.documentId}${args.tabId ? ` (tab: ${args.tabId})` : ''}`
      );
      log.info(`Style options: ${JSON.stringify(args.style)}`);
      log.info(`Target specification: ${JSON.stringify(args.target)}`);

      try {
        // STEP 1: Determine the target paragraph's range based on the targeting method
        if ('textToFind' in args.target) {
          // Find the text first
          log.info(
            `Finding text "${args.target.textToFind}" (instance ${args.target.matchInstance || 1})${args.tabId ? ` in tab ${args.tabId}` : ''}`
          );
          const textRange = await GDocsHelpers.findTextRange(
            docs,
            args.documentId,
            args.target.textToFind,
            args.target.matchInstance || 1,
            args.tabId
          );

          if (!textRange) {
            throw new UserError(
              `Could not find "${args.target.textToFind}" in the document${args.tabId ? ` (tab: ${args.tabId})` : ''}.`
            );
          }

          log.info(
            `Found text at range ${textRange.startIndex}-${textRange.endIndex}, now locating containing paragraph`
          );

          // Then find the paragraph containing this text
          const paragraphRange = await GDocsHelpers.getParagraphRange(
            docs,
            args.documentId,
            textRange.startIndex,
            args.tabId
          );

          if (!paragraphRange) {
            throw new UserError(`Found the text but could not determine the paragraph boundaries.`);
          }

          startIndex = paragraphRange.startIndex;
          endIndex = paragraphRange.endIndex;
          log.info(`Text is contained within paragraph at range ${startIndex}-${endIndex}`);
        } else if ('indexWithinParagraph' in args.target) {
          // Find paragraph containing the specified index
          log.info(
            `Finding paragraph containing index ${args.target.indexWithinParagraph}${args.tabId ? ` in tab ${args.tabId}` : ''}`
          );
          const paragraphRange = await GDocsHelpers.getParagraphRange(
            docs,
            args.documentId,
            args.target.indexWithinParagraph,
            args.tabId
          );

          if (!paragraphRange) {
            throw new UserError(
              `Could not find paragraph containing index ${args.target.indexWithinParagraph}${args.tabId ? ` in tab ${args.tabId}` : ''}.`
            );
          }

          startIndex = paragraphRange.startIndex;
          endIndex = paragraphRange.endIndex;
          log.info(`Located paragraph at range ${startIndex}-${endIndex}`);
        } else if ('startIndex' in args.target && 'endIndex' in args.target) {
          // Use directly provided range
          startIndex = args.target.startIndex;
          endIndex = args.target.endIndex;
          log.info(`Using provided paragraph range ${startIndex}-${endIndex}`);
        }

        // Verify that we have a valid range
        if (startIndex === undefined || endIndex === undefined) {
          throw new UserError(
            'Could not determine target paragraph range from the provided information.'
          );
        }

        if (endIndex <= startIndex) {
          throw new UserError(
            `Invalid paragraph range: end index (${endIndex}) must be greater than start index (${startIndex}).`
          );
        }

        // STEP 2: Build and apply the paragraph style request
        log.info(`Building paragraph style request for range ${startIndex}-${endIndex}`);
        const requestInfo = GDocsHelpers.buildUpdateParagraphStyleRequest(
          startIndex,
          endIndex,
          args.style,
          args.tabId
        );

        if (!requestInfo) {
          return 'No valid paragraph styling options were provided.';
        }

        log.info(`Applying styles: ${requestInfo.fields.join(', ')}`);
        await GDocsHelpers.executeBatchUpdate(docs, args.documentId, [requestInfo.request]);

        return `Successfully applied paragraph styles (${requestInfo.fields.join(', ')}) to the paragraph${args.tabId ? ` in tab ${args.tabId}` : ''}.`;
      } catch (error: any) {
        // Detailed error logging
        log.error(`Error applying paragraph style in doc ${args.documentId}:`);
        log.error(error.stack || error.message || error);

        if (error instanceof UserError) throw error;
        if (error instanceof NotImplementedError) throw error;

        // Provide a more helpful error message
        throw new UserError(`Failed to apply paragraph style: ${error.message || 'Unknown error'}`);
      }
    },
  });
}