Skip to content

Commit b1369e2

Browse files
authored
feat: Google Sheets MCP to replace legacy tool bundle (#723)
* wip: init commit of google sheet MCP * wip: fix issues, add update_cells * feat: improve tools, add dockerfile * chore: clean up * feat: clear_range tool
1 parent ddab026 commit b1369e2

17 files changed

+1823
-396
lines changed
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
name: Build release google sheets
2+
3+
permissions:
4+
id-token: write
5+
contents: read
6+
packages: write
7+
8+
on:
9+
workflow_dispatch:
10+
push:
11+
paths:
12+
- 'google/sheets/**'
13+
branches:
14+
- main
15+
16+
jobs:
17+
oss-tools-build:
18+
runs-on: depot-ubuntu-22.04
19+
concurrency:
20+
group: google-sheets-build
21+
cancel-in-progress: true
22+
steps:
23+
- uses: actions/checkout@v4
24+
25+
- name: Log in to GitHub Container Registry
26+
uses: docker/login-action@v3
27+
with:
28+
registry: ghcr.io
29+
username: ${{ secrets.GHCR_USERNAME }}
30+
password: ${{ secrets.GHCR_TOKEN }}
31+
32+
- name: Build and Push Docker image
33+
uses: depot/build-push-action@v1
34+
id: build-and-push
35+
with:
36+
project: bbqjs4tj1g
37+
context: ./google/sheets
38+
push: true
39+
pull: true
40+
platforms: linux/amd64,linux/arm64
41+
tags: |
42+
ghcr.io/${{ github.repository }}/google-sheets:latest
43+
44+
- name: Install Cosign
45+
uses: sigstore/[email protected]
46+
with:
47+
cosign-release: 'v2.4.3'
48+
49+
- name: Sign Images
50+
env:
51+
DIGEST: ${{ steps.build-and-push.outputs.digest }}
52+
TAGS: ghcr.io/${{ github.repository }}/google-sheets:latest
53+
run: |
54+
images=""
55+
for tag in ${TAGS}; do
56+
images+="${tag}@${DIGEST} "
57+
done
58+
cosign sign --yes ${images}

google/sheets/.python-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.13

google/sheets/Dockerfile

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
FROM ghcr.io/astral-sh/uv:python3.13-bookworm-slim
2+
3+
# Prevents Python from writing .pyc files and buffers stdout/stderr
4+
ENV PYTHONDONTWRITEBYTECODE=1 \
5+
PYTHONUNBUFFERED=1
6+
7+
WORKDIR /app
8+
9+
# Install system deps (if any needed) - using apt for Debian
10+
RUN apt-get update && apt-get install -y --no-install-recommends \
11+
build-essential \
12+
git \
13+
&& rm -rf /var/lib/apt/lists/*
14+
15+
# Copy uv project files first for better Docker layer caching
16+
COPY pyproject.toml uv.lock ./
17+
18+
# Copy your application code (needed for local package build)
19+
COPY . .
20+
21+
# Install dependencies using uv sync (faster and more reliable)
22+
RUN uv sync --frozen --no-dev --no-editable
23+
24+
EXPOSE 9000
25+
26+
# Start the server using uv run (ensures proper virtual environment)
27+
CMD ["uv", "run", "python", "-m", "app.server"]

google/sheets/app/__init__.py

Whitespace-only changes.
Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,24 @@
1-
import os
21

32
from google.oauth2.credentials import Credentials
43
from googleapiclient.discovery import build
54
from googleapiclient.errors import HttpError
5+
import gspread
6+
from fastmcp.exceptions import ToolError
67

78

8-
def client(service_name: str, version: str):
9-
token = os.getenv('GOOGLE_OAUTH_TOKEN')
10-
if token is None:
11-
raise ValueError("GOOGLE_OAUTH_TOKEN environment variable is not set")
12-
9+
def get_google_client(token: str, service_name: str = "sheets", version: str = "v4"):
1310
creds = Credentials(token=token)
1411
try:
1512
service = build(serviceName=service_name, version=version, credentials=creds)
1613
return service
1714
except HttpError as err:
18-
print(err)
19-
exit(1)
20-
15+
raise ToolError(f"Error getting Google client: {err}")
2116

22-
def gspread_client():
23-
import gspread
24-
token = os.getenv('GOOGLE_OAUTH_TOKEN')
25-
if token is None:
26-
raise ValueError("GOOGLE_OAUTH_TOKEN environment variable is not set")
2717

18+
def get_gspread_client(token: str) -> gspread.Client:
2819
creds = Credentials(token=token)
2920
try:
3021
service = gspread.authorize(creds)
31-
3222
return service
3323
except HttpError as err:
34-
print(err)
35-
exit(1)
24+
raise ToolError(f"Error getting Gspread client: {err}")

0 commit comments

Comments
 (0)