arcade-google-sheets 4.2.1__py3-none-any.whl → 5.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,29 @@
1
+ import sys
2
+ from typing import cast
3
+
4
+ from arcade_mcp_server import MCPApp
5
+ from arcade_mcp_server.mcp_app import TransportType
6
+
7
+ import arcade_google_sheets
8
+
9
+ app = MCPApp(
10
+ name="GoogleSheets",
11
+ instructions=(
12
+ "Use this server when you need to interact with Google Sheets to enable users to "
13
+ "efficiently manage and access their spreadsheets."
14
+ ),
15
+ )
16
+
17
+ app.add_tools_from_module(arcade_google_sheets)
18
+
19
+
20
+ def main() -> None:
21
+ transport = sys.argv[1] if len(sys.argv) > 1 else "stdio"
22
+ host = sys.argv[2] if len(sys.argv) > 2 else "127.0.0.1"
23
+ port = int(sys.argv[3]) if len(sys.argv) > 3 else 8000
24
+
25
+ app.run(transport=cast(TransportType, transport), host=host, port=port)
26
+
27
+
28
+ if __name__ == "__main__":
29
+ main()
@@ -30,14 +30,14 @@ class CellExtendedValue(BaseModel):
30
30
  errorValue: Optional["CellErrorValue"] = None
31
31
 
32
32
  @model_validator(mode="after")
33
- def check_exactly_one_value(cls, instance): # type: ignore[no-untyped-def]
34
- provided = [v for v in instance.__dict__.values() if v is not None]
33
+ def check_exactly_one_value(self): # type: ignore[no-untyped-def]
34
+ provided = [v for v in self.__dict__.values() if v is not None]
35
35
  if len(provided) != 1:
36
36
  raise ValueError(
37
37
  "Exactly one of numberValue, stringValue, boolValue, "
38
38
  "formulaValue, or errorValue must be set."
39
39
  )
40
- return instance
40
+ return self
41
41
 
42
42
 
43
43
  class NumberFormat(BaseModel):
@@ -2,9 +2,10 @@ import base64
2
2
  import json
3
3
  from typing import Annotated
4
4
 
5
- from arcade_tdk import ToolContext, ToolMetadataKey, tool
6
- from arcade_tdk.auth import Google
7
- from arcade_tdk.errors import ToolExecutionError
5
+ from arcade_core.schema import ToolMetadataKey
6
+ from arcade_mcp_server import Context, tool
7
+ from arcade_mcp_server.auth import Google
8
+ from arcade_mcp_server.exceptions import ToolExecutionError
8
9
 
9
10
 
10
11
  @tool(
@@ -12,7 +13,7 @@ from arcade_tdk.errors import ToolExecutionError
12
13
  requires_metadata=[ToolMetadataKey.CLIENT_ID, ToolMetadataKey.COORDINATOR_URL],
13
14
  )
14
15
  def generate_google_file_picker_url(
15
- context: ToolContext,
16
+ context: Context,
16
17
  ) -> Annotated[dict, "Google File Picker URL for user file selection and permission granting"]:
17
18
  """Generate a Google File Picker URL for user-driven file selection and authorization.
18
19
 
@@ -1,7 +1,7 @@
1
1
  from typing import Annotated
2
2
 
3
- from arcade_tdk import ToolContext, tool
4
- from arcade_tdk.auth import Google
3
+ from arcade_mcp_server import Context, tool
4
+ from arcade_mcp_server.auth import Google
5
5
 
6
6
  from arcade_google_sheets.templates import sheet_url_template
7
7
  from arcade_google_sheets.utils import (
@@ -19,7 +19,7 @@ from arcade_google_sheets.utils import (
19
19
  ),
20
20
  )
21
21
  async def get_spreadsheet(
22
- context: ToolContext,
22
+ context: Context,
23
23
  spreadsheet_id: Annotated[str, "The id of the spreadsheet to get"],
24
24
  sheet_position: Annotated[
25
25
  int | None,
@@ -89,7 +89,7 @@ async def get_spreadsheet(
89
89
  ),
90
90
  )
91
91
  async def get_spreadsheet_metadata(
92
- context: ToolContext,
92
+ context: Context,
93
93
  spreadsheet_id: Annotated[str, "The id of the spreadsheet to get metadata for"],
94
94
  ) -> Annotated[dict, "The spreadsheet metadata for the specified spreadsheet"]:
95
95
  """Gets the metadata for a spreadsheet including the metadata for the sheets in the spreadsheet.
