From 2e67ceacacfa8063b2ea31852312bd4890e72672 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 4 Oct 2025 17:58:03 +0000 Subject: [PATCH 1/7] Add CI/CD pipelines for testing and publishing This change introduces two GitHub Actions workflows: - ci.yml: Runs on push and pull requests to the main branch. It installs dependencies, lints, formats, type-checks, and runs tests. - publish.yml: Triggers on new releases. It publishes the package to PyPI and a Docker image to the GitHub Container Registry. Additionally, this change: - Adds `ruff` and `mypy` as development dependencies. - Includes a `Dockerfile` for building the application image. - Modifies the test suite to skip tests requiring a `JULES_API_KEY` if the environment variable is not set, making the tests runnable in a CI environment. --- .github/workflows/ci.yml | 29 ++++++++++ .github/workflows/publish.yml | 61 +++++++++++++++++++++ Dockerfile | 20 +++++++ pyproject.toml | 4 ++ tests/test_jules_mcp/test_jules_mcp.py | 7 +++ uv.lock | 76 +++++++++++++++++++++++++- 6 files changed, 196 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/publish.yml create mode 100644 Dockerfile diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..8d39f49 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,29 @@ +name: CI + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.13' + - name: Install uv + run: pip install uv + - name: Install dependencies + run: uv sync --all-extras + - name: Lint with ruff + run: uv run ruff check . + - name: Format with ruff + run: uv run ruff format --check . + - name: Type check with mypy + run: uv run mypy . + - name: Test with pytest + run: uv run pytest \ No newline at end of file diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..c4499cc --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,61 @@ +name: Publish Package + +on: + release: + types: [created] + +jobs: + pypi-publish: + name: Upload release to PyPI + runs-on: ubuntu-latest + environment: + name: pypi + url: https://pypi.org/p/jules-mcp + permissions: + id-token: write # this is required for trusted publishing + + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.13" + - name: Install dependencies + run: pip install build + - name: Build package + run: python -m build + - name: Publish package + uses: pypa/gh-action-pypi-publish@v1.9.0 + + + docker-publish: + name: Upload release to GitHub Container Registry + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to the Container registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ghcr.io/${{ github.repository }} + + - name: Build and push Docker image + uses: docker/build-push-action@v6 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..6808f27 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +# Use an official Python runtime as a parent image +FROM python:3.13-slim + +# Set the working directory in the container +WORKDIR /app + +# Install uv +RUN pip install uv + +# Copy the dependency files +COPY pyproject.toml uv.lock ./ + +# Install dependencies +RUN uv sync --no-dev + +# Copy the rest of the application code +COPY jules_mcp/ ./jules_mcp + +# Set the entrypoint +ENTRYPOINT ["python", "-m", "jules_mcp"] \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 74bfb1e..e26f669 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,10 +11,14 @@ dependencies = [ [scripts] +[tool.setuptools.packages.find] +where = ["."] [dependency-groups] dev = [ "pytest>=8.4.2", "pytest-asyncio>=1.2.0", "types-requests>=2.32.4.20250913", + "ruff>=0.5.5", + "mypy>=1.11.0", ] diff --git a/tests/test_jules_mcp/test_jules_mcp.py b/tests/test_jules_mcp/test_jules_mcp.py index 5771174..b9fe3ad 100644 --- a/tests/test_jules_mcp/test_jules_mcp.py +++ b/tests/test_jules_mcp/test_jules_mcp.py @@ -12,13 +12,20 @@ # See the License for the specific language governing permissions and # limitations under the License. +import os + import pytest from fastmcp import Client from jules_mcp import mcp +requires_api_key = pytest.mark.skipif( + not os.environ.get("JULES_API_KEY"), reason="JULES_API_KEY is not set" +) + @pytest.mark.asyncio +@requires_api_key async def test_tool_execution(): client: Client async with Client(mcp) as client: diff --git a/uv.lock b/uv.lock index e0de93b..95f29ac 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 3 +revision = 2 requires-python = ">=3.13" [[package]] @@ -531,8 +531,10 @@ dependencies = [ [package.dev-dependencies] dev = [ + { name = "mypy" }, { name = "pytest" }, { name = "pytest-asyncio" }, + { name = "ruff" }, { name = "types-requests" }, ] @@ -545,8 +547,10 @@ requires-dist = [ [package.metadata.requires-dev] dev = [ + { name = "mypy", specifier = ">=1.11.0" }, { name = "pytest", specifier = ">=8.4.2" }, { name = "pytest-asyncio", specifier = ">=1.2.0" }, + { name = "ruff", specifier = ">=0.5.5" }, { name = "types-requests", specifier = ">=2.32.4.20250913" }, ] @@ -725,6 +729,41 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fd/69/b547032297c7e63ba2af494edba695d781af8a0c6e89e4d06cf848b21d80/multidict-6.6.4-py3-none-any.whl", hash = "sha256:27d8f8e125c07cb954e54d75d04905a9bba8a439c1d84aca94949d4d03d8601c", size = 12313, upload-time = "2025-08-11T12:08:46.891Z" }, ] +[[package]] +name = "mypy" +version = "1.18.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mypy-extensions" }, + { name = "pathspec" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/77/8f0d0001ffad290cef2f7f216f96c814866248a0b92a722365ed54648e7e/mypy-1.18.2.tar.gz", hash = "sha256:06a398102a5f203d7477b2923dda3634c36727fa5c237d8f859ef90c42a9924b", size = 3448846, upload-time = "2025-09-19T00:11:10.519Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5f/04/7f462e6fbba87a72bc8097b93f6842499c428a6ff0c81dd46948d175afe8/mypy-1.18.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:07b8b0f580ca6d289e69209ec9d3911b4a26e5abfde32228a288eb79df129fcc", size = 12898728, upload-time = "2025-09-19T00:10:01.33Z" }, + { url = "https://files.pythonhosted.org/packages/99/5b/61ed4efb64f1871b41fd0b82d29a64640f3516078f6c7905b68ab1ad8b13/mypy-1.18.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ed4482847168439651d3feee5833ccedbf6657e964572706a2adb1f7fa4dfe2e", size = 11910758, upload-time = "2025-09-19T00:10:42.607Z" }, + { url = "https://files.pythonhosted.org/packages/3c/46/d297d4b683cc89a6e4108c4250a6a6b717f5fa96e1a30a7944a6da44da35/mypy-1.18.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3ad2afadd1e9fea5cf99a45a822346971ede8685cc581ed9cd4d42eaf940986", size = 12475342, upload-time = "2025-09-19T00:11:00.371Z" }, + { url = "https://files.pythonhosted.org/packages/83/45/4798f4d00df13eae3bfdf726c9244bcb495ab5bd588c0eed93a2f2dd67f3/mypy-1.18.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a431a6f1ef14cf8c144c6b14793a23ec4eae3db28277c358136e79d7d062f62d", size = 13338709, upload-time = "2025-09-19T00:11:03.358Z" }, + { url = "https://files.pythonhosted.org/packages/d7/09/479f7358d9625172521a87a9271ddd2441e1dab16a09708f056e97007207/mypy-1.18.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7ab28cc197f1dd77a67e1c6f35cd1f8e8b73ed2217e4fc005f9e6a504e46e7ba", size = 13529806, upload-time = "2025-09-19T00:10:26.073Z" }, + { url = "https://files.pythonhosted.org/packages/71/cf/ac0f2c7e9d0ea3c75cd99dff7aec1c9df4a1376537cb90e4c882267ee7e9/mypy-1.18.2-cp313-cp313-win_amd64.whl", hash = "sha256:0e2785a84b34a72ba55fb5daf079a1003a34c05b22238da94fcae2bbe46f3544", size = 9833262, upload-time = "2025-09-19T00:10:40.035Z" }, + { url = "https://files.pythonhosted.org/packages/5a/0c/7d5300883da16f0063ae53996358758b2a2df2a09c72a5061fa79a1f5006/mypy-1.18.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:62f0e1e988ad41c2a110edde6c398383a889d95b36b3e60bcf155f5164c4fdce", size = 12893775, upload-time = "2025-09-19T00:10:03.814Z" }, + { url = "https://files.pythonhosted.org/packages/50/df/2cffbf25737bdb236f60c973edf62e3e7b4ee1c25b6878629e88e2cde967/mypy-1.18.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8795a039bab805ff0c1dfdb8cd3344642c2b99b8e439d057aba30850b8d3423d", size = 11936852, upload-time = "2025-09-19T00:10:51.631Z" }, + { url = "https://files.pythonhosted.org/packages/be/50/34059de13dd269227fb4a03be1faee6e2a4b04a2051c82ac0a0b5a773c9a/mypy-1.18.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6ca1e64b24a700ab5ce10133f7ccd956a04715463d30498e64ea8715236f9c9c", size = 12480242, upload-time = "2025-09-19T00:11:07.955Z" }, + { url = "https://files.pythonhosted.org/packages/5b/11/040983fad5132d85914c874a2836252bbc57832065548885b5bb5b0d4359/mypy-1.18.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d924eef3795cc89fecf6bedc6ed32b33ac13e8321344f6ddbf8ee89f706c05cb", size = 13326683, upload-time = "2025-09-19T00:09:55.572Z" }, + { url = "https://files.pythonhosted.org/packages/e9/ba/89b2901dd77414dd7a8c8729985832a5735053be15b744c18e4586e506ef/mypy-1.18.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20c02215a080e3a2be3aa50506c67242df1c151eaba0dcbc1e4e557922a26075", size = 13514749, upload-time = "2025-09-19T00:10:44.827Z" }, + { url = "https://files.pythonhosted.org/packages/25/bc/cc98767cffd6b2928ba680f3e5bc969c4152bf7c2d83f92f5a504b92b0eb/mypy-1.18.2-cp314-cp314-win_amd64.whl", hash = "sha256:749b5f83198f1ca64345603118a6f01a4e99ad4bf9d103ddc5a3200cc4614adf", size = 9982959, upload-time = "2025-09-19T00:10:37.344Z" }, + { url = "https://files.pythonhosted.org/packages/87/e3/be76d87158ebafa0309946c4a73831974d4d6ab4f4ef40c3b53a385a66fd/mypy-1.18.2-py3-none-any.whl", hash = "sha256:22a1748707dd62b58d2ae53562ffc4d7f8bcc727e8ac7cbc69c053ddc874d47e", size = 2352367, upload-time = "2025-09-19T00:10:15.489Z" }, +] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, +] + [[package]] name = "openapi-core" version = "0.19.5" @@ -813,6 +852,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7d/eb/b6260b31b1a96386c0a880edebe26f89669098acea8e0318bff6adb378fd/pathable-0.4.4-py3-none-any.whl", hash = "sha256:5ae9e94793b6ef5a4cbe0a7ce9dbbefc1eec38df253763fd0aeeacf2762dbbc2", size = 9592, upload-time = "2025-01-10T18:43:11.88Z" }, ] +[[package]] +name = "pathspec" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, +] + [[package]] name = "pluggy" version = "1.6.0" @@ -1179,6 +1227,32 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/32/7d/97119da51cb1dd3f2f3c0805f155a3aa4a95fa44fe7d78ae15e69edf4f34/rpds_py-0.27.1-cp314-cp314t-win_amd64.whl", hash = "sha256:6567d2bb951e21232c2f660c24cf3470bb96de56cdcb3f071a83feeaff8a2772", size = 230097, upload-time = "2025-08-27T12:15:03.961Z" }, ] +[[package]] +name = "ruff" +version = "0.13.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/8e/f9f9ca747fea8e3ac954e3690d4698c9737c23b51731d02df999c150b1c9/ruff-0.13.3.tar.gz", hash = "sha256:5b0ba0db740eefdfbcce4299f49e9eaefc643d4d007749d77d047c2bab19908e", size = 5438533, upload-time = "2025-10-02T19:29:31.582Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/33/8f7163553481466a92656d35dea9331095122bb84cf98210bef597dd2ecd/ruff-0.13.3-py3-none-linux_armv6l.whl", hash = "sha256:311860a4c5e19189c89d035638f500c1e191d283d0cc2f1600c8c80d6dcd430c", size = 12484040, upload-time = "2025-10-02T19:28:49.199Z" }, + { url = "https://files.pythonhosted.org/packages/b0/b5/4a21a4922e5dd6845e91896b0d9ef493574cbe061ef7d00a73c61db531af/ruff-0.13.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:2bdad6512fb666b40fcadb65e33add2b040fc18a24997d2e47fee7d66f7fcae2", size = 13122975, upload-time = "2025-10-02T19:28:52.446Z" }, + { url = "https://files.pythonhosted.org/packages/40/90/15649af836d88c9f154e5be87e64ae7d2b1baa5a3ef317cb0c8fafcd882d/ruff-0.13.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fc6fa4637284708d6ed4e5e970d52fc3b76a557d7b4e85a53013d9d201d93286", size = 12346621, upload-time = "2025-10-02T19:28:54.712Z" }, + { url = "https://files.pythonhosted.org/packages/a5/42/bcbccb8141305f9a6d3f72549dd82d1134299177cc7eaf832599700f95a7/ruff-0.13.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c9e6469864f94a98f412f20ea143d547e4c652f45e44f369d7b74ee78185838", size = 12574408, upload-time = "2025-10-02T19:28:56.679Z" }, + { url = "https://files.pythonhosted.org/packages/ce/19/0f3681c941cdcfa2d110ce4515624c07a964dc315d3100d889fcad3bfc9e/ruff-0.13.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5bf62b705f319476c78891e0e97e965b21db468b3c999086de8ffb0d40fd2822", size = 12285330, upload-time = "2025-10-02T19:28:58.79Z" }, + { url = "https://files.pythonhosted.org/packages/10/f8/387976bf00d126b907bbd7725219257feea58650e6b055b29b224d8cb731/ruff-0.13.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:78cc1abed87ce40cb07ee0667ce99dbc766c9f519eabfd948ed87295d8737c60", size = 13980815, upload-time = "2025-10-02T19:29:01.577Z" }, + { url = "https://files.pythonhosted.org/packages/0c/a6/7c8ec09d62d5a406e2b17d159e4817b63c945a8b9188a771193b7e1cc0b5/ruff-0.13.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:4fb75e7c402d504f7a9a259e0442b96403fa4a7310ffe3588d11d7e170d2b1e3", size = 14987733, upload-time = "2025-10-02T19:29:04.036Z" }, + { url = "https://files.pythonhosted.org/packages/97/e5/f403a60a12258e0fd0c2195341cfa170726f254c788673495d86ab5a9a9d/ruff-0.13.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:17b951f9d9afb39330b2bdd2dd144ce1c1335881c277837ac1b50bfd99985ed3", size = 14439848, upload-time = "2025-10-02T19:29:06.684Z" }, + { url = "https://files.pythonhosted.org/packages/39/49/3de381343e89364c2334c9f3268b0349dc734fc18b2d99a302d0935c8345/ruff-0.13.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6052f8088728898e0a449f0dde8fafc7ed47e4d878168b211977e3e7e854f662", size = 13421890, upload-time = "2025-10-02T19:29:08.767Z" }, + { url = "https://files.pythonhosted.org/packages/ab/b5/c0feca27d45ae74185a6bacc399f5d8920ab82df2d732a17213fb86a2c4c/ruff-0.13.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc742c50f4ba72ce2a3be362bd359aef7d0d302bf7637a6f942eaa763bd292af", size = 13444870, upload-time = "2025-10-02T19:29:11.234Z" }, + { url = "https://files.pythonhosted.org/packages/50/a1/b655298a1f3fda4fdc7340c3f671a4b260b009068fbeb3e4e151e9e3e1bf/ruff-0.13.3-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:8e5640349493b378431637019366bbd73c927e515c9c1babfea3e932f5e68e1d", size = 13691599, upload-time = "2025-10-02T19:29:13.353Z" }, + { url = "https://files.pythonhosted.org/packages/32/b0/a8705065b2dafae007bcae21354e6e2e832e03eb077bb6c8e523c2becb92/ruff-0.13.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6b139f638a80eae7073c691a5dd8d581e0ba319540be97c343d60fb12949c8d0", size = 12421893, upload-time = "2025-10-02T19:29:15.668Z" }, + { url = "https://files.pythonhosted.org/packages/0d/1e/cbe7082588d025cddbb2f23e6dfef08b1a2ef6d6f8328584ad3015b5cebd/ruff-0.13.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:6b547def0a40054825de7cfa341039ebdfa51f3d4bfa6a0772940ed351d2746c", size = 12267220, upload-time = "2025-10-02T19:29:17.583Z" }, + { url = "https://files.pythonhosted.org/packages/a5/99/4086f9c43f85e0755996d09bdcb334b6fee9b1eabdf34e7d8b877fadf964/ruff-0.13.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9cc48a3564423915c93573f1981d57d101e617839bef38504f85f3677b3a0a3e", size = 13177818, upload-time = "2025-10-02T19:29:19.943Z" }, + { url = "https://files.pythonhosted.org/packages/9b/de/7b5db7e39947d9dc1c5f9f17b838ad6e680527d45288eeb568e860467010/ruff-0.13.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1a993b17ec03719c502881cb2d5f91771e8742f2ca6de740034433a97c561989", size = 13618715, upload-time = "2025-10-02T19:29:22.527Z" }, + { url = "https://files.pythonhosted.org/packages/28/d3/bb25ee567ce2f61ac52430cf99f446b0e6d49bdfa4188699ad005fdd16aa/ruff-0.13.3-py3-none-win32.whl", hash = "sha256:f14e0d1fe6460f07814d03c6e32e815bff411505178a1f539a38f6097d3e8ee3", size = 12334488, upload-time = "2025-10-02T19:29:24.782Z" }, + { url = "https://files.pythonhosted.org/packages/cf/49/12f5955818a1139eed288753479ba9d996f6ea0b101784bb1fe6977ec128/ruff-0.13.3-py3-none-win_amd64.whl", hash = "sha256:621e2e5812b691d4f244638d693e640f188bacbb9bc793ddd46837cea0503dd2", size = 13455262, upload-time = "2025-10-02T19:29:26.882Z" }, + { url = "https://files.pythonhosted.org/packages/fe/72/7b83242b26627a00e3af70d0394d68f8f02750d642567af12983031777fc/ruff-0.13.3-py3-none-win_arm64.whl", hash = "sha256:9e9e9d699841eaf4c2c798fa783df2fabc680b72059a02ca0ed81c460bc58330", size = 12538484, upload-time = "2025-10-02T19:29:28.951Z" }, +] + [[package]] name = "six" version = "1.17.0" From 1b54ef5d588128ae9c72638837965c116f87f966 Mon Sep 17 00:00:00 2001 From: Yurii Serhiichuk Date: Sat, 4 Oct 2025 20:04:29 +0200 Subject: [PATCH 2/7] Reformat code with ruff --- jules_mcp/__init__.py | 1 - jules_mcp/__main__.py | 3 +-- jules_mcp/jules_mcp.py | 56 +++++++++++++++++++++++++----------------- 3 files changed, 34 insertions(+), 26 deletions(-) diff --git a/jules_mcp/__init__.py b/jules_mcp/__init__.py index f365267..3ff67a6 100644 --- a/jules_mcp/__init__.py +++ b/jules_mcp/__init__.py @@ -1,4 +1,3 @@ - # Copyright (C) 2025 Yurii Serhiichuk # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/jules_mcp/__main__.py b/jules_mcp/__main__.py index 9c3e149..453b7c4 100644 --- a/jules_mcp/__main__.py +++ b/jules_mcp/__main__.py @@ -1,4 +1,3 @@ - # Copyright (C) 2025 Yurii Serhiichuk # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,5 +14,5 @@ from jules_mcp.jules_mcp import start_mcp -if __name__ == '__main__': +if __name__ == "__main__": start_mcp() diff --git a/jules_mcp/jules_mcp.py b/jules_mcp/jules_mcp.py index e28d42c..8557aa3 100644 --- a/jules_mcp/jules_mcp.py +++ b/jules_mcp/jules_mcp.py @@ -47,9 +47,12 @@ def jules(api_key: str | None = None) -> JulesClient: description="Get a single source by ID (e.g., sources/abc123 or abc123)", tags={"sources"}, annotations=ToolAnnotations( - title="Get Jules source by ID", readOnlyHint=True, idempotentHint=True, - destructiveHint=False, openWorldHint=True, - ) + title="Get Jules source by ID", + readOnlyHint=True, + idempotentHint=True, + destructiveHint=False, + openWorldHint=True, + ), ) def get_source(source_id: str) -> models.Source: """Get a single source by ID. @@ -68,9 +71,9 @@ def get_source(source_id: str) -> models.Source: tags={"sources"}, ) def list_sources( - filter_str: str | None = None, - page_size: int | None = None, - page_token: str | None = None, + filter_str: str | None = None, + page_size: int | None = None, + page_token: str | None = None, ) -> dict[str, Any]: """List sources (paginated). @@ -84,7 +87,9 @@ def list_sources( - sources: list[Source] - nextPageToken: Optional[str] """ - result = jules().sources.list(filter_str=filter_str, page_size=page_size, page_token=page_token) + result = jules().sources.list( + filter_str=filter_str, page_size=page_size, page_token=page_token + ) return result @@ -115,11 +120,11 @@ def get_all_sources(filter_str: str | None = None) -> list[models.Source]: tags={"sessions"}, ) def create_session( - prompt: str, - source: str, - starting_branch: str | None = None, - title: str | None = None, - require_plan_approval: bool = False, + prompt: str, + source: str, + starting_branch: str | None = None, + title: str | None = None, + require_plan_approval: bool = False, ) -> models.Session: """Create a new session. @@ -161,7 +166,9 @@ def get_session(session_id: str) -> models.Session: description="List sessions with pagination; returns items and nextPageToken.", tags={"sessions"}, ) -def list_sessions(page_size: int | None = None, page_token: str | None = None) -> dict[str, Any]: +def list_sessions( + page_size: int | None = None, page_token: str | None = None +) -> dict[str, Any]: """List sessions (paginated). Args: @@ -222,9 +229,9 @@ def send_session_message(session_id: str, prompt: str) -> dict[str, str]: tags={"sessions"}, ) def wait_for_session_completion( - session_id: str, - poll_interval: int = 5, - timeout: int | None = 600, + session_id: str, + poll_interval: int = 5, + timeout: int | None = 600, ) -> models.Session: """Wait for a session to reach a terminal state. @@ -233,8 +240,9 @@ def wait_for_session_completion( poll_interval: Seconds between polling requests (default: 5). timeout: Optional timeout in seconds (default: 600). Set None for no timeout. """ - return jules().sessions.wait_for_completion(session_id, poll_interval=poll_interval, - timeout=timeout) + return jules().sessions.wait_for_completion( + session_id, poll_interval=poll_interval, timeout=timeout + ) # -------------------- Activities -------------------- @@ -261,9 +269,9 @@ def get_activity(session_id: str, activity_id: str) -> models.Activity: tags={"activities"}, ) def list_activities( - session_id: str, - page_size: int | None = None, - page_token: str | None = None, + session_id: str, + page_size: int | None = None, + page_token: str | None = None, ) -> dict[str, Any]: """List activities for a session (paginated). @@ -277,7 +285,9 @@ def list_activities( - activities: list[Activity] - nextPageToken: Optional[str] """ - return jules().activities.list(session_id, page_size=page_size, page_token=page_token) + return jules().activities.list( + session_id, page_size=page_size, page_token=page_token + ) @mcp.tool( @@ -295,5 +305,5 @@ def start_mcp() -> None: mcp.run() -if __name__ == '__main__': +if __name__ == "__main__": start_mcp() From 14a464fec286899f3407b4700f3d0bd8515f44ea Mon Sep 17 00:00:00 2001 From: Yurii Serhiichuk Date: Sat, 4 Oct 2025 20:04:35 +0200 Subject: [PATCH 3/7] Add py.typed --- jules_mcp/py.typed | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 jules_mcp/py.typed diff --git a/jules_mcp/py.typed b/jules_mcp/py.typed new file mode 100644 index 0000000..e69de29 From b82a1b727cd0830ac02e8bbdcfbf693e2f23b3a2 Mon Sep 17 00:00:00 2001 From: Yurii Serhiichuk Date: Sat, 4 Oct 2025 20:04:42 +0200 Subject: [PATCH 4/7] Cleanup CI workflow --- .github/workflows/ci.yml | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8d39f49..74c7466 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,20 +10,28 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - name: Set up Python + - uses: actions/checkout@v5 + - name: "Set up Python" uses: actions/setup-python@v5 with: - python-version: '3.13' + python-version-file: "pyproject.toml" + - name: Install uv - run: pip install uv - - name: Install dependencies - run: uv sync --all-extras + uses: astral-sh/setup-uv@v6 + with: + enable-cache: true + + - name: Install the project + run: uv sync --locked --all-extras --dev + - name: Lint with ruff run: uv run ruff check . + - name: Format with ruff run: uv run ruff format --check . - - name: Type check with mypy - run: uv run mypy . + +# - name: Type check with mypy +# run: uv run mypy . + - name: Test with pytest - run: uv run pytest \ No newline at end of file + run: uv run pytest From d34c70dda6425552f5f64e286332ce8bebc18752 Mon Sep 17 00:00:00 2001 From: Yurii Serhiichuk Date: Sat, 4 Oct 2025 20:13:28 +0200 Subject: [PATCH 5/7] Update Dockerfile to use `uv` runtime, modify entrypoint, and add `JULES_API_KEY` environment variable --- Dockerfile | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index 6808f27..26c9e00 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,9 @@ # Use an official Python runtime as a parent image -FROM python:3.13-slim +FROM ghcr.io/astral-sh/uv:python3.13-trixie-slim # Set the working directory in the container WORKDIR /app -# Install uv -RUN pip install uv - # Copy the dependency files COPY pyproject.toml uv.lock ./ @@ -16,5 +13,7 @@ RUN uv sync --no-dev # Copy the rest of the application code COPY jules_mcp/ ./jules_mcp +ENV JULES_API_KEY="" + # Set the entrypoint -ENTRYPOINT ["python", "-m", "jules_mcp"] \ No newline at end of file +ENTRYPOINT ["uv", "run", "--with", "fastmcp", "--with", "jules-agent-sdk", "--with", "requests", "fastmcp", "run", "jules_mcp/jules_mcp.py"] From 423eeb32c766d751701e106761792783555e3a83 Mon Sep 17 00:00:00 2001 From: Yurii Serhiichuk Date: Sat, 4 Oct 2025 20:13:40 +0200 Subject: [PATCH 6/7] Update publish workflow to use `uv` tools and actions dependencies --- .github/workflows/publish.yml | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index c4499cc..f2f2216 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -15,15 +15,24 @@ jobs: id-token: write # this is required for trusted publishing steps: - - uses: actions/checkout@v4 - - name: Set up Python + - uses: actions/checkout@v5 + - name: "Set up Python" uses: actions/setup-python@v5 with: - python-version: "3.13" - - name: Install dependencies - run: pip install build + python-version-file: "pyproject.toml" + + - name: Install uv + uses: astral-sh/setup-uv@v6 + with: + enable-cache: true - name: Build package - run: python -m build + run: uv build + - uses: actions/upload-artifact@v4 + with: + name: artifact + path: dist/* + - name: Check package + run: uvx twine check dist/* - name: Publish package uses: pypa/gh-action-pypi-publish@v1.9.0 @@ -37,7 +46,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Log in to the Container registry uses: docker/login-action@v3 @@ -58,4 +67,4 @@ jobs: context: . push: true tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} \ No newline at end of file + labels: ${{ steps.meta.outputs.labels }} From 418b8cea392029baf5c82bf32b02a9591af7a0df Mon Sep 17 00:00:00 2001 From: Yurii Serhiichuk Date: Sat, 4 Oct 2025 20:22:36 +0200 Subject: [PATCH 7/7] Update CI workflow to use `uv run -m pytest` for testing See https://github.com/astral-sh/uv/issues/9291 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 74c7466..66ad527 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,4 +34,4 @@ jobs: # run: uv run mypy . - name: Test with pytest - run: uv run pytest + run: uv run -m pytest