diff --git a/CHANGELOG.md b/CHANGELOG.md index e12ec2c..c557e12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ 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) - [#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..8a72da2 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, + CapsuleReleaseJob, CapsuleSearchParams, Computation, DataAssetAttachParams, @@ -86,3 +87,35 @@ 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) -> 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) -> 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)