@@ -104,7 +104,7 @@ async def get_spreadsheet_metadata(
104
104
 
105
105
  metadata = get_spreadsheet_metadata_helper(service, spreadsheet_id)
106
106
  metadata_dict = metadata.model_dump(exclude_none=True)
107
- for sheet in metadata_dict.get("sheets", []):
107
+ for sheet in metadata_dict.get("sheets") or []:
108
108
  sheet["sheet_url"] = sheet_url_template.format(
109
109
  spreadsheet_id=spreadsheet_id,
110
110
  sheet_id=sheet["properties"]["sheetId"],
@@ -114,5 +114,5 @@ async def get_spreadsheet_metadata(
114
114
  "spreadsheet_title": metadata_dict["properties"]["title"],
115
115
  "spreadsheet_id": metadata_dict["spreadsheetId"],
116
116
  "spreadsheet_url": metadata_dict["spreadsheetUrl"],
117
- "sheets": metadata_dict["sheets"],
117
+ "sheets": metadata_dict.get("sheets") or [],
118
118
  }
@@ -1,7 +1,7 @@
1
1
  from typing import Annotated, Any
2
2
 
3
- from arcade_tdk import ToolContext, tool
4
- from arcade_tdk.auth import Google
3
+ from arcade_mcp_server import Context, tool
4
+ from arcade_mcp_server.auth import Google
5
5
 
6
6
  from arcade_google_sheets.enums import OrderBy
7
7
  from arcade_google_sheets.templates import (
@@ -20,7 +20,7 @@ from arcade_google_sheets.utils import (
20
20
  ),
21
21
  )
22
22
  async def search_spreadsheets(
23
- context: ToolContext,
23
+ context: Context,
24
24
  spreadsheet_contains: Annotated[
25
25
  list[str] | None,
26
26
  "Keywords or phrases that must be in the spreadsheet title. Provide a list of "
@@ -107,7 +107,9 @@ async def search_spreadsheets(
107
107
  params.pop("pageToken", None)
108
108
 
109
109
  results = drive_service.files().list(**params).execute()
110
- batch = results.get("files", [])
110
+ batch = results.get("files") or []
111
+ if not isinstance(batch, list):
112
+ batch = []
111
113
  spreadsheets.extend(batch[: limit - len(spreadsheets)])
112
114
 
113
115
  pagination_token = results.get("nextPageToken")
@@ -1,7 +1,7 @@
1
1
  from typing import Annotated, Any
2
2
 
3
- from arcade_tdk import ToolContext, tool
4
- from arcade_tdk.auth import Google
3
+ from arcade_mcp_server import Context, tool
4
+ from arcade_mcp_server.auth import Google
5
5
 
6
6
  from arcade_google_sheets.utils import build_sheets_service
7
7
  from arcade_google_sheets.who_am_i_util import build_who_am_i_response
@@ -17,7 +17,7 @@ from arcade_google_sheets.who_am_i_util import build_who_am_i_response
17
17
  )
18
18
  )
19
19
  async def who_am_i(
20
- context: ToolContext,
20
+ context: Context,
21
21
  ) -> Annotated[
22
22
  dict[str, Any],
23
23
  "Get comprehensive user profile and Google Sheets environment information.",
@@ -1,7 +1,7 @@
1
1
  from typing import Annotated
2
2
 
3
- from arcade_tdk import ToolContext, tool
4
- from arcade_tdk.auth import Google
3
+ from arcade_mcp_server import Context, tool
4
+ from arcade_mcp_server.auth import Google
5
5
 
6
6
  from arcade_google_sheets.converters import SheetDataInputToValueRangesConverter
7
7
  from arcade_google_sheets.models import (
@@ -26,7 +26,7 @@ from arcade_google_sheets.utils import (
26
26
  )
27
27
  )
28
28
  def create_spreadsheet(
29
- context: ToolContext,
29
+ context: Context,
30
30
  title: Annotated[str, "The title of the new spreadsheet"] = "Untitled spreadsheet",
31
31
  data: Annotated[
32
32
  str | None,
@@ -71,7 +71,7 @@ def create_spreadsheet(
71
71
  )
72
72
  )
73
73
  def write_to_cell(
74
- context: ToolContext,
74
+ context: Context,
75
75
  spreadsheet_id: Annotated[str, "The id of the spreadsheet to write to"],
76
76
  column: Annotated[str, "The column string to write to. For example, 'A', 'F', or 'AZ'"],
77
77
  row: Annotated[int, "The row number to write to"],
@@ -115,7 +115,7 @@ def write_to_cell(
115
115
  )
116
116
  )
117
117
  def update_cells(
118
- context: ToolContext,
118
+ context: Context,
119
119
  spreadsheet_id: Annotated[str, "The id of the spreadsheet to write to"],
120
120
  data: Annotated[
121
121
  str,
@@ -163,7 +163,7 @@ def update_cells(
163
163
  )
164
164
  )
165
165
  def add_note_to_cell(
166
- context: ToolContext,
166
+ context: Context,
167
167
  spreadsheet_id: Annotated[str, "The id of the spreadsheet to add a comment to"],
168
168
  column: Annotated[str, "The column string to add a note to. For example, 'A', 'F', or 'AZ'"],
169
169
  row: Annotated[int, "The row number to add a note to"],
@@ -2,7 +2,7 @@ import logging
2
2
  import string
3
3
  from typing import Any
4
4
 
5
- from arcade_tdk.errors import RetryableToolError, ToolExecutionError
5
+ from arcade_mcp_server.exceptions import RetryableToolError, ToolExecutionError
6
6
  from google.oauth2.credentials import Credentials
7
7
  from googleapiclient.discovery import Resource, build
8
8
 
@@ -380,8 +380,8 @@ def parse_get_spreadsheet_response(api_response: dict) -> dict:
380
380
  """
381
381
  Parse the get spreadsheet Google Sheets API response into a structured dictionary.
382
382
  """
383
- properties = api_response.get("properties", {})
384
- sheets = [parse_sheet(sheet) for sheet in api_response.get("sheets", [])]
383
+ properties = api_response.get("properties") or {}
384
+ sheets = [parse_sheet(sheet) for sheet in (api_response.get("sheets") or [])]
385
385
 
386
386
  return {
387
387
  "title": properties.get("title", ""),
@@ -396,9 +396,9 @@ def parse_sheet(api_sheet: dict) -> dict:
396
396
  Parse an individual sheet's data from the Google Sheets 'get spreadsheet'
397
397
  API response into a structured dictionary.
398
398
  """
399
- props = api_sheet.get("properties", {})
400
- grid_props = props.get("gridProperties", {})
401
- cell_data = convert_api_grid_data_to_dict(api_sheet.get("data", []))
399
+ props = api_sheet.get("properties") or {}
400
+ grid_props = props.get("gridProperties") or {}
401
+ cell_data = convert_api_grid_data_to_dict(api_sheet.get("data") or [])
402
402
 
403
403
  return {
404
404
  "sheetId": props.get("sheetId"),
@@ -419,7 +419,9 @@ def extract_user_entered_cell_value(cell: dict) -> Any:
419
419
  Returns:
420
420
  The extracted value if present, otherwise None.
421
421
  """
422
- user_val = cell.get("userEnteredValue", {})
422
+ if not isinstance(cell, dict):
423
+ return ""
424
+ user_val = cell.get("userEnteredValue") or {}
423
425
  for key in ["stringValue", "numberValue", "boolValue", "formulaValue"]:
424
426
  if key in user_val:
425
427
  return user_val[key]
@@ -440,11 +442,12 @@ def process_row(row: dict, start_column_index: int) -> dict:
440
442
  dict: A mapping of column letters to cell values for non-empty cells.
441
443
  """
442
444
  row_result = {}
443
- for j, cell in enumerate(row.get("values", [])):
445
+ for j, cell in enumerate(row.get("values") or []):
446
+ cell_dict = cell if isinstance(cell, dict) else {}
444
447
  column_index = start_column_index + j
445
448
  column_string = index_to_col(column_index)
446
- user_entered_cell_value = extract_user_entered_cell_value(cell)
447
- formatted_cell_value = cell.get("formattedValue", "")
449
+ user_entered_cell_value = extract_user_entered_cell_value(cell_dict)
450
+ formatted_cell_value = cell_dict.get("formattedValue", "")
448
451
 
449
452
  if user_entered_cell_value != "" or formatted_cell_value != "":
450
453
  row_result[column_string] = {
@@ -470,14 +473,19 @@ def convert_api_grid_data_to_dict(grids: list[dict]) -> dict:
470
473
  dict: A dictionary mapping row numbers to dictionaries of column letter/value pairs.
471
474
  Only includes non-empty rows and non-empty cells.
472
475
  """
476
+ if not grids:
477
+ return {}
473
478
  result = {}
474
479
  for grid in grids:
480
+ if not isinstance(grid, dict):
481
+ continue
475
482
  start_row = grid.get("startRow", 0)
476
483
  start_column = grid.get("startColumn", 0)
477
484
 
478
- for i, row in enumerate(grid.get("rowData", []), start=1):
485
+ for i, row in enumerate(grid.get("rowData") or [], start=1):
479
486
  current_row = start_row + i
480
- row_data = process_row(row, start_column)
487
+ row_dict = row if isinstance(row, dict) else {}
488
+ row_data = process_row(row_dict, start_column)
481
489
 
482
490
  if row_data:
483
491
  result[current_row] = row_data
@@ -719,7 +727,7 @@ def batch_update(service: Resource, spreadsheet_id: str, data: list[ValueRange])
719
727
  .execute()
720
728
  )
721
729
  updated_ranges = [
722
- value_response["updatedRange"] for value_response in response.get("responses", [])
730
+ value_response["updatedRange"] for value_response in (response.get("responses") or [])
723
731
  ]
724
732
  return {
725
733
  "spreadsheet_id": response["spreadsheetId"],
@@ -1,20 +1,19 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arcade_google_sheets
3
- Version: 4.2.1
4
- Summary: Arcade.dev LLM tools for Google Sheets
3
+ Version: 5.0.0
4
+ Summary: Arcade.dev LLM tools for Google Sheets.
5
5
  Author-email: Arcade <dev@arcade.dev>
6
6
  License: Proprietary - Arcade Software License Agreement v1.0
7
7
  License-File: LICENSE
8
8
  Requires-Python: >=3.10
9
- Requires-Dist: arcade-tdk<3.0.0,>=2.3.1
9
+ Requires-Dist: arcade-mcp-server<2.0.0,>=1.9.2
10
10
  Requires-Dist: google-api-core<3.0.0,>=2.19.1
11
11
  Requires-Dist: google-api-python-client<3.0.0,>=2.137.0
12
12
  Requires-Dist: google-auth-httplib2<1.0.0,>=0.2.0
13
13
  Requires-Dist: google-auth<3.0.0,>=2.32.0
14
14
  Requires-Dist: googleapis-common-protos<2.0.0,>=1.63.2
15
15
  Provides-Extra: dev
16
- Requires-Dist: arcade-ai[evals]<3.0.0,>=2.2.1; extra == 'dev'
17
- Requires-Dist: arcade-serve<3.0.0,>=2.1.0; extra == 'dev'
16
+ Requires-Dist: arcade-mcp[evals]<2.0.0,>=1.5.6; extra == 'dev'
18
17
  Requires-Dist: mypy<1.6.0,>=1.5.1; extra == 'dev'
19
18
  Requires-Dist: pre-commit<3.5.0,>=3.4.0; extra == 'dev'
20
19
  Requires-Dist: pytest-asyncio<0.25.0,>=0.24.0; extra == 'dev'
@@ -0,0 +1,21 @@
1
+ arcade_google_sheets/__init__.py,sha256=asCX32sPK-bi2HRpWZSlLd7dRKy7Uztqxwu-eoXTY1g,466
2
+ arcade_google_sheets/__main__.py,sha256=IZerVlNUgDiSWMpq0_dB-ivmohL1CsRat2GIg9PD2Zg,743
3
+ arcade_google_sheets/constants.py,sha256=4tQOrQ1YagSklJSSw5Eq21XCcFCJdO7lso5SqWIdrPI,63
4
+ arcade_google_sheets/converters.py,sha256=1muE_28Jk0eyFUzoVo_lN0_qy3z-uv7f5XoZLH9mitQ,3924
5
+ arcade_google_sheets/enums.py,sha256=30OMPu2l_aUjz8kQzC1mR5w0lLbs0fZn8T6PDtoXpL4,4608
6
+ arcade_google_sheets/models.py,sha256=dy-a4-72Sa7u8LUghrzZYBv4m7w3hWotUE8ezxRUN-Y,8610
7
+ arcade_google_sheets/templates.py,sha256=hSvjiErERLPe6RqWY6QNmivOy7Ofdg_zABcLWCI-ByU,189
8
+ arcade_google_sheets/types.py,sha256=R-rCRcyFqDZx3jgl_kWeCliqC8fHuZ8ub_LQ2KoU2AE,37
9
+ arcade_google_sheets/utils.py,sha256=5rWHJkHM3XLca5k9vxkx3Ns0zrt6XK8pbxHkfXj9-zY,35211
10
+ arcade_google_sheets/who_am_i_util.py,sha256=MfDKsAOMUkH0DZNjMzcmDiuvxMEUJMT9o_a7uixCJF4,2734
11
+ arcade_google_sheets/tools/__init__.py,sha256=GBe1Op-o6QzCy9nA3qS53Ij2Uex5IXdFPUnGQo_TPkg,673
12
+ arcade_google_sheets/tools/file_picker.py,sha256=DQaNsJYCho4uk_JeMz1lkPEoJy0UG0ujbAJ4lzkxUE4,2389
13
+ arcade_google_sheets/tools/read.py,sha256=Fghd2sF5aOJaOqENHl25cTRRdMOj927W84nFzCBOgIw,4017
14
+ arcade_google_sheets/tools/search.py,sha256=tbudaTSb3JPar0wyYQaLcipD4o19vxpnpyixq4m0rpY,4912
15
+ arcade_google_sheets/tools/system_context.py,sha256=iE19p5SFXa3McnrgXjt1eTzHrVLSrDf3gzj00zBVMLc,1182
16
+ arcade_google_sheets/tools/write.py,sha256=vdfWegznaozsQd5JNNtqzFCvZSlAozh5kmwbeku9stw,7776
17
+ arcade_google_sheets-5.0.0.dist-info/METADATA,sha256=1ooqlOgVwGIEVeA5C1gumtID2Q9TzXO_2Bq6vUq9TVE,1074
18
+ arcade_google_sheets-5.0.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
19
+ arcade_google_sheets-5.0.0.dist-info/entry_points.txt,sha256=FbDSAKpaNbY3OvFkCdMJQsybxYs6pOxAyljHUoNt9c0,134
20
+ arcade_google_sheets-5.0.0.dist-info/licenses/LICENSE,sha256=ixeE7aL9b2B-_ZYHTY1vQcJB4NufKeo-LWwKNObGDN0,1960
21
+ arcade_google_sheets-5.0.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.27.0
2
+ Generator: hatchling 1.28.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ arcade-google-sheets = arcade_google_sheets.__main__:main
3
+ arcade_google_sheets = arcade_google_sheets.__main__:main
@@ -1,19 +0,0 @@
1
- arcade_google_sheets/__init__.py,sha256=asCX32sPK-bi2HRpWZSlLd7dRKy7Uztqxwu-eoXTY1g,466
2
- arcade_google_sheets/constants.py,sha256=4tQOrQ1YagSklJSSw5Eq21XCcFCJdO7lso5SqWIdrPI,63
3
- arcade_google_sheets/converters.py,sha256=1muE_28Jk0eyFUzoVo_lN0_qy3z-uv7f5XoZLH9mitQ,3924
4
- arcade_google_sheets/enums.py,sha256=30OMPu2l_aUjz8kQzC1mR5w0lLbs0fZn8T6PDtoXpL4,4608
5
- arcade_google_sheets/models.py,sha256=xwPdwUis0OHTDbSLy85qWl7zIh2lDTi5HZMNKwdZjzo,8627
6
- arcade_google_sheets/templates.py,sha256=hSvjiErERLPe6RqWY6QNmivOy7Ofdg_zABcLWCI-ByU,189
7
- arcade_google_sheets/types.py,sha256=R-rCRcyFqDZx3jgl_kWeCliqC8fHuZ8ub_LQ2KoU2AE,37
8
- arcade_google_sheets/utils.py,sha256=VmDZOzAOEtfSPOra-ieVl_U16RLonQUOnZ4RW4Gf-oA,34895
9
- arcade_google_sheets/who_am_i_util.py,sha256=MfDKsAOMUkH0DZNjMzcmDiuvxMEUJMT9o_a7uixCJF4,2734
10
- arcade_google_sheets/tools/__init__.py,sha256=GBe1Op-o6QzCy9nA3qS53Ij2Uex5IXdFPUnGQo_TPkg,673
11
- arcade_google_sheets/tools/file_picker.py,sha256=Dqn-hfMoTsWyHM8QCakVgHr5TKrzL_1Lj-vYHVGtOW4,2342
12
- arcade_google_sheets/tools/read.py,sha256=qQX_TdcbPbkuaHPuhx7CzWsWJ_a97FSCDJqOlGHF_TM,4003
13
- arcade_google_sheets/tools/search.py,sha256=olCaUwDqW26yaYqMT5lPmQDFL6M9g7qON7JG3mcgYJM,4841
14
- arcade_google_sheets/tools/system_context.py,sha256=rVpYeyFsL8mj0Lt6VWOhnfGaSy3ezOHL74EFJHjE9Ww,1176
15
- arcade_google_sheets/tools/write.py,sha256=4kNx941PQt6VUGTogbepnbfUdcsVze6u5c8QvlNnWCI,7782
16
- arcade_google_sheets-4.2.1.dist-info/METADATA,sha256=xM1z7j9tHzv6ncsWfQ2fU0dKY7BkNZ_lBgufskoz68w,1123
17
- arcade_google_sheets-4.2.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
18
- arcade_google_sheets-4.2.1.dist-info/licenses/LICENSE,sha256=ixeE7aL9b2B-_ZYHTY1vQcJB4NufKeo-LWwKNObGDN0,1960
19
- arcade_google_sheets-4.2.1.dist-info/RECORD,,