From 8b5584c1fc48abb72bb0cc13b6526f334d985447 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Su=C3=A1rez=20Fuentes?= Date: Fri, 3 Oct 2025 20:26:40 +0200 Subject: [PATCH 1/2] feat: add format_cells tool for cell formatting in Google Sheets Added a new MCP tool that enables formatting of spreadsheet cells including: - Number formats (currency, percent, date, time, etc.) - Background colors (RGB with 0-1 range) - Text formatting (bold, italic, font size, foreground color) - Horizontal alignment (left, center, right) Updated documentation with: - Tool description and parameters in Available Tools section - New feature highlight in Key Features - Example prompts demonstrating cell formatting usage --- README.md | 15 +++- pyproject.toml | 2 +- src/mcp_google_sheets/server.py | 121 ++++++++++++++++++++++++++++++++ 3 files changed, 136 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d29c912..61fbb83 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,8 @@ You're ready! Start issuing commands via your MCP client. ## ✨ Key Features * **Seamless Integration:** Connects directly to Google Drive & Google Sheets APIs. -* **Comprehensive Tools:** Offers a wide range of operations (CRUD, listing, batching, sharing, formatting, etc.). +* **Comprehensive Tools:** Offers a wide range of operations (CRUD, listing, batching, sharing, cell formatting, etc.). +* **Cell Formatting:** Apply number formats, colors, text styles, and alignment to spreadsheet cells. * **Flexible Authentication:** Supports **Service Accounts (recommended)**, OAuth 2.0, and direct credential injection via environment variables. * **Easy Deployment:** Run instantly with `uvx` (zero-install feel) or clone for development using `uv`. * **AI-Ready:** Designed for use with MCP-compatible clients, enabling natural language spreadsheet interaction. @@ -148,6 +149,15 @@ This server exposes the following tools for interacting with Google Sheets: * `recipients` (array of objects): `[{email_address: 'user@example.com', role: 'writer'}, ...]`. Roles: `reader`, `commenter`, `writer`. * `send_notification` (optional boolean, default True): Send email notifications. * _Returns:_ Dictionary with `successes` and `failures` lists. +* **`format_cells`**: Apply formatting to cells in a Google Spreadsheet. + * `spreadsheet_id` (string) + * `sheet` (string): Name of the sheet. + * `range` (string): Cell range in A1 notation (e.g., `'A1:C10'` or `'E17'`). + * `number_format` (optional object): Number format with `type` and `pattern` keys. Example: `{'type': 'CURRENCY', 'pattern': '$#,##0.00'}`. Common types: `NUMBER`, `CURRENCY`, `PERCENT`, `DATE`, `TIME`, `TEXT`. + * `background_color` (optional object): Background color with `red`, `green`, `blue` keys (0-1 range). Example: `{'red': 1, 'green': 0.647, 'blue': 0}` for orange. + * `text_format` (optional object): Text format with keys like `bold`, `italic`, `fontSize`, `foregroundColor`, etc. Example: `{'bold': True, 'fontSize': 11}`. + * `horizontal_alignment` (optional string): One of: `LEFT`, `CENTER`, `RIGHT`. + * _Returns:_ Format operation result object. * **`add_columns`**: Adds columns to a sheet. *(Verify parameters if implemented)* * **`copy_sheet`**: Duplicates a sheet within a spreadsheet. *(Verify parameters if implemented)* * **`rename_sheet`**: Renames an existing sheet. *(Verify parameters if implemented)* @@ -458,6 +468,9 @@ Once connected, try prompts like: * "Append these rows to the 'Log' sheet in spreadsheet `XYZ`: `[['2024-07-31', 'Task A Completed'], ['2024-08-01', 'Task B Started']]`" * "Get a summary of the spreadsheets 'Sales Data' and 'Inventory Count'." * "Share the 'Team Vacation Schedule' spreadsheet with `team@example.com` as a reader and `manager@example.com` as a writer. Don't send notifications." +* "Format cells A1:E1 in Sheet1 with orange background, bold black text." +* "Apply currency formatting to cells E2:E10 in the 'Sales' sheet." +* "Center align the text in cells B1:D1 and make them bold." --- diff --git a/pyproject.toml b/pyproject.toml index 527b926..7c5a683 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ source = "uv-dynamic-versioning" [tool.uv-dynamic-versioning] pattern = "default" -strict = true +strict = false [project.scripts] mcp-google-sheets = "mcp_google_sheets:main" diff --git a/src/mcp_google_sheets/server.py b/src/mcp_google_sheets/server.py index a3f926a..d0c25a3 100644 --- a/src/mcp_google_sheets/server.py +++ b/src/mcp_google_sheets/server.py @@ -928,6 +928,127 @@ def share_spreadsheet(spreadsheet_id: str, return {"successes": successes, "failures": failures} + +@mcp.tool() +def format_cells(spreadsheet_id: str, + sheet: str, + range: str, + number_format: Optional[Dict[str, str]] = None, + background_color: Optional[Dict[str, float]] = None, + text_format: Optional[Dict[str, Any]] = None, + horizontal_alignment: Optional[str] = None, + ctx: Context = None) -> Dict[str, Any]: + """ + Apply formatting to cells in a Google Spreadsheet. + + Args: + spreadsheet_id: The ID of the spreadsheet (found in the URL) + sheet: The name of the sheet + range: Cell range in A1 notation (e.g., 'A1:C10' or 'E17') + number_format: Optional number format with 'type' and 'pattern' keys. + Example: {'type': 'NUMBER', 'pattern': '$#,##0.00'} for currency + Common types: 'NUMBER', 'CURRENCY', 'PERCENT', 'DATE', 'TIME', 'TEXT' + background_color: Optional background color with 'red', 'green', 'blue' keys (0-1 range). + Example: {'red': 1, 'green': 1, 'blue': 1} for white + text_format: Optional text format with keys like 'bold', 'italic', 'fontSize', etc. + Example: {'bold': True, 'fontSize': 11} + horizontal_alignment: Optional horizontal alignment. One of: 'LEFT', 'CENTER', 'RIGHT' + + Returns: + Result of the format operation + """ + sheets_service = ctx.request_context.lifespan_context.sheets_service + + # Get sheet ID + spreadsheet = sheets_service.spreadsheets().get(spreadsheetId=spreadsheet_id).execute() + sheet_id = None + + for s in spreadsheet['sheets']: + if s['properties']['title'] == sheet: + sheet_id = s['properties']['sheetId'] + break + + if sheet_id is None: + return {"error": f"Sheet '{sheet}' not found"} + + # Parse A1 notation to get row/column indices + # Simple parser for ranges like 'A1', 'A1:B2', 'E17', etc. + import re + match = re.match(r'([A-Z]+)(\d+)(?::([A-Z]+)(\d+))?', range) + if not match: + return {"error": f"Invalid range format: {range}"} + + def col_to_index(col: str) -> int: + """Convert column letter to 0-based index""" + result = 0 + for char in col: + result = result * 26 + (ord(char) - ord('A') + 1) + return result - 1 + + start_col = col_to_index(match.group(1)) + start_row = int(match.group(2)) - 1 + + if match.group(3) and match.group(4): + end_col = col_to_index(match.group(3)) + 1 + end_row = int(match.group(4)) + else: + end_col = start_col + 1 + end_row = start_row + 1 + + # Build the cell format + cell_format = {} + fields = [] + + if number_format: + cell_format['numberFormat'] = number_format + fields.append('userEnteredFormat.numberFormat') + + if background_color: + cell_format['backgroundColor'] = background_color + cell_format['backgroundColorStyle'] = {'rgbColor': background_color} + fields.append('userEnteredFormat.backgroundColor') + fields.append('userEnteredFormat.backgroundColorStyle') + + if text_format: + cell_format['textFormat'] = text_format + fields.append('userEnteredFormat.textFormat') + + if horizontal_alignment: + cell_format['horizontalAlignment'] = horizontal_alignment + fields.append('userEnteredFormat.horizontalAlignment') + + if not fields: + return {"error": "No format options provided"} + + # Prepare the format request + request_body = { + "requests": [ + { + "repeatCell": { + "range": { + "sheetId": sheet_id, + "startRowIndex": start_row, + "endRowIndex": end_row, + "startColumnIndex": start_col, + "endColumnIndex": end_col + }, + "cell": { + "userEnteredFormat": cell_format + }, + "fields": ','.join(fields) + } + } + ] + } + + # Execute the request + result = sheets_service.spreadsheets().batchUpdate( + spreadsheetId=spreadsheet_id, + body=request_body + ).execute() + + return result + def main(): # Run the server mcp.run() From 7f61ed95a5cf3d662c7d37f7d11ef12491994d74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Su=C3=A1rez=20Fuentes?= Date: Fri, 5 Dec 2025 20:52:04 +0100 Subject: [PATCH 2/2] Add information to debug the start --- src/mcp_google_sheets/server.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mcp_google_sheets/server.py b/src/mcp_google_sheets/server.py index d0c25a3..0060e50 100644 --- a/src/mcp_google_sheets/server.py +++ b/src/mcp_google_sheets/server.py @@ -1051,4 +1051,5 @@ def col_to_index(col: str) -> int: def main(): # Run the server + print("Starting Google Sheets MCP server...") mcp.run()