Spaces:
Sleeping
Sleeping
| import type { FastMCP } from 'fastmcp'; | |
| import { UserError } from 'fastmcp'; | |
| import { z } from 'zod'; | |
| import { getSheetsClient } from '../../clients.js'; | |
| import * as SheetsHelpers from '../../googleSheetsApiHelpers.js'; | |
| function colIndexToLetters(index: number): string { | |
| let s = ''; | |
| let i = index; | |
| do { | |
| s = String.fromCharCode(65 + (i % 26)) + s; | |
| i = Math.floor(i / 26) - 1; | |
| } while (i >= 0); | |
| return s; | |
| } | |
| function rgbToHex(rgb: { red?: number; green?: number; blue?: number } | null | undefined): string { | |
| if (!rgb) return '#000000'; | |
| const r = Math.round((rgb.red ?? 0) * 255); | |
| const g = Math.round((rgb.green ?? 0) * 255); | |
| const b = Math.round((rgb.blue ?? 0) * 255); | |
| return `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`.toUpperCase(); | |
| } | |
| export function register(server: FastMCP) { | |
| server.addTool({ | |
| name: 'getConditionalFormatting', | |
| description: | |
| 'Lists all conditional formatting rules for a sheet as JSON. Each rule includes its index (needed for deleteConditionalFormatting), kind (BOOLEAN or GRADIENT), ranges, condition type/values, and applied formats (colors, bold, italic).', | |
| parameters: z.strictObject({ | |
| spreadsheetId: z | |
| .string() | |
| .describe( | |
| 'The spreadsheet ID — the long string between /d/ and /edit in a Google Sheets URL.' | |
| ), | |
| sheetName: z | |
| .string() | |
| .optional() | |
| .describe('Name of the sheet/tab. Defaults to the first sheet if not provided.'), | |
| }), | |
| execute: async (args, { log }) => { | |
| const sheets = await getSheetsClient(); | |
| log.info(`Getting conditional formatting rules for spreadsheet ${args.spreadsheetId}`); | |
| try { | |
| const sheetId = await SheetsHelpers.resolveSheetId( | |
| sheets, | |
| args.spreadsheetId, | |
| args.sheetName | |
| ); | |
| const response = await sheets.spreadsheets.get({ | |
| spreadsheetId: args.spreadsheetId, | |
| fields: 'sheets(properties(sheetId,title),conditionalFormats)', | |
| }); | |
| const sheet = response.data.sheets?.find((s) => s.properties?.sheetId === sheetId); | |
| const rules = sheet?.conditionalFormats ?? []; | |
| const sheetTitle = sheet?.properties?.title ?? null; | |
| const ruleSummaries = rules.map((rule, idx) => { | |
| const condition = rule.booleanRule?.condition; | |
| const gradient = rule.gradientRule; | |
| const fmt = rule.booleanRule?.format ?? {}; | |
| const ranges = (rule.ranges ?? []).map((r) => { | |
| const startCol = | |
| r.startColumnIndex != null ? colIndexToLetters(r.startColumnIndex) : ''; | |
| const endCol = r.endColumnIndex != null ? colIndexToLetters(r.endColumnIndex - 1) : ''; | |
| const startRow = r.startRowIndex != null ? r.startRowIndex + 1 : ''; | |
| const endRow = r.endRowIndex != null ? r.endRowIndex : ''; | |
| return `${startCol}${startRow}:${endCol}${endRow}`; | |
| }); | |
| const kind = gradient ? 'GRADIENT' : 'BOOLEAN'; | |
| const conditionType = condition?.type ?? (gradient ? 'GRADIENT' : null); | |
| const conditionValues = (condition?.values ?? []) | |
| .map((v) => v.userEnteredValue) | |
| .filter((v): v is string => typeof v === 'string'); | |
| const bg = fmt.backgroundColor; | |
| const backgroundColor = bg | |
| ? rgbToHex({ red: bg.red ?? 0, green: bg.green ?? 0, blue: bg.blue ?? 0 }) | |
| : null; | |
| const fg = fmt.textFormat?.foregroundColor; | |
| const textColor = fg | |
| ? rgbToHex({ red: fg.red ?? 0, green: fg.green ?? 0, blue: fg.blue ?? 0 }) | |
| : null; | |
| return { | |
| index: idx, | |
| kind, | |
| ranges, | |
| conditionType, | |
| conditionValues, | |
| backgroundColor, | |
| textColor, | |
| bold: fmt.textFormat?.bold ?? false, | |
| italic: fmt.textFormat?.italic ?? false, | |
| }; | |
| }); | |
| return JSON.stringify( | |
| { | |
| spreadsheetId: args.spreadsheetId, | |
| sheetName: sheetTitle, | |
| count: ruleSummaries.length, | |
| rules: ruleSummaries, | |
| }, | |
| null, | |
| 2 | |
| ); | |
| } catch (error: any) { | |
| log.error(`Error getting conditional formatting: ${error.message || error}`); | |
| if (error instanceof UserError) throw error; | |
| throw new UserError( | |
| `Failed to get conditional formatting: ${error.message || 'Unknown error'}` | |
| ); | |
| } | |
| }, | |
| }); | |
| } | |