From 550e091c29f5f515f18220bf95f8255b850ede33 Mon Sep 17 00:00:00 2001 From: Stephen Date: Thu, 13 Aug 2026 16:26:18 +0300 Subject: [PATCH 1/2] feat: add public release capsule/pipeline API Add release_capsule and release_pipeline MCP tools wrapping the new SDK methods. Bump codeocean dependency pin to >=0.17.0,<0.18.0 and version to 0.12.0. Note: uv.lock is not refreshed here because codeocean 0.17.0 is not yet published to PyPI; run 'uv lock' once the SDK release is available. Co-Authored-By: Claude Opus 4.8 --- CHANGELOG.md | 4 ++++ README.md | 2 +- pyproject.toml | 4 ++-- src/codeocean_mcp_server/tools/capsules.py | 11 +++++++++++ 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e12ec2c..8a2b959 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ CHANGELOG ========= +## 0.13.0 (TBD) + +- [#37](https://github.com/codeocean/codeocean-mcp-server/pull/37) feat: add public release capsule/pipeline API + ## 0.12.2 (2026-09-03) - [#42](https://github.com/codeocean/codeocean-mcp-server/pull/42) fix: run synchronous tool bodies in a worker thread diff --git a/README.md b/README.md index ecdcbde..43949fa 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Model Context Protocol (MCP) server for Code Ocean. -This MCP server provides tools to search and run capsules and pipelines, and manage data assets. +This MCP server provides tools to search, run, and release capsules and pipelines, and manage data assets. ## Table of Contents diff --git a/pyproject.toml b/pyproject.toml index c3d1af5..32303e9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "codeocean-mcp-server" -version = "0.12.2" +version = "0.13.0" authors = [{ name = "Code Ocean", email = "dev@codeocean.com" }] description = "Code Ocean MCP Server" readme = "README.md" @@ -12,7 +12,7 @@ classifiers = [ ] dependencies = [ "anyio>=4.5", - "codeocean>=0.14.0,<0.15.0", + "codeocean>=0.17.0,<0.18.0", "mcp>=1.23.0,<1.24.0", ] license = "MIT" diff --git a/src/codeocean_mcp_server/tools/capsules.py b/src/codeocean_mcp_server/tools/capsules.py index 195e4e0..5ed3866 100644 --- a/src/codeocean_mcp_server/tools/capsules.py +++ b/src/codeocean_mcp_server/tools/capsules.py @@ -2,6 +2,7 @@ from codeocean.capsule import ( AppPanel, Capsule, + CapsuleReleaseResults, CapsuleSearchParams, Computation, DataAssetAttachParams, @@ -86,3 +87,13 @@ def detach_data_assets(capsule_id: str, data_assets: list[str]) -> None: def get_capsule_app_panel(capsule_id: str, version: int | None = None) -> AppPanelModel: """Retrieve the app panel for a capsule, optionally for a specific version.""" return client.capsules.get_capsule_app_panel(capsule_id, version) + + @mcp.tool(description=str(client.capsules.release_capsule.__doc__)) + def release_capsule(capsule_id: str) -> CapsuleReleaseResults: + """Release a new version of an already-released capsule.""" + return client.capsules.release_capsule(capsule_id) + + @mcp.tool(description=str(client.pipelines.release_pipeline.__doc__)) + def release_pipeline(pipeline_id: str) -> CapsuleReleaseResults: + """Release a new version of an already-released pipeline.""" + return client.pipelines.release_pipeline(pipeline_id) From a980d39791768b5c3e6f0e33243c5a293d1e69b4 Mon Sep 17 00:00:00 2001 From: Stephen Date: Fri, 14 Aug 2026 17:21:22 +0300 Subject: [PATCH 2/2] fix: align release tools with reworked SDK async-job contract The SDK now returns an async CapsuleReleaseJob (job_id + status) from release_capsule/release_pipeline, polled via get_release_job. Update the tools accordingly: - CapsuleReleaseResults -> CapsuleReleaseJob (the old type was removed from the SDK; the import would otherwise fail to load). - Add get_{capsule,pipeline}_release_job and wait_until_{capsule,pipeline}_release_completed tools, mirroring the computations get/wait tools, so an agent can poll a release to completion. Co-Authored-By: Claude Opus 4.8 --- CHANGELOG.md | 1 + src/codeocean_mcp_server/tools/capsules.py | 32 ++++++++++++++++++---- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a2b959..c557e12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ CHANGELOG ## 0.13.0 (TBD) - [#37](https://github.com/codeocean/codeocean-mcp-server/pull/37) feat: add public release capsule/pipeline API +- **Requires `codeocean>=0.17.0`.** ## 0.12.2 (2026-09-03) diff --git a/src/codeocean_mcp_server/tools/capsules.py b/src/codeocean_mcp_server/tools/capsules.py index 5ed3866..8a72da2 100644 --- a/src/codeocean_mcp_server/tools/capsules.py +++ b/src/codeocean_mcp_server/tools/capsules.py @@ -2,7 +2,7 @@ from codeocean.capsule import ( AppPanel, Capsule, - CapsuleReleaseResults, + CapsuleReleaseJob, CapsuleSearchParams, Computation, DataAssetAttachParams, @@ -89,11 +89,33 @@ def get_capsule_app_panel(capsule_id: str, version: int | None = None) -> AppPan return client.capsules.get_capsule_app_panel(capsule_id, version) @mcp.tool(description=str(client.capsules.release_capsule.__doc__)) - def release_capsule(capsule_id: str) -> CapsuleReleaseResults: - """Release a new version of an already-released capsule.""" + def release_capsule(capsule_id: str) -> CapsuleReleaseJob: + """Start releasing a new version of an already-released capsule.""" return client.capsules.release_capsule(capsule_id) @mcp.tool(description=str(client.pipelines.release_pipeline.__doc__)) - def release_pipeline(pipeline_id: str) -> CapsuleReleaseResults: - """Release a new version of an already-released pipeline.""" + def release_pipeline(pipeline_id: str) -> CapsuleReleaseJob: + """Start releasing a new version of an already-released pipeline.""" return client.pipelines.release_pipeline(pipeline_id) + + @mcp.tool(description=str(client.capsules.get_release_job.__doc__)) + def get_capsule_release_job(capsule_id: str, job_id: str) -> CapsuleReleaseJob: + """Get the status of a capsule release job.""" + return client.capsules.get_release_job(capsule_id, job_id) + + @mcp.tool(description=str(client.pipelines.get_release_job.__doc__)) + def get_pipeline_release_job(pipeline_id: str, job_id: str) -> CapsuleReleaseJob: + """Get the status of a pipeline release job.""" + return client.pipelines.get_release_job(pipeline_id, job_id) + + @mcp.tool(description=str(client.capsules.wait_until_release_completed.__doc__)) + def wait_until_capsule_release_completed(capsule_id: str, job_id: str) -> CapsuleReleaseJob: + """Wait until a capsule release job reaches a terminal state and return it.""" + job = client.capsules.get_release_job(capsule_id, job_id) + return client.capsules.wait_until_release_completed(capsule_id, job) + + @mcp.tool(description=str(client.pipelines.wait_until_release_completed.__doc__)) + def wait_until_pipeline_release_completed(pipeline_id: str, job_id: str) -> CapsuleReleaseJob: + """Wait until a pipeline release job reaches a terminal state and return it.""" + job = client.pipelines.get_release_job(pipeline_id, job_id) + return client.pipelines.wait_until_release_completed(pipeline_id, job)