diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 4a2f7e609..cce9d1c6d 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "1.5.1"
+ ".": "1.7.0"
}
\ No newline at end of file
diff --git a/.stats.yml b/.stats.yml
index 81d520c67..73c283357 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
-configured_endpoints: 112
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-b493cadf2e3b9658163a6bcf8f51e088dda169f12d68469c4441d17e889f5556.yml
-openapi_spec_hash: b27ec3822d88d10efa268f1681fddff3
-config_hash: 6c26299fd9ef01fb4713612a9a2ad17c
+configured_endpoints: 117
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-15b7fd06098d8cb3639efb3b401a03d7d97a8ab4960b08077aa871f0cacb33d3.yml
+openapi_spec_hash: 93ab2fe88f9e57d8f262ad6d1179190e
+config_hash: eb28692edd68a6ae95cf92af931c9976
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 55d18da5c..569b852a4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,26 @@
# Changelog
+## 1.7.0 (2026-02-05)
+
+Full Changelog: [v1.5.1...v1.7.0](https://github.com/runloopai/api-client-python/compare/v1.5.1...v1.7.0)
+
+### Features
+
+* add api to query devbox usage ([#7296](https://github.com/runloopai/api-client-python/issues/7296)) ([ab903f5](https://github.com/runloopai/api-client-python/commit/ab903f557ecc8be15c8d150068a82dfaaac23f4e))
+* adding MCP Config routes ([#7331](https://github.com/runloopai/api-client-python/issues/7331)) ([79d05c0](https://github.com/runloopai/api-client-python/commit/79d05c00f9bb85a458cc300c5b065b6249a16a88))
+* **devbox:** add mcp configuration to devbox create ([#7341](https://github.com/runloopai/api-client-python/issues/7341)) ([0b81e31](https://github.com/runloopai/api-client-python/commit/0b81e3151e3df41f667923cac4e714ba6d7ca3bc))
+* **devbox:** adding gateway config ([#736](https://github.com/runloopai/api-client-python/issues/736)) ([9f4b49d](https://github.com/runloopai/api-client-python/commit/9f4b49daa13dd3aac5a8ee26a262fd7b5b705ff1))
+
+
+### Bug Fixes
+
+* **sdk:** fixed pagination for scorer, scenario and network policy list methods ([#731](https://github.com/runloopai/api-client-python/issues/731)) ([d55fceb](https://github.com/runloopai/api-client-python/commit/d55fceb7d42877cd0ac69a934ef300815f371342))
+
+
+### Chores
+
+* Mark legacy tunnel methods deprecated ([#737](https://github.com/runloopai/api-client-python/issues/737)) ([764b445](https://github.com/runloopai/api-client-python/commit/764b445018ec50db3105c0dd8ea58b02e4690e12))
+
## 1.5.1 (2026-01-30)
Full Changelog: [v1.5.0...v1.5.1](https://github.com/runloopai/api-client-python/compare/v1.5.0...v1.5.1)
diff --git a/api.md b/api.md
index 2720824ad..6cbe1e604 100644
--- a/api.md
+++ b/api.md
@@ -126,6 +126,7 @@ from runloop_api_client.types import (
DevboxExecutionDetailView,
DevboxKillExecutionRequest,
DevboxListView,
+ DevboxResourceUsageView,
DevboxSendStdInRequest,
DevboxSendStdInResult,
DevboxSnapshotListView,
@@ -158,6 +159,7 @@ Methods:
- client.devboxes.read_file_contents(id, \*\*params) -> str
- client.devboxes.remove_tunnel(id, \*\*params) -> object
- client.devboxes.resume(id) -> DevboxView
+- client.devboxes.retrieve_resource_usage(id) -> DevboxResourceUsageView
- client.devboxes.shutdown(id) -> DevboxView
- client.devboxes.snapshot_disk(id, \*\*params) -> DevboxSnapshotView
- client.devboxes.snapshot_disk_async(id, \*\*params) -> DevboxSnapshotView
@@ -299,7 +301,6 @@ from runloop_api_client.types.scenarios import (
ScorerRetrieveResponse,
ScorerUpdateResponse,
ScorerListResponse,
- ScorerValidateResponse,
)
```
@@ -309,7 +310,6 @@ Methods:
- client.scenarios.scorers.retrieve(id) -> ScorerRetrieveResponse
- client.scenarios.scorers.update(id, \*\*params) -> ScorerUpdateResponse
- client.scenarios.scorers.list(\*\*params) -> SyncScenarioScorersCursorIDPage[ScorerListResponse]
-- client.scenarios.scorers.validate(id, \*\*params) -> ScorerValidateResponse
# Objects
@@ -420,3 +420,24 @@ Methods:
- client.gateway_configs.update(id, \*\*params) -> GatewayConfigView
- client.gateway_configs.list(\*\*params) -> SyncGatewayConfigsCursorIDPage[GatewayConfigView]
- client.gateway_configs.delete(id) -> GatewayConfigView
+
+# McpConfigs
+
+Types:
+
+```python
+from runloop_api_client.types import (
+ McpConfigCreateParameters,
+ McpConfigListView,
+ McpConfigUpdateParameters,
+ McpConfigView,
+)
+```
+
+Methods:
+
+- client.mcp_configs.create(\*\*params) -> McpConfigView
+- client.mcp_configs.retrieve(id) -> McpConfigView
+- client.mcp_configs.update(id, \*\*params) -> McpConfigView
+- client.mcp_configs.list(\*\*params) -> SyncMcpConfigsCursorIDPage[McpConfigView]
+- client.mcp_configs.delete(id) -> McpConfigView
diff --git a/docs/sdk/types.rst b/docs/sdk/types.rst
index 9d2983cd0..ab6abafee 100644
--- a/docs/sdk/types.rst
+++ b/docs/sdk/types.rst
@@ -81,7 +81,7 @@ These TypeDicts define parameters for storage object creation, listing, and down
Scorer Parameters
-----------------
-These TypeDicts define parameters for scorer creation, listing, updating, and validation.
+These TypeDicts define parameters for scorer creation, listing, and updating.
.. autotypeddict:: runloop_api_client.sdk._types.SDKScorerCreateParams
@@ -89,8 +89,6 @@ These TypeDicts define parameters for scorer creation, listing, updating, and va
.. autotypeddict:: runloop_api_client.sdk._types.SDKScorerUpdateParams
-.. autotypeddict:: runloop_api_client.sdk._types.SDKScorerValidateParams
-
Core Request Options
--------------------
diff --git a/pyproject.toml b/pyproject.toml
index 60897f964..44b3bde57 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "runloop_api_client"
-version = "1.5.1"
+version = "1.7.0"
description = "The official Python library for the runloop API"
dynamic = ["readme"]
license = "MIT"
diff --git a/src/runloop_api_client/_client.py b/src/runloop_api_client/_client.py
index 32561bc0f..0f626950b 100644
--- a/src/runloop_api_client/_client.py
+++ b/src/runloop_api_client/_client.py
@@ -39,6 +39,7 @@
scenarios,
benchmarks,
blueprints,
+ mcp_configs,
repositories,
benchmark_jobs,
benchmark_runs,
@@ -50,6 +51,7 @@
from .resources.secrets import SecretsResource, AsyncSecretsResource
from .resources.benchmarks import BenchmarksResource, AsyncBenchmarksResource
from .resources.blueprints import BlueprintsResource, AsyncBlueprintsResource
+ from .resources.mcp_configs import McpConfigsResource, AsyncMcpConfigsResource
from .resources.repositories import RepositoriesResource, AsyncRepositoriesResource
from .resources.benchmark_jobs import BenchmarkJobsResource, AsyncBenchmarkJobsResource
from .resources.benchmark_runs import BenchmarkRunsResource, AsyncBenchmarkRunsResource
@@ -190,6 +192,12 @@ def gateway_configs(self) -> GatewayConfigsResource:
return GatewayConfigsResource(self)
+ @cached_property
+ def mcp_configs(self) -> McpConfigsResource:
+ from .resources.mcp_configs import McpConfigsResource
+
+ return McpConfigsResource(self)
+
@cached_property
def with_raw_response(self) -> RunloopWithRawResponse:
return RunloopWithRawResponse(self)
@@ -432,6 +440,12 @@ def gateway_configs(self) -> AsyncGatewayConfigsResource:
return AsyncGatewayConfigsResource(self)
+ @cached_property
+ def mcp_configs(self) -> AsyncMcpConfigsResource:
+ from .resources.mcp_configs import AsyncMcpConfigsResource
+
+ return AsyncMcpConfigsResource(self)
+
@cached_property
def with_raw_response(self) -> AsyncRunloopWithRawResponse:
return AsyncRunloopWithRawResponse(self)
@@ -623,6 +637,12 @@ def gateway_configs(self) -> gateway_configs.GatewayConfigsResourceWithRawRespon
return GatewayConfigsResourceWithRawResponse(self._client.gateway_configs)
+ @cached_property
+ def mcp_configs(self) -> mcp_configs.McpConfigsResourceWithRawResponse:
+ from .resources.mcp_configs import McpConfigsResourceWithRawResponse
+
+ return McpConfigsResourceWithRawResponse(self._client.mcp_configs)
+
class AsyncRunloopWithRawResponse:
_client: AsyncRunloop
@@ -702,6 +722,12 @@ def gateway_configs(self) -> gateway_configs.AsyncGatewayConfigsResourceWithRawR
return AsyncGatewayConfigsResourceWithRawResponse(self._client.gateway_configs)
+ @cached_property
+ def mcp_configs(self) -> mcp_configs.AsyncMcpConfigsResourceWithRawResponse:
+ from .resources.mcp_configs import AsyncMcpConfigsResourceWithRawResponse
+
+ return AsyncMcpConfigsResourceWithRawResponse(self._client.mcp_configs)
+
class RunloopWithStreamedResponse:
_client: Runloop
@@ -781,6 +807,12 @@ def gateway_configs(self) -> gateway_configs.GatewayConfigsResourceWithStreaming
return GatewayConfigsResourceWithStreamingResponse(self._client.gateway_configs)
+ @cached_property
+ def mcp_configs(self) -> mcp_configs.McpConfigsResourceWithStreamingResponse:
+ from .resources.mcp_configs import McpConfigsResourceWithStreamingResponse
+
+ return McpConfigsResourceWithStreamingResponse(self._client.mcp_configs)
+
class AsyncRunloopWithStreamedResponse:
_client: AsyncRunloop
@@ -860,6 +892,12 @@ def gateway_configs(self) -> gateway_configs.AsyncGatewayConfigsResourceWithStre
return AsyncGatewayConfigsResourceWithStreamingResponse(self._client.gateway_configs)
+ @cached_property
+ def mcp_configs(self) -> mcp_configs.AsyncMcpConfigsResourceWithStreamingResponse:
+ from .resources.mcp_configs import AsyncMcpConfigsResourceWithStreamingResponse
+
+ return AsyncMcpConfigsResourceWithStreamingResponse(self._client.mcp_configs)
+
Client = Runloop
diff --git a/src/runloop_api_client/_version.py b/src/runloop_api_client/_version.py
index 962984a45..da0fd9d41 100644
--- a/src/runloop_api_client/_version.py
+++ b/src/runloop_api_client/_version.py
@@ -1,4 +1,4 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
__title__ = "runloop_api_client"
-__version__ = "1.5.1" # x-release-please-version
+__version__ = "1.7.0" # x-release-please-version
diff --git a/src/runloop_api_client/pagination.py b/src/runloop_api_client/pagination.py
index e937259d7..e084ef68e 100644
--- a/src/runloop_api_client/pagination.py
+++ b/src/runloop_api_client/pagination.py
@@ -32,6 +32,8 @@
"AsyncNetworkPoliciesCursorIDPage",
"SyncGatewayConfigsCursorIDPage",
"AsyncGatewayConfigsCursorIDPage",
+ "SyncMcpConfigsCursorIDPage",
+ "AsyncMcpConfigsCursorIDPage",
]
_T = TypeVar("_T")
@@ -102,6 +104,11 @@ class GatewayConfigsCursorIDPageItem(Protocol):
id: str
+@runtime_checkable
+class McpConfigsCursorIDPageItem(Protocol):
+ id: str
+
+
class SyncBlueprintsCursorIDPage(BaseSyncPage[_T], BasePage[_T], Generic[_T]):
blueprints: List[_T]
has_more: Optional[bool] = None
@@ -984,3 +991,71 @@ def next_page_info(self) -> Optional[PageInfo]:
return None
return PageInfo(params={"starting_after": item.id})
+
+
+class SyncMcpConfigsCursorIDPage(BaseSyncPage[_T], BasePage[_T], Generic[_T]):
+ mcp_configs: List[_T]
+ has_more: Optional[bool] = None
+ total_count: Optional[int] = None
+
+ @override
+ def _get_page_items(self) -> List[_T]:
+ mcp_configs = self.mcp_configs
+ if not mcp_configs:
+ return []
+ return mcp_configs
+
+ @override
+ def has_next_page(self) -> bool:
+ has_more = self.has_more
+ if has_more is not None and has_more is False:
+ return False
+
+ return super().has_next_page()
+
+ @override
+ def next_page_info(self) -> Optional[PageInfo]:
+ mcp_configs = self.mcp_configs
+ if not mcp_configs:
+ return None
+
+ item = cast(Any, mcp_configs[-1])
+ if not isinstance(item, McpConfigsCursorIDPageItem) or item.id is None: # pyright: ignore[reportUnnecessaryComparison]
+ # TODO emit warning log
+ return None
+
+ return PageInfo(params={"starting_after": item.id})
+
+
+class AsyncMcpConfigsCursorIDPage(BaseAsyncPage[_T], BasePage[_T], Generic[_T]):
+ mcp_configs: List[_T]
+ has_more: Optional[bool] = None
+ total_count: Optional[int] = None
+
+ @override
+ def _get_page_items(self) -> List[_T]:
+ mcp_configs = self.mcp_configs
+ if not mcp_configs:
+ return []
+ return mcp_configs
+
+ @override
+ def has_next_page(self) -> bool:
+ has_more = self.has_more
+ if has_more is not None and has_more is False:
+ return False
+
+ return super().has_next_page()
+
+ @override
+ def next_page_info(self) -> Optional[PageInfo]:
+ mcp_configs = self.mcp_configs
+ if not mcp_configs:
+ return None
+
+ item = cast(Any, mcp_configs[-1])
+ if not isinstance(item, McpConfigsCursorIDPageItem) or item.id is None: # pyright: ignore[reportUnnecessaryComparison]
+ # TODO emit warning log
+ return None
+
+ return PageInfo(params={"starting_after": item.id})
diff --git a/src/runloop_api_client/resources/__init__.py b/src/runloop_api_client/resources/__init__.py
index 877822444..2e0584f25 100644
--- a/src/runloop_api_client/resources/__init__.py
+++ b/src/runloop_api_client/resources/__init__.py
@@ -56,6 +56,14 @@
BlueprintsResourceWithStreamingResponse,
AsyncBlueprintsResourceWithStreamingResponse,
)
+from .mcp_configs import (
+ McpConfigsResource,
+ AsyncMcpConfigsResource,
+ McpConfigsResourceWithRawResponse,
+ AsyncMcpConfigsResourceWithRawResponse,
+ McpConfigsResourceWithStreamingResponse,
+ AsyncMcpConfigsResourceWithStreamingResponse,
+)
from .repositories import (
RepositoriesResource,
AsyncRepositoriesResource,
@@ -170,4 +178,10 @@
"AsyncGatewayConfigsResourceWithRawResponse",
"GatewayConfigsResourceWithStreamingResponse",
"AsyncGatewayConfigsResourceWithStreamingResponse",
+ "McpConfigsResource",
+ "AsyncMcpConfigsResource",
+ "McpConfigsResourceWithRawResponse",
+ "AsyncMcpConfigsResourceWithRawResponse",
+ "McpConfigsResourceWithStreamingResponse",
+ "AsyncMcpConfigsResourceWithStreamingResponse",
]
diff --git a/src/runloop_api_client/resources/devboxes/devboxes.py b/src/runloop_api_client/resources/devboxes/devboxes.py
index 37c4b994a..34b118ed8 100644
--- a/src/runloop_api_client/resources/devboxes/devboxes.py
+++ b/src/runloop_api_client/resources/devboxes/devboxes.py
@@ -105,6 +105,7 @@
from ...types.shared_params.mount import Mount
from ...types.devbox_snapshot_view import DevboxSnapshotView
from ...types.shared.launch_parameters import LaunchParameters as SharedLaunchParameters
+from ...types.devbox_resource_usage_view import DevboxResourceUsageView
from ...types.devbox_execution_detail_view import DevboxExecutionDetailView
from ...types.devbox_create_ssh_key_response import DevboxCreateSSHKeyResponse
from ...types.shared_params.launch_parameters import LaunchParameters
@@ -189,6 +190,7 @@ def create(
file_mounts: Optional[Dict[str, str]] | Omit = omit,
gateways: Optional[Dict[str, devbox_create_params.Gateways]] | Omit = omit,
launch_parameters: Optional[LaunchParameters] | Omit = omit,
+ mcp: Optional[Iterable[devbox_create_params.Mcp]] | Omit = omit,
metadata: Optional[Dict[str, str]] | Omit = omit,
mounts: Optional[Iterable[Mount]] | Omit = omit,
name: Optional[str] | Omit = omit,
@@ -239,6 +241,11 @@ def create(
launch_parameters: Parameters to configure the resources and launch time behavior of the Devbox.
+ mcp: [Beta] (Optional) MCP specifications for MCP server access. Each spec links an
+ MCP config to a secret. The devbox will receive environment variables
+ (RL_MCP_URL, RL_MCP_TOKEN) for accessing MCP servers through the MCP hub.
+ Example: [{'mcp_config': 'github-readonly', 'secret': 'MY_GITHUB_TOKEN'}]
+
metadata: User defined metadata to attach to the devbox for organization.
mounts: A list of mounts to be included in the Devbox.
@@ -281,6 +288,7 @@ def create(
"file_mounts": file_mounts,
"gateways": gateways,
"launch_parameters": launch_parameters,
+ "mcp": mcp,
"metadata": metadata,
"mounts": mounts,
"name": name,
@@ -498,6 +506,7 @@ def create_and_await_running(
file_mounts: Optional[Dict[str, str]] | Omit = omit,
gateways: Optional[Dict[str, devbox_create_params.Gateways]] | Omit = omit,
launch_parameters: Optional[LaunchParameters] | Omit = omit,
+ mcp: Optional[Iterable[devbox_create_params.Mcp]] | Omit = omit,
metadata: Optional[Dict[str, str]] | Omit = omit,
mounts: Optional[Iterable[Mount]] | Omit = omit,
name: Optional[str] | Omit = omit,
@@ -539,6 +548,7 @@ def create_and_await_running(
file_mounts=file_mounts,
gateways=gateways,
launch_parameters=launch_parameters,
+ mcp=mcp,
metadata=metadata,
mounts=mounts,
name=name,
@@ -1364,6 +1374,43 @@ def resume(
cast_to=DevboxView,
)
+ def retrieve_resource_usage(
+ self,
+ id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> DevboxResourceUsageView:
+ """Get resource usage metrics for a specific Devbox.
+
+ Returns CPU, memory, and disk
+ consumption calculated from the Devbox's lifecycle, excluding any suspended
+ periods for CPU and memory. Disk usage includes the full elapsed time since
+ storage is consumed even when suspended.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._get(
+ f"/v1/devboxes/{id}/usage",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=DevboxResourceUsageView,
+ )
+
def shutdown(
self,
id: str,
@@ -1805,6 +1852,7 @@ async def create(
file_mounts: Optional[Dict[str, str]] | Omit = omit,
gateways: Optional[Dict[str, devbox_create_params.Gateways]] | Omit = omit,
launch_parameters: Optional[LaunchParameters] | Omit = omit,
+ mcp: Optional[Iterable[devbox_create_params.Mcp]] | Omit = omit,
metadata: Optional[Dict[str, str]] | Omit = omit,
mounts: Optional[Iterable[Mount]] | Omit = omit,
name: Optional[str] | Omit = omit,
@@ -1855,6 +1903,11 @@ async def create(
launch_parameters: Parameters to configure the resources and launch time behavior of the Devbox.
+ mcp: [Beta] (Optional) MCP specifications for MCP server access. Each spec links an
+ MCP config to a secret. The devbox will receive environment variables
+ (RL_MCP_URL, RL_MCP_TOKEN) for accessing MCP servers through the MCP hub.
+ Example: [{'mcp_config': 'github-readonly', 'secret': 'MY_GITHUB_TOKEN'}]
+
metadata: User defined metadata to attach to the devbox for organization.
mounts: A list of mounts to be included in the Devbox.
@@ -1897,6 +1950,7 @@ async def create(
"file_mounts": file_mounts,
"gateways": gateways,
"launch_parameters": launch_parameters,
+ "mcp": mcp,
"metadata": metadata,
"mounts": mounts,
"name": name,
@@ -1961,6 +2015,7 @@ async def create_and_await_running(
file_mounts: Optional[Dict[str, str]] | Omit = omit,
gateways: Optional[Dict[str, devbox_create_params.Gateways]] | Omit = omit,
launch_parameters: Optional[LaunchParameters] | Omit = omit,
+ mcp: Optional[Iterable[devbox_create_params.Mcp]] | Omit = omit,
metadata: Optional[Dict[str, str]] | Omit = omit,
mounts: Optional[Iterable[Mount]] | Omit = omit,
name: Optional[str] | Omit = omit,
@@ -2003,6 +2058,7 @@ async def create_and_await_running(
file_mounts=file_mounts,
gateways=gateways,
launch_parameters=launch_parameters,
+ mcp=mcp,
metadata=metadata,
mounts=mounts,
name=name,
@@ -2980,6 +3036,43 @@ async def resume(
cast_to=DevboxView,
)
+ async def retrieve_resource_usage(
+ self,
+ id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> DevboxResourceUsageView:
+ """Get resource usage metrics for a specific Devbox.
+
+ Returns CPU, memory, and disk
+ consumption calculated from the Devbox's lifecycle, excluding any suspended
+ periods for CPU and memory. Disk usage includes the full elapsed time since
+ storage is consumed even when suspended.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._get(
+ f"/v1/devboxes/{id}/usage",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=DevboxResourceUsageView,
+ )
+
async def shutdown(
self,
id: str,
@@ -3434,6 +3527,9 @@ def __init__(self, devboxes: DevboxesResource) -> None:
self.resume = to_raw_response_wrapper(
devboxes.resume,
)
+ self.retrieve_resource_usage = to_raw_response_wrapper(
+ devboxes.retrieve_resource_usage,
+ )
self.shutdown = to_raw_response_wrapper(
devboxes.shutdown,
)
@@ -3539,6 +3635,9 @@ def __init__(self, devboxes: AsyncDevboxesResource) -> None:
self.resume = async_to_raw_response_wrapper(
devboxes.resume,
)
+ self.retrieve_resource_usage = async_to_raw_response_wrapper(
+ devboxes.retrieve_resource_usage,
+ )
self.shutdown = async_to_raw_response_wrapper(
devboxes.shutdown,
)
@@ -3644,6 +3743,9 @@ def __init__(self, devboxes: DevboxesResource) -> None:
self.resume = to_streamed_response_wrapper(
devboxes.resume,
)
+ self.retrieve_resource_usage = to_streamed_response_wrapper(
+ devboxes.retrieve_resource_usage,
+ )
self.shutdown = to_streamed_response_wrapper(
devboxes.shutdown,
)
@@ -3749,6 +3851,9 @@ def __init__(self, devboxes: AsyncDevboxesResource) -> None:
self.resume = async_to_streamed_response_wrapper(
devboxes.resume,
)
+ self.retrieve_resource_usage = async_to_streamed_response_wrapper(
+ devboxes.retrieve_resource_usage,
+ )
self.shutdown = async_to_streamed_response_wrapper(
devboxes.shutdown,
)
diff --git a/src/runloop_api_client/resources/mcp_configs.py b/src/runloop_api_client/resources/mcp_configs.py
new file mode 100644
index 000000000..24b05cc2d
--- /dev/null
+++ b/src/runloop_api_client/resources/mcp_configs.py
@@ -0,0 +1,662 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+
+import httpx
+
+from ..types import mcp_config_list_params, mcp_config_create_params, mcp_config_update_params
+from .._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given
+from .._utils import maybe_transform, async_maybe_transform
+from .._compat import cached_property
+from .._resource import SyncAPIResource, AsyncAPIResource
+from .._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ..pagination import SyncMcpConfigsCursorIDPage, AsyncMcpConfigsCursorIDPage
+from .._base_client import AsyncPaginator, make_request_options
+from ..types.mcp_config_view import McpConfigView
+
+__all__ = ["McpConfigsResource", "AsyncMcpConfigsResource"]
+
+
+class McpConfigsResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> McpConfigsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/runloopai/api-client-python#accessing-raw-response-data-eg-headers
+ """
+ return McpConfigsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> McpConfigsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/runloopai/api-client-python#with_streaming_response
+ """
+ return McpConfigsResourceWithStreamingResponse(self)
+
+ def create(
+ self,
+ *,
+ allowed_tools: SequenceNotStr[str],
+ endpoint: str,
+ name: str,
+ description: Optional[str] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ idempotency_key: str | None = None,
+ ) -> McpConfigView:
+ """
+ [Beta] Create a new McpConfig to connect to an upstream MCP (Model Context
+ Protocol) server. The config specifies the target endpoint and which tools are
+ allowed.
+
+ Args:
+ allowed_tools:
+ Glob patterns specifying which tools are allowed from this MCP server. Examples:
+ ['*'] for all tools, ['github.search_*', 'github.get_*'] for specific patterns.
+
+ endpoint: The target MCP server endpoint URL (e.g., 'https://mcp.example.com').
+
+ name: The human-readable name for the McpConfig. Must be unique within your account.
+ The first segment before '-' is used as the service name for tool routing (e.g.,
+ 'github-readonly' uses 'github' as the service name).
+
+ description: Optional description for this MCP configuration.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+
+ idempotency_key: Specify a custom idempotency key for this request
+ """
+ return self._post(
+ "/v1/mcp-configs",
+ body=maybe_transform(
+ {
+ "allowed_tools": allowed_tools,
+ "endpoint": endpoint,
+ "name": name,
+ "description": description,
+ },
+ mcp_config_create_params.McpConfigCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ idempotency_key=idempotency_key,
+ ),
+ cast_to=McpConfigView,
+ )
+
+ def retrieve(
+ self,
+ id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> McpConfigView:
+ """
+ [Beta] Get a specific McpConfig by its unique identifier.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._get(
+ f"/v1/mcp-configs/{id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=McpConfigView,
+ )
+
+ def update(
+ self,
+ id: str,
+ *,
+ allowed_tools: Optional[SequenceNotStr[str]] | Omit = omit,
+ description: Optional[str] | Omit = omit,
+ endpoint: Optional[str] | Omit = omit,
+ name: Optional[str] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ idempotency_key: str | None = None,
+ ) -> McpConfigView:
+ """[Beta] Update an existing McpConfig.
+
+ All fields are optional.
+
+ Args:
+ allowed_tools: New glob patterns specifying which tools are allowed. Examples: ['*'] for all
+ tools, ['github.search_*'] for specific patterns.
+
+ description: New description for this MCP configuration.
+
+ endpoint: New target MCP server endpoint URL.
+
+ name: New name for the McpConfig. Must be unique within your account.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+
+ idempotency_key: Specify a custom idempotency key for this request
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._post(
+ f"/v1/mcp-configs/{id}",
+ body=maybe_transform(
+ {
+ "allowed_tools": allowed_tools,
+ "description": description,
+ "endpoint": endpoint,
+ "name": name,
+ },
+ mcp_config_update_params.McpConfigUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ idempotency_key=idempotency_key,
+ ),
+ cast_to=McpConfigView,
+ )
+
+ def list(
+ self,
+ *,
+ id: str | Omit = omit,
+ limit: int | Omit = omit,
+ name: str | Omit = omit,
+ starting_after: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> SyncMcpConfigsCursorIDPage[McpConfigView]:
+ """
+ [Beta] List all McpConfigs for the authenticated account.
+
+ Args:
+ id: Filter by ID.
+
+ limit: The limit of items to return. Default is 20. Max is 5000.
+
+ name: Filter by name (prefix match supported).
+
+ starting_after: Load the next page of data starting after the item with the given ID.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._get_api_list(
+ "/v1/mcp-configs",
+ page=SyncMcpConfigsCursorIDPage[McpConfigView],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "id": id,
+ "limit": limit,
+ "name": name,
+ "starting_after": starting_after,
+ },
+ mcp_config_list_params.McpConfigListParams,
+ ),
+ ),
+ model=McpConfigView,
+ )
+
+ def delete(
+ self,
+ id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ idempotency_key: str | None = None,
+ ) -> McpConfigView:
+ """[Beta] Delete an existing McpConfig.
+
+ This action is irreversible.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+
+ idempotency_key: Specify a custom idempotency key for this request
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._post(
+ f"/v1/mcp-configs/{id}/delete",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ idempotency_key=idempotency_key,
+ ),
+ cast_to=McpConfigView,
+ )
+
+
+class AsyncMcpConfigsResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncMcpConfigsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/runloopai/api-client-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncMcpConfigsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncMcpConfigsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/runloopai/api-client-python#with_streaming_response
+ """
+ return AsyncMcpConfigsResourceWithStreamingResponse(self)
+
+ async def create(
+ self,
+ *,
+ allowed_tools: SequenceNotStr[str],
+ endpoint: str,
+ name: str,
+ description: Optional[str] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ idempotency_key: str | None = None,
+ ) -> McpConfigView:
+ """
+ [Beta] Create a new McpConfig to connect to an upstream MCP (Model Context
+ Protocol) server. The config specifies the target endpoint and which tools are
+ allowed.
+
+ Args:
+ allowed_tools:
+ Glob patterns specifying which tools are allowed from this MCP server. Examples:
+ ['*'] for all tools, ['github.search_*', 'github.get_*'] for specific patterns.
+
+ endpoint: The target MCP server endpoint URL (e.g., 'https://mcp.example.com').
+
+ name: The human-readable name for the McpConfig. Must be unique within your account.
+ The first segment before '-' is used as the service name for tool routing (e.g.,
+ 'github-readonly' uses 'github' as the service name).
+
+ description: Optional description for this MCP configuration.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+
+ idempotency_key: Specify a custom idempotency key for this request
+ """
+ return await self._post(
+ "/v1/mcp-configs",
+ body=await async_maybe_transform(
+ {
+ "allowed_tools": allowed_tools,
+ "endpoint": endpoint,
+ "name": name,
+ "description": description,
+ },
+ mcp_config_create_params.McpConfigCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ idempotency_key=idempotency_key,
+ ),
+ cast_to=McpConfigView,
+ )
+
+ async def retrieve(
+ self,
+ id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> McpConfigView:
+ """
+ [Beta] Get a specific McpConfig by its unique identifier.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._get(
+ f"/v1/mcp-configs/{id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=McpConfigView,
+ )
+
+ async def update(
+ self,
+ id: str,
+ *,
+ allowed_tools: Optional[SequenceNotStr[str]] | Omit = omit,
+ description: Optional[str] | Omit = omit,
+ endpoint: Optional[str] | Omit = omit,
+ name: Optional[str] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ idempotency_key: str | None = None,
+ ) -> McpConfigView:
+ """[Beta] Update an existing McpConfig.
+
+ All fields are optional.
+
+ Args:
+ allowed_tools: New glob patterns specifying which tools are allowed. Examples: ['*'] for all
+ tools, ['github.search_*'] for specific patterns.
+
+ description: New description for this MCP configuration.
+
+ endpoint: New target MCP server endpoint URL.
+
+ name: New name for the McpConfig. Must be unique within your account.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+
+ idempotency_key: Specify a custom idempotency key for this request
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._post(
+ f"/v1/mcp-configs/{id}",
+ body=await async_maybe_transform(
+ {
+ "allowed_tools": allowed_tools,
+ "description": description,
+ "endpoint": endpoint,
+ "name": name,
+ },
+ mcp_config_update_params.McpConfigUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ idempotency_key=idempotency_key,
+ ),
+ cast_to=McpConfigView,
+ )
+
+ def list(
+ self,
+ *,
+ id: str | Omit = omit,
+ limit: int | Omit = omit,
+ name: str | Omit = omit,
+ starting_after: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AsyncPaginator[McpConfigView, AsyncMcpConfigsCursorIDPage[McpConfigView]]:
+ """
+ [Beta] List all McpConfigs for the authenticated account.
+
+ Args:
+ id: Filter by ID.
+
+ limit: The limit of items to return. Default is 20. Max is 5000.
+
+ name: Filter by name (prefix match supported).
+
+ starting_after: Load the next page of data starting after the item with the given ID.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._get_api_list(
+ "/v1/mcp-configs",
+ page=AsyncMcpConfigsCursorIDPage[McpConfigView],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "id": id,
+ "limit": limit,
+ "name": name,
+ "starting_after": starting_after,
+ },
+ mcp_config_list_params.McpConfigListParams,
+ ),
+ ),
+ model=McpConfigView,
+ )
+
+ async def delete(
+ self,
+ id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ idempotency_key: str | None = None,
+ ) -> McpConfigView:
+ """[Beta] Delete an existing McpConfig.
+
+ This action is irreversible.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+
+ idempotency_key: Specify a custom idempotency key for this request
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._post(
+ f"/v1/mcp-configs/{id}/delete",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ idempotency_key=idempotency_key,
+ ),
+ cast_to=McpConfigView,
+ )
+
+
+class McpConfigsResourceWithRawResponse:
+ def __init__(self, mcp_configs: McpConfigsResource) -> None:
+ self._mcp_configs = mcp_configs
+
+ self.create = to_raw_response_wrapper(
+ mcp_configs.create,
+ )
+ self.retrieve = to_raw_response_wrapper(
+ mcp_configs.retrieve,
+ )
+ self.update = to_raw_response_wrapper(
+ mcp_configs.update,
+ )
+ self.list = to_raw_response_wrapper(
+ mcp_configs.list,
+ )
+ self.delete = to_raw_response_wrapper(
+ mcp_configs.delete,
+ )
+
+
+class AsyncMcpConfigsResourceWithRawResponse:
+ def __init__(self, mcp_configs: AsyncMcpConfigsResource) -> None:
+ self._mcp_configs = mcp_configs
+
+ self.create = async_to_raw_response_wrapper(
+ mcp_configs.create,
+ )
+ self.retrieve = async_to_raw_response_wrapper(
+ mcp_configs.retrieve,
+ )
+ self.update = async_to_raw_response_wrapper(
+ mcp_configs.update,
+ )
+ self.list = async_to_raw_response_wrapper(
+ mcp_configs.list,
+ )
+ self.delete = async_to_raw_response_wrapper(
+ mcp_configs.delete,
+ )
+
+
+class McpConfigsResourceWithStreamingResponse:
+ def __init__(self, mcp_configs: McpConfigsResource) -> None:
+ self._mcp_configs = mcp_configs
+
+ self.create = to_streamed_response_wrapper(
+ mcp_configs.create,
+ )
+ self.retrieve = to_streamed_response_wrapper(
+ mcp_configs.retrieve,
+ )
+ self.update = to_streamed_response_wrapper(
+ mcp_configs.update,
+ )
+ self.list = to_streamed_response_wrapper(
+ mcp_configs.list,
+ )
+ self.delete = to_streamed_response_wrapper(
+ mcp_configs.delete,
+ )
+
+
+class AsyncMcpConfigsResourceWithStreamingResponse:
+ def __init__(self, mcp_configs: AsyncMcpConfigsResource) -> None:
+ self._mcp_configs = mcp_configs
+
+ self.create = async_to_streamed_response_wrapper(
+ mcp_configs.create,
+ )
+ self.retrieve = async_to_streamed_response_wrapper(
+ mcp_configs.retrieve,
+ )
+ self.update = async_to_streamed_response_wrapper(
+ mcp_configs.update,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ mcp_configs.list,
+ )
+ self.delete = async_to_streamed_response_wrapper(
+ mcp_configs.delete,
+ )
diff --git a/src/runloop_api_client/resources/scenarios/scorers.py b/src/runloop_api_client/resources/scenarios/scorers.py
index 9e5d5e198..cdb011dc7 100644
--- a/src/runloop_api_client/resources/scenarios/scorers.py
+++ b/src/runloop_api_client/resources/scenarios/scorers.py
@@ -16,13 +16,11 @@
)
from ...pagination import SyncScenarioScorersCursorIDPage, AsyncScenarioScorersCursorIDPage
from ..._base_client import AsyncPaginator, make_request_options
-from ...types.scenarios import scorer_list_params, scorer_create_params, scorer_update_params, scorer_validate_params
-from ...types.scenario_environment_param import ScenarioEnvironmentParam
+from ...types.scenarios import scorer_list_params, scorer_create_params, scorer_update_params
from ...types.scenarios.scorer_list_response import ScorerListResponse
from ...types.scenarios.scorer_create_response import ScorerCreateResponse
from ...types.scenarios.scorer_update_response import ScorerUpdateResponse
from ...types.scenarios.scorer_retrieve_response import ScorerRetrieveResponse
-from ...types.scenarios.scorer_validate_response import ScorerValidateResponse
__all__ = ["ScorersResource", "AsyncScorersResource"]
@@ -232,59 +230,6 @@ def list(
model=ScorerListResponse,
)
- def validate(
- self,
- id: str,
- *,
- scoring_context: object,
- environment_parameters: ScenarioEnvironmentParam | Omit = omit,
- # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
- # The extra values given here take precedence over values defined on the client or passed to this method.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
- idempotency_key: str | None = None,
- ) -> ScorerValidateResponse:
- """
- Validate a scenario scorer.
-
- Args:
- scoring_context: Json context that gets passed to the custom scorer
-
- environment_parameters: The Environment in which the Scenario will run.
-
- extra_headers: Send extra headers
-
- extra_query: Add additional query parameters to the request
-
- extra_body: Add additional JSON properties to the request
-
- timeout: Override the client-level default timeout for this request, in seconds
-
- idempotency_key: Specify a custom idempotency key for this request
- """
- if not id:
- raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
- return self._post(
- f"/v1/scenarios/scorers/{id}/validate",
- body=maybe_transform(
- {
- "scoring_context": scoring_context,
- "environment_parameters": environment_parameters,
- },
- scorer_validate_params.ScorerValidateParams,
- ),
- options=make_request_options(
- extra_headers=extra_headers,
- extra_query=extra_query,
- extra_body=extra_body,
- timeout=timeout,
- idempotency_key=idempotency_key,
- ),
- cast_to=ScorerValidateResponse,
- )
-
class AsyncScorersResource(AsyncAPIResource):
@cached_property
@@ -491,59 +436,6 @@ def list(
model=ScorerListResponse,
)
- async def validate(
- self,
- id: str,
- *,
- scoring_context: object,
- environment_parameters: ScenarioEnvironmentParam | Omit = omit,
- # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
- # The extra values given here take precedence over values defined on the client or passed to this method.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
- idempotency_key: str | None = None,
- ) -> ScorerValidateResponse:
- """
- Validate a scenario scorer.
-
- Args:
- scoring_context: Json context that gets passed to the custom scorer
-
- environment_parameters: The Environment in which the Scenario will run.
-
- extra_headers: Send extra headers
-
- extra_query: Add additional query parameters to the request
-
- extra_body: Add additional JSON properties to the request
-
- timeout: Override the client-level default timeout for this request, in seconds
-
- idempotency_key: Specify a custom idempotency key for this request
- """
- if not id:
- raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
- return await self._post(
- f"/v1/scenarios/scorers/{id}/validate",
- body=await async_maybe_transform(
- {
- "scoring_context": scoring_context,
- "environment_parameters": environment_parameters,
- },
- scorer_validate_params.ScorerValidateParams,
- ),
- options=make_request_options(
- extra_headers=extra_headers,
- extra_query=extra_query,
- extra_body=extra_body,
- timeout=timeout,
- idempotency_key=idempotency_key,
- ),
- cast_to=ScorerValidateResponse,
- )
-
class ScorersResourceWithRawResponse:
def __init__(self, scorers: ScorersResource) -> None:
@@ -561,9 +453,6 @@ def __init__(self, scorers: ScorersResource) -> None:
self.list = to_raw_response_wrapper(
scorers.list,
)
- self.validate = to_raw_response_wrapper(
- scorers.validate,
- )
class AsyncScorersResourceWithRawResponse:
@@ -582,9 +471,6 @@ def __init__(self, scorers: AsyncScorersResource) -> None:
self.list = async_to_raw_response_wrapper(
scorers.list,
)
- self.validate = async_to_raw_response_wrapper(
- scorers.validate,
- )
class ScorersResourceWithStreamingResponse:
@@ -603,9 +489,6 @@ def __init__(self, scorers: ScorersResource) -> None:
self.list = to_streamed_response_wrapper(
scorers.list,
)
- self.validate = to_streamed_response_wrapper(
- scorers.validate,
- )
class AsyncScorersResourceWithStreamingResponse:
@@ -624,6 +507,3 @@ def __init__(self, scorers: AsyncScorersResource) -> None:
self.list = async_to_streamed_response_wrapper(
scorers.list,
)
- self.validate = async_to_streamed_response_wrapper(
- scorers.validate,
- )
diff --git a/src/runloop_api_client/sdk/_types.py b/src/runloop_api_client/sdk/_types.py
index 9eb0526bc..9e7f8780e 100644
--- a/src/runloop_api_client/sdk/_types.py
+++ b/src/runloop_api_client/sdk/_types.py
@@ -37,7 +37,7 @@
from .._types import Body, Query, Headers, Timeout, NotGiven
from ..lib.polling import PollingConfig
from ..types.devboxes import DiskSnapshotListParams, DiskSnapshotUpdateParams
-from ..types.scenarios import ScorerListParams, ScorerCreateParams, ScorerUpdateParams, ScorerValidateParams
+from ..types.scenarios import ScorerListParams, ScorerCreateParams, ScorerUpdateParams
from ..types.devbox_create_params import DevboxBaseCreateParams
from ..types.scenario_start_run_params import ScenarioStartRunBaseParams
from ..types.benchmark_start_run_params import BenchmarkSelfStartRunParams
@@ -181,10 +181,6 @@ class SDKScorerUpdateParams(ScorerUpdateParams, LongRequestOptions):
pass
-class SDKScorerValidateParams(ScorerValidateParams, LongRequestOptions):
- pass
-
-
class SDKAgentCreateParams(AgentCreateParams, LongRequestOptions):
pass
diff --git a/src/runloop_api_client/sdk/async_scorer.py b/src/runloop_api_client/sdk/async_scorer.py
index 91ced0c38..12df27033 100644
--- a/src/runloop_api_client/sdk/async_scorer.py
+++ b/src/runloop_api_client/sdk/async_scorer.py
@@ -7,10 +7,9 @@
from ._types import (
BaseRequestOptions,
SDKScorerUpdateParams,
- SDKScorerValidateParams,
)
from .._client import AsyncRunloop
-from ..types.scenarios import ScorerUpdateResponse, ScorerRetrieveResponse, ScorerValidateResponse
+from ..types.scenarios import ScorerUpdateResponse, ScorerRetrieveResponse
class AsyncScorer:
@@ -66,12 +65,3 @@ async def update(self, **params: Unpack[SDKScorerUpdateParams]) -> ScorerUpdateR
:rtype: ScorerUpdateResponse
"""
return await self._client.scenarios.scorers.update(self._id, **params)
-
- async def validate(self, **params: Unpack[SDKScorerValidateParams]) -> ScorerValidateResponse:
- """Run the scorer against the provided context and return the result.
-
- :param params: See :typeddict:`~runloop_api_client.sdk._types.SDKScorerValidateParams` for available parameters
- :return: Validation result with score
- :rtype: ScorerValidateResponse
- """
- return await self._client.scenarios.scorers.validate(self._id, **params)
diff --git a/src/runloop_api_client/sdk/scorer.py b/src/runloop_api_client/sdk/scorer.py
index 8df57ac05..86c31cd05 100644
--- a/src/runloop_api_client/sdk/scorer.py
+++ b/src/runloop_api_client/sdk/scorer.py
@@ -7,10 +7,9 @@
from ._types import (
BaseRequestOptions,
SDKScorerUpdateParams,
- SDKScorerValidateParams,
)
from .._client import Runloop
-from ..types.scenarios import ScorerUpdateResponse, ScorerRetrieveResponse, ScorerValidateResponse
+from ..types.scenarios import ScorerUpdateResponse, ScorerRetrieveResponse
class Scorer:
@@ -66,12 +65,3 @@ def update(self, **params: Unpack[SDKScorerUpdateParams]) -> ScorerUpdateRespons
:rtype: ScorerUpdateResponse
"""
return self._client.scenarios.scorers.update(self._id, **params)
-
- def validate(self, **params: Unpack[SDKScorerValidateParams]) -> ScorerValidateResponse:
- """Run the scorer against the provided context and return the result.
-
- :param params: See :typeddict:`~runloop_api_client.sdk._types.SDKScorerValidateParams` for available parameters
- :return: Validation result with score
- :rtype: ScorerValidateResponse
- """
- return self._client.scenarios.scorers.validate(self._id, **params)
diff --git a/src/runloop_api_client/types/__init__.py b/src/runloop_api_client/types/__init__.py
index 8fb72317f..e6543484f 100644
--- a/src/runloop_api_client/types/__init__.py
+++ b/src/runloop_api_client/types/__init__.py
@@ -22,6 +22,7 @@
from .benchmark_view import BenchmarkView as BenchmarkView
from .blueprint_view import BlueprintView as BlueprintView
from .agent_list_view import AgentListView as AgentListView
+from .mcp_config_view import McpConfigView as McpConfigView
from .devbox_list_view import DevboxListView as DevboxListView
from .object_list_view import ObjectListView as ObjectListView
from .scoring_contract import ScoringContract as ScoringContract
@@ -44,6 +45,7 @@
from .devbox_create_params import DevboxCreateParams as DevboxCreateParams
from .devbox_snapshot_view import DevboxSnapshotView as DevboxSnapshotView
from .devbox_update_params import DevboxUpdateParams as DevboxUpdateParams
+from .mcp_config_list_view import McpConfigListView as McpConfigListView
from .object_create_params import ObjectCreateParams as ObjectCreateParams
from .scenario_environment import ScenarioEnvironment as ScenarioEnvironment
from .scenario_list_params import ScenarioListParams as ScenarioListParams
@@ -53,6 +55,7 @@
from .blueprint_list_params import BlueprintListParams as BlueprintListParams
from .devbox_execute_params import DevboxExecuteParams as DevboxExecuteParams
from .blueprint_preview_view import BlueprintPreviewView as BlueprintPreviewView
+from .mcp_config_list_params import McpConfigListParams as McpConfigListParams
from .object_download_params import ObjectDownloadParams as ObjectDownloadParams
from .repository_list_params import RepositoryListParams as RepositoryListParams
from .scenario_create_params import ScenarioCreateParams as ScenarioCreateParams
@@ -68,6 +71,8 @@
from .inspection_source_param import InspectionSourceParam as InspectionSourceParam
from .blueprint_preview_params import BlueprintPreviewParams as BlueprintPreviewParams
from .gateway_config_list_view import GatewayConfigListView as GatewayConfigListView
+from .mcp_config_create_params import McpConfigCreateParams as McpConfigCreateParams
+from .mcp_config_update_params import McpConfigUpdateParams as McpConfigUpdateParams
from .network_policy_list_view import NetworkPolicyListView as NetworkPolicyListView
from .object_download_url_view import ObjectDownloadURLView as ObjectDownloadURLView
from .repository_create_params import RepositoryCreateParams as RepositoryCreateParams
@@ -84,6 +89,7 @@
from .benchmark_start_run_params import BenchmarkStartRunParams as BenchmarkStartRunParams
from .blueprint_build_parameters import BlueprintBuildParameters as BlueprintBuildParameters
from .devbox_execute_sync_params import DevboxExecuteSyncParams as DevboxExecuteSyncParams
+from .devbox_resource_usage_view import DevboxResourceUsageView as DevboxResourceUsageView
from .gateway_config_list_params import GatewayConfigListParams as GatewayConfigListParams
from .input_context_update_param import InputContextUpdateParam as InputContextUpdateParam
from .network_policy_list_params import NetworkPolicyListParams as NetworkPolicyListParams
diff --git a/src/runloop_api_client/types/devbox_create_params.py b/src/runloop_api_client/types/devbox_create_params.py
index b31efdfe7..ae2f0db59 100644
--- a/src/runloop_api_client/types/devbox_create_params.py
+++ b/src/runloop_api_client/types/devbox_create_params.py
@@ -9,7 +9,7 @@
from .shared_params.launch_parameters import LaunchParameters
from .shared_params.code_mount_parameters import CodeMountParameters
-__all__ = ["DevboxCreateParams", "Gateways", "Tunnel"]
+__all__ = ["DevboxCreateParams", "Gateways", "Mcp", "Tunnel"]
# We split up the original DevboxCreateParams into two nested types to enable us to
# omit blueprint_id, blueprint_name, and snapshot_id when we unpack the TypedDict
@@ -49,6 +49,14 @@ class DevboxBaseCreateParams(TypedDict, total=False):
launch_parameters: Optional[LaunchParameters]
"""Parameters to configure the resources and launch time behavior of the Devbox."""
+ mcp: Optional[Iterable[Mcp]]
+ """[Beta] (Optional) MCP specifications for MCP server access.
+
+ Each spec links an MCP config to a secret. The devbox will receive environment
+ variables (RL_MCP_URL, RL_MCP_TOKEN) for accessing MCP servers through the MCP
+ hub. Example: [{'mcp_config': 'github-readonly', 'secret': 'MY_GITHUB_TOKEN'}]
+ """
+
metadata: Optional[Dict[str, str]]
"""User defined metadata to attach to the devbox for organization."""
@@ -114,6 +122,18 @@ class Gateways(TypedDict, total=False):
"""The secret containing the credential. Can be a secret ID or name."""
+class Mcp(TypedDict, total=False):
+ """
+ [Beta] McpSpec links an MCP configuration to a secret for MCP server access in a devbox. The MCP hub will proxy requests to upstream MCP servers using the specified credential, with tool-level access control based on the MCP config's allowed_tools.
+ """
+
+ mcp_config: Required[str]
+ """The MCP config to use. Can be an MCP config ID (mcp_xxx) or name."""
+
+ secret: Required[str]
+ """The secret containing the MCP server credential. Can be a secret ID or name."""
+
+
class Tunnel(TypedDict, total=False):
"""(Optional) Configuration for creating a V2 tunnel at Devbox launch time.
diff --git a/src/runloop_api_client/types/devbox_resource_usage_view.py b/src/runloop_api_client/types/devbox_resource_usage_view.py
new file mode 100644
index 000000000..fe5c83a57
--- /dev/null
+++ b/src/runloop_api_client/types/devbox_resource_usage_view.py
@@ -0,0 +1,48 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+
+from .._models import BaseModel
+
+__all__ = ["DevboxResourceUsageView"]
+
+
+class DevboxResourceUsageView(BaseModel):
+ id: str
+ """The devbox ID."""
+
+ disk_gb_seconds: int
+ """Disk usage in GB-seconds (total_elapsed_seconds multiplied by disk size in GB).
+
+ Disk is billed for elapsed time since storage is consumed even when suspended.
+ """
+
+ memory_gb_seconds: int
+ """Memory usage in GB-seconds (total_active_seconds multiplied by memory in GB)."""
+
+ start_time_ms: int
+ """The devbox creation time in milliseconds since epoch."""
+
+ status: str
+ """The current status of the devbox."""
+
+ total_active_seconds: int
+ """
+ Total time in seconds the devbox was actively running (excludes time spent
+ suspended).
+ """
+
+ total_elapsed_seconds: int
+ """
+ Total elapsed time in seconds from devbox creation to now (or end time if
+ terminated). Includes all time regardless of devbox state.
+ """
+
+ vcpu_seconds: int
+ """
+ vCPU usage in vCPU-seconds (total_active_seconds multiplied by the number of
+ vCPUs).
+ """
+
+ end_time_ms: Optional[int] = None
+ """The devbox end time in milliseconds since epoch, or null if still running."""
diff --git a/src/runloop_api_client/types/mcp_config_create_params.py b/src/runloop_api_client/types/mcp_config_create_params.py
new file mode 100644
index 000000000..4a2dcd9e2
--- /dev/null
+++ b/src/runloop_api_client/types/mcp_config_create_params.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Required, TypedDict
+
+from .._types import SequenceNotStr
+
+__all__ = ["McpConfigCreateParams"]
+
+
+class McpConfigCreateParams(TypedDict, total=False):
+ allowed_tools: Required[SequenceNotStr[str]]
+ """Glob patterns specifying which tools are allowed from this MCP server.
+
+ Examples: ['*'] for all tools, ['github.search_*', 'github.get_*'] for specific
+ patterns.
+ """
+
+ endpoint: Required[str]
+ """The target MCP server endpoint URL (e.g., 'https://mcp.example.com')."""
+
+ name: Required[str]
+ """The human-readable name for the McpConfig.
+
+ Must be unique within your account. The first segment before '-' is used as the
+ service name for tool routing (e.g., 'github-readonly' uses 'github' as the
+ service name).
+ """
+
+ description: Optional[str]
+ """Optional description for this MCP configuration."""
diff --git a/src/runloop_api_client/types/mcp_config_list_params.py b/src/runloop_api_client/types/mcp_config_list_params.py
new file mode 100644
index 000000000..8b786ba13
--- /dev/null
+++ b/src/runloop_api_client/types/mcp_config_list_params.py
@@ -0,0 +1,21 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["McpConfigListParams"]
+
+
+class McpConfigListParams(TypedDict, total=False):
+ id: str
+ """Filter by ID."""
+
+ limit: int
+ """The limit of items to return. Default is 20. Max is 5000."""
+
+ name: str
+ """Filter by name (prefix match supported)."""
+
+ starting_after: str
+ """Load the next page of data starting after the item with the given ID."""
diff --git a/src/runloop_api_client/types/mcp_config_list_view.py b/src/runloop_api_client/types/mcp_config_list_view.py
new file mode 100644
index 000000000..72075a255
--- /dev/null
+++ b/src/runloop_api_client/types/mcp_config_list_view.py
@@ -0,0 +1,21 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List
+
+from .._models import BaseModel
+from .mcp_config_view import McpConfigView
+
+__all__ = ["McpConfigListView"]
+
+
+class McpConfigListView(BaseModel):
+ """A paginated list of McpConfigs."""
+
+ has_more: bool
+ """Whether there are more results available beyond this page."""
+
+ mcp_configs: List[McpConfigView]
+ """The list of McpConfigs."""
+
+ total_count: int
+ """Total count of McpConfigs that match the query."""
diff --git a/src/runloop_api_client/types/mcp_config_update_params.py b/src/runloop_api_client/types/mcp_config_update_params.py
new file mode 100644
index 000000000..234ca939f
--- /dev/null
+++ b/src/runloop_api_client/types/mcp_config_update_params.py
@@ -0,0 +1,27 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import TypedDict
+
+from .._types import SequenceNotStr
+
+__all__ = ["McpConfigUpdateParams"]
+
+
+class McpConfigUpdateParams(TypedDict, total=False):
+ allowed_tools: Optional[SequenceNotStr[str]]
+ """New glob patterns specifying which tools are allowed.
+
+ Examples: ['*'] for all tools, ['github.search_*'] for specific patterns.
+ """
+
+ description: Optional[str]
+ """New description for this MCP configuration."""
+
+ endpoint: Optional[str]
+ """New target MCP server endpoint URL."""
+
+ name: Optional[str]
+ """New name for the McpConfig. Must be unique within your account."""
diff --git a/src/runloop_api_client/types/mcp_config_view.py b/src/runloop_api_client/types/mcp_config_view.py
new file mode 100644
index 000000000..988537a59
--- /dev/null
+++ b/src/runloop_api_client/types/mcp_config_view.py
@@ -0,0 +1,34 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+
+from .._models import BaseModel
+
+__all__ = ["McpConfigView"]
+
+
+class McpConfigView(BaseModel):
+ """
+ An McpConfig defines a configuration for connecting to an upstream MCP (Model Context Protocol) server. It specifies the target endpoint and which tools are allowed.
+ """
+
+ id: str
+ """The unique identifier of the McpConfig."""
+
+ allowed_tools: List[str]
+ """
+ Glob patterns specifying which tools are allowed from this MCP server (e.g.,
+ ['github.search_*', 'github.get_*'] or ['*'] for all tools).
+ """
+
+ create_time_ms: int
+ """Creation time of the McpConfig (Unix timestamp in milliseconds)."""
+
+ endpoint: str
+ """The target MCP server endpoint URL (e.g., 'https://mcp.example.com')."""
+
+ name: str
+ """The human-readable name of the McpConfig. Unique per account."""
+
+ description: Optional[str] = None
+ """Optional description for this MCP configuration."""
diff --git a/src/runloop_api_client/types/scenarios/__init__.py b/src/runloop_api_client/types/scenarios/__init__.py
index d25c85c4e..97efebd9d 100644
--- a/src/runloop_api_client/types/scenarios/__init__.py
+++ b/src/runloop_api_client/types/scenarios/__init__.py
@@ -9,6 +9,4 @@
from .scorer_update_params import ScorerUpdateParams as ScorerUpdateParams
from .scorer_create_response import ScorerCreateResponse as ScorerCreateResponse
from .scorer_update_response import ScorerUpdateResponse as ScorerUpdateResponse
-from .scorer_validate_params import ScorerValidateParams as ScorerValidateParams
from .scorer_retrieve_response import ScorerRetrieveResponse as ScorerRetrieveResponse
-from .scorer_validate_response import ScorerValidateResponse as ScorerValidateResponse
diff --git a/src/runloop_api_client/types/scenarios/scorer_validate_params.py b/src/runloop_api_client/types/scenarios/scorer_validate_params.py
deleted file mode 100644
index 41215ea2d..000000000
--- a/src/runloop_api_client/types/scenarios/scorer_validate_params.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from __future__ import annotations
-
-from typing_extensions import Required, TypedDict
-
-from ..scenario_environment_param import ScenarioEnvironmentParam
-
-__all__ = ["ScorerValidateParams"]
-
-
-class ScorerValidateParams(TypedDict, total=False):
- scoring_context: Required[object]
- """Json context that gets passed to the custom scorer"""
-
- environment_parameters: ScenarioEnvironmentParam
- """The Environment in which the Scenario will run."""
diff --git a/src/runloop_api_client/types/scenarios/scorer_validate_response.py b/src/runloop_api_client/types/scenarios/scorer_validate_response.py
deleted file mode 100644
index 6c2755e6c..000000000
--- a/src/runloop_api_client/types/scenarios/scorer_validate_response.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from typing import Optional
-
-from ..._models import BaseModel
-from ..scenario_environment import ScenarioEnvironment
-from ..scoring_function_result_view import ScoringFunctionResultView
-
-__all__ = ["ScorerValidateResponse"]
-
-
-class ScorerValidateResponse(BaseModel):
- name: str
- """Name of the custom scorer."""
-
- scoring_context: object
- """Json context that gets passed to the custom scorer"""
-
- scoring_result: ScoringFunctionResultView
- """Result of the scoring function."""
-
- environment_parameters: Optional[ScenarioEnvironment] = None
- """The Environment in which the Scenario will run."""
diff --git a/tests/api_resources/scenarios/test_scorers.py b/tests/api_resources/scenarios/test_scorers.py
index 73be902fd..359e0dcc7 100644
--- a/tests/api_resources/scenarios/test_scorers.py
+++ b/tests/api_resources/scenarios/test_scorers.py
@@ -15,7 +15,6 @@
ScorerCreateResponse,
ScorerUpdateResponse,
ScorerRetrieveResponse,
- ScorerValidateResponse,
)
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -175,81 +174,6 @@ def test_streaming_response_list(self, client: Runloop) -> None:
assert cast(Any, response.is_closed) is True
- @parametrize
- def test_method_validate(self, client: Runloop) -> None:
- scorer = client.scenarios.scorers.validate(
- id="id",
- scoring_context={},
- )
- assert_matches_type(ScorerValidateResponse, scorer, path=["response"])
-
- @parametrize
- def test_method_validate_with_all_params(self, client: Runloop) -> None:
- scorer = client.scenarios.scorers.validate(
- id="id",
- scoring_context={},
- environment_parameters={
- "blueprint_id": "blueprint_id",
- "launch_parameters": {
- "after_idle": {
- "idle_time_seconds": 0,
- "on_idle": "shutdown",
- },
- "architecture": "x86_64",
- "available_ports": [0],
- "custom_cpu_cores": 0,
- "custom_disk_size": 0,
- "custom_gb_memory": 0,
- "keep_alive_time_seconds": 0,
- "launch_commands": ["string"],
- "network_policy_id": "network_policy_id",
- "required_services": ["string"],
- "resource_size_request": "X_SMALL",
- "user_parameters": {
- "uid": 0,
- "username": "username",
- },
- },
- "snapshot_id": "snapshot_id",
- "working_directory": "working_directory",
- },
- )
- assert_matches_type(ScorerValidateResponse, scorer, path=["response"])
-
- @parametrize
- def test_raw_response_validate(self, client: Runloop) -> None:
- response = client.scenarios.scorers.with_raw_response.validate(
- id="id",
- scoring_context={},
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- scorer = response.parse()
- assert_matches_type(ScorerValidateResponse, scorer, path=["response"])
-
- @parametrize
- def test_streaming_response_validate(self, client: Runloop) -> None:
- with client.scenarios.scorers.with_streaming_response.validate(
- id="id",
- scoring_context={},
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- scorer = response.parse()
- assert_matches_type(ScorerValidateResponse, scorer, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
- @parametrize
- def test_path_params_validate(self, client: Runloop) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
- client.scenarios.scorers.with_raw_response.validate(
- id="",
- scoring_context={},
- )
-
class TestAsyncScorers:
parametrize = pytest.mark.parametrize(
@@ -406,78 +330,3 @@ async def test_streaming_response_list(self, async_client: AsyncRunloop) -> None
assert_matches_type(AsyncScenarioScorersCursorIDPage[ScorerListResponse], scorer, path=["response"])
assert cast(Any, response.is_closed) is True
-
- @parametrize
- async def test_method_validate(self, async_client: AsyncRunloop) -> None:
- scorer = await async_client.scenarios.scorers.validate(
- id="id",
- scoring_context={},
- )
- assert_matches_type(ScorerValidateResponse, scorer, path=["response"])
-
- @parametrize
- async def test_method_validate_with_all_params(self, async_client: AsyncRunloop) -> None:
- scorer = await async_client.scenarios.scorers.validate(
- id="id",
- scoring_context={},
- environment_parameters={
- "blueprint_id": "blueprint_id",
- "launch_parameters": {
- "after_idle": {
- "idle_time_seconds": 0,
- "on_idle": "shutdown",
- },
- "architecture": "x86_64",
- "available_ports": [0],
- "custom_cpu_cores": 0,
- "custom_disk_size": 0,
- "custom_gb_memory": 0,
- "keep_alive_time_seconds": 0,
- "launch_commands": ["string"],
- "network_policy_id": "network_policy_id",
- "required_services": ["string"],
- "resource_size_request": "X_SMALL",
- "user_parameters": {
- "uid": 0,
- "username": "username",
- },
- },
- "snapshot_id": "snapshot_id",
- "working_directory": "working_directory",
- },
- )
- assert_matches_type(ScorerValidateResponse, scorer, path=["response"])
-
- @parametrize
- async def test_raw_response_validate(self, async_client: AsyncRunloop) -> None:
- response = await async_client.scenarios.scorers.with_raw_response.validate(
- id="id",
- scoring_context={},
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- scorer = await response.parse()
- assert_matches_type(ScorerValidateResponse, scorer, path=["response"])
-
- @parametrize
- async def test_streaming_response_validate(self, async_client: AsyncRunloop) -> None:
- async with async_client.scenarios.scorers.with_streaming_response.validate(
- id="id",
- scoring_context={},
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- scorer = await response.parse()
- assert_matches_type(ScorerValidateResponse, scorer, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
- @parametrize
- async def test_path_params_validate(self, async_client: AsyncRunloop) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
- await async_client.scenarios.scorers.with_raw_response.validate(
- id="",
- scoring_context={},
- )
diff --git a/tests/api_resources/test_devboxes.py b/tests/api_resources/test_devboxes.py
index fcfc5c1de..eef022a71 100644
--- a/tests/api_resources/test_devboxes.py
+++ b/tests/api_resources/test_devboxes.py
@@ -17,6 +17,7 @@
TunnelView,
DevboxTunnelView,
DevboxSnapshotView,
+ DevboxResourceUsageView,
DevboxExecutionDetailView,
DevboxCreateSSHKeyResponse,
DevboxAsyncExecutionDetailView,
@@ -92,6 +93,12 @@ def test_method_create_with_all_params(self, client: Runloop) -> None:
"username": "username",
},
},
+ mcp=[
+ {
+ "mcp_config": "mcp_config",
+ "secret": "secret",
+ }
+ ],
metadata={"foo": "string"},
mounts=[
{
@@ -845,6 +852,44 @@ def test_path_params_resume(self, client: Runloop) -> None:
"",
)
+ @parametrize
+ def test_method_retrieve_resource_usage(self, client: Runloop) -> None:
+ devbox = client.devboxes.retrieve_resource_usage(
+ "id",
+ )
+ assert_matches_type(DevboxResourceUsageView, devbox, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve_resource_usage(self, client: Runloop) -> None:
+ response = client.devboxes.with_raw_response.retrieve_resource_usage(
+ "id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ devbox = response.parse()
+ assert_matches_type(DevboxResourceUsageView, devbox, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve_resource_usage(self, client: Runloop) -> None:
+ with client.devboxes.with_streaming_response.retrieve_resource_usage(
+ "id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ devbox = response.parse()
+ assert_matches_type(DevboxResourceUsageView, devbox, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve_resource_usage(self, client: Runloop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.devboxes.with_raw_response.retrieve_resource_usage(
+ "",
+ )
+
@parametrize
def test_method_shutdown(self, client: Runloop) -> None:
devbox = client.devboxes.shutdown(
@@ -1704,6 +1749,12 @@ async def test_method_create_with_all_params(self, async_client: AsyncRunloop) -
"username": "username",
},
},
+ mcp=[
+ {
+ "mcp_config": "mcp_config",
+ "secret": "secret",
+ }
+ ],
metadata={"foo": "string"},
mounts=[
{
@@ -2457,6 +2508,44 @@ async def test_path_params_resume(self, async_client: AsyncRunloop) -> None:
"",
)
+ @parametrize
+ async def test_method_retrieve_resource_usage(self, async_client: AsyncRunloop) -> None:
+ devbox = await async_client.devboxes.retrieve_resource_usage(
+ "id",
+ )
+ assert_matches_type(DevboxResourceUsageView, devbox, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve_resource_usage(self, async_client: AsyncRunloop) -> None:
+ response = await async_client.devboxes.with_raw_response.retrieve_resource_usage(
+ "id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ devbox = await response.parse()
+ assert_matches_type(DevboxResourceUsageView, devbox, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve_resource_usage(self, async_client: AsyncRunloop) -> None:
+ async with async_client.devboxes.with_streaming_response.retrieve_resource_usage(
+ "id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ devbox = await response.parse()
+ assert_matches_type(DevboxResourceUsageView, devbox, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve_resource_usage(self, async_client: AsyncRunloop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.devboxes.with_raw_response.retrieve_resource_usage(
+ "",
+ )
+
@parametrize
async def test_method_shutdown(self, async_client: AsyncRunloop) -> None:
devbox = await async_client.devboxes.shutdown(
diff --git a/tests/api_resources/test_mcp_configs.py b/tests/api_resources/test_mcp_configs.py
new file mode 100644
index 000000000..0d3135668
--- /dev/null
+++ b/tests/api_resources/test_mcp_configs.py
@@ -0,0 +1,441 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from tests.utils import assert_matches_type
+from runloop_api_client import Runloop, AsyncRunloop
+from runloop_api_client.types import (
+ McpConfigView,
+)
+from runloop_api_client.pagination import SyncMcpConfigsCursorIDPage, AsyncMcpConfigsCursorIDPage
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestMcpConfigs:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_create(self, client: Runloop) -> None:
+ mcp_config = client.mcp_configs.create(
+ allowed_tools=["string"],
+ endpoint="endpoint",
+ name="name",
+ )
+ assert_matches_type(McpConfigView, mcp_config, path=["response"])
+
+ @parametrize
+ def test_method_create_with_all_params(self, client: Runloop) -> None:
+ mcp_config = client.mcp_configs.create(
+ allowed_tools=["string"],
+ endpoint="endpoint",
+ name="name",
+ description="description",
+ )
+ assert_matches_type(McpConfigView, mcp_config, path=["response"])
+
+ @parametrize
+ def test_raw_response_create(self, client: Runloop) -> None:
+ response = client.mcp_configs.with_raw_response.create(
+ allowed_tools=["string"],
+ endpoint="endpoint",
+ name="name",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ mcp_config = response.parse()
+ assert_matches_type(McpConfigView, mcp_config, path=["response"])
+
+ @parametrize
+ def test_streaming_response_create(self, client: Runloop) -> None:
+ with client.mcp_configs.with_streaming_response.create(
+ allowed_tools=["string"],
+ endpoint="endpoint",
+ name="name",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ mcp_config = response.parse()
+ assert_matches_type(McpConfigView, mcp_config, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_retrieve(self, client: Runloop) -> None:
+ mcp_config = client.mcp_configs.retrieve(
+ "id",
+ )
+ assert_matches_type(McpConfigView, mcp_config, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: Runloop) -> None:
+ response = client.mcp_configs.with_raw_response.retrieve(
+ "id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ mcp_config = response.parse()
+ assert_matches_type(McpConfigView, mcp_config, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: Runloop) -> None:
+ with client.mcp_configs.with_streaming_response.retrieve(
+ "id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ mcp_config = response.parse()
+ assert_matches_type(McpConfigView, mcp_config, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve(self, client: Runloop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.mcp_configs.with_raw_response.retrieve(
+ "",
+ )
+
+ @parametrize
+ def test_method_update(self, client: Runloop) -> None:
+ mcp_config = client.mcp_configs.update(
+ id="id",
+ )
+ assert_matches_type(McpConfigView, mcp_config, path=["response"])
+
+ @parametrize
+ def test_method_update_with_all_params(self, client: Runloop) -> None:
+ mcp_config = client.mcp_configs.update(
+ id="id",
+ allowed_tools=["string"],
+ description="description",
+ endpoint="endpoint",
+ name="name",
+ )
+ assert_matches_type(McpConfigView, mcp_config, path=["response"])
+
+ @parametrize
+ def test_raw_response_update(self, client: Runloop) -> None:
+ response = client.mcp_configs.with_raw_response.update(
+ id="id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ mcp_config = response.parse()
+ assert_matches_type(McpConfigView, mcp_config, path=["response"])
+
+ @parametrize
+ def test_streaming_response_update(self, client: Runloop) -> None:
+ with client.mcp_configs.with_streaming_response.update(
+ id="id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ mcp_config = response.parse()
+ assert_matches_type(McpConfigView, mcp_config, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_update(self, client: Runloop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.mcp_configs.with_raw_response.update(
+ id="",
+ )
+
+ @parametrize
+ def test_method_list(self, client: Runloop) -> None:
+ mcp_config = client.mcp_configs.list()
+ assert_matches_type(SyncMcpConfigsCursorIDPage[McpConfigView], mcp_config, path=["response"])
+
+ @parametrize
+ def test_method_list_with_all_params(self, client: Runloop) -> None:
+ mcp_config = client.mcp_configs.list(
+ id="id",
+ limit=0,
+ name="name",
+ starting_after="starting_after",
+ )
+ assert_matches_type(SyncMcpConfigsCursorIDPage[McpConfigView], mcp_config, path=["response"])
+
+ @parametrize
+ def test_raw_response_list(self, client: Runloop) -> None:
+ response = client.mcp_configs.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ mcp_config = response.parse()
+ assert_matches_type(SyncMcpConfigsCursorIDPage[McpConfigView], mcp_config, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list(self, client: Runloop) -> None:
+ with client.mcp_configs.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ mcp_config = response.parse()
+ assert_matches_type(SyncMcpConfigsCursorIDPage[McpConfigView], mcp_config, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_delete(self, client: Runloop) -> None:
+ mcp_config = client.mcp_configs.delete(
+ "id",
+ )
+ assert_matches_type(McpConfigView, mcp_config, path=["response"])
+
+ @parametrize
+ def test_raw_response_delete(self, client: Runloop) -> None:
+ response = client.mcp_configs.with_raw_response.delete(
+ "id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ mcp_config = response.parse()
+ assert_matches_type(McpConfigView, mcp_config, path=["response"])
+
+ @parametrize
+ def test_streaming_response_delete(self, client: Runloop) -> None:
+ with client.mcp_configs.with_streaming_response.delete(
+ "id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ mcp_config = response.parse()
+ assert_matches_type(McpConfigView, mcp_config, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_delete(self, client: Runloop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.mcp_configs.with_raw_response.delete(
+ "",
+ )
+
+
+class TestAsyncMcpConfigs:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_create(self, async_client: AsyncRunloop) -> None:
+ mcp_config = await async_client.mcp_configs.create(
+ allowed_tools=["string"],
+ endpoint="endpoint",
+ name="name",
+ )
+ assert_matches_type(McpConfigView, mcp_config, path=["response"])
+
+ @parametrize
+ async def test_method_create_with_all_params(self, async_client: AsyncRunloop) -> None:
+ mcp_config = await async_client.mcp_configs.create(
+ allowed_tools=["string"],
+ endpoint="endpoint",
+ name="name",
+ description="description",
+ )
+ assert_matches_type(McpConfigView, mcp_config, path=["response"])
+
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncRunloop) -> None:
+ response = await async_client.mcp_configs.with_raw_response.create(
+ allowed_tools=["string"],
+ endpoint="endpoint",
+ name="name",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ mcp_config = await response.parse()
+ assert_matches_type(McpConfigView, mcp_config, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncRunloop) -> None:
+ async with async_client.mcp_configs.with_streaming_response.create(
+ allowed_tools=["string"],
+ endpoint="endpoint",
+ name="name",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ mcp_config = await response.parse()
+ assert_matches_type(McpConfigView, mcp_config, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncRunloop) -> None:
+ mcp_config = await async_client.mcp_configs.retrieve(
+ "id",
+ )
+ assert_matches_type(McpConfigView, mcp_config, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncRunloop) -> None:
+ response = await async_client.mcp_configs.with_raw_response.retrieve(
+ "id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ mcp_config = await response.parse()
+ assert_matches_type(McpConfigView, mcp_config, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncRunloop) -> None:
+ async with async_client.mcp_configs.with_streaming_response.retrieve(
+ "id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ mcp_config = await response.parse()
+ assert_matches_type(McpConfigView, mcp_config, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncRunloop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.mcp_configs.with_raw_response.retrieve(
+ "",
+ )
+
+ @parametrize
+ async def test_method_update(self, async_client: AsyncRunloop) -> None:
+ mcp_config = await async_client.mcp_configs.update(
+ id="id",
+ )
+ assert_matches_type(McpConfigView, mcp_config, path=["response"])
+
+ @parametrize
+ async def test_method_update_with_all_params(self, async_client: AsyncRunloop) -> None:
+ mcp_config = await async_client.mcp_configs.update(
+ id="id",
+ allowed_tools=["string"],
+ description="description",
+ endpoint="endpoint",
+ name="name",
+ )
+ assert_matches_type(McpConfigView, mcp_config, path=["response"])
+
+ @parametrize
+ async def test_raw_response_update(self, async_client: AsyncRunloop) -> None:
+ response = await async_client.mcp_configs.with_raw_response.update(
+ id="id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ mcp_config = await response.parse()
+ assert_matches_type(McpConfigView, mcp_config, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_update(self, async_client: AsyncRunloop) -> None:
+ async with async_client.mcp_configs.with_streaming_response.update(
+ id="id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ mcp_config = await response.parse()
+ assert_matches_type(McpConfigView, mcp_config, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_update(self, async_client: AsyncRunloop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.mcp_configs.with_raw_response.update(
+ id="",
+ )
+
+ @parametrize
+ async def test_method_list(self, async_client: AsyncRunloop) -> None:
+ mcp_config = await async_client.mcp_configs.list()
+ assert_matches_type(AsyncMcpConfigsCursorIDPage[McpConfigView], mcp_config, path=["response"])
+
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncRunloop) -> None:
+ mcp_config = await async_client.mcp_configs.list(
+ id="id",
+ limit=0,
+ name="name",
+ starting_after="starting_after",
+ )
+ assert_matches_type(AsyncMcpConfigsCursorIDPage[McpConfigView], mcp_config, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncRunloop) -> None:
+ response = await async_client.mcp_configs.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ mcp_config = await response.parse()
+ assert_matches_type(AsyncMcpConfigsCursorIDPage[McpConfigView], mcp_config, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncRunloop) -> None:
+ async with async_client.mcp_configs.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ mcp_config = await response.parse()
+ assert_matches_type(AsyncMcpConfigsCursorIDPage[McpConfigView], mcp_config, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_delete(self, async_client: AsyncRunloop) -> None:
+ mcp_config = await async_client.mcp_configs.delete(
+ "id",
+ )
+ assert_matches_type(McpConfigView, mcp_config, path=["response"])
+
+ @parametrize
+ async def test_raw_response_delete(self, async_client: AsyncRunloop) -> None:
+ response = await async_client.mcp_configs.with_raw_response.delete(
+ "id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ mcp_config = await response.parse()
+ assert_matches_type(McpConfigView, mcp_config, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_delete(self, async_client: AsyncRunloop) -> None:
+ async with async_client.mcp_configs.with_streaming_response.delete(
+ "id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ mcp_config = await response.parse()
+ assert_matches_type(McpConfigView, mcp_config, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_delete(self, async_client: AsyncRunloop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.mcp_configs.with_raw_response.delete(
+ "",
+ )
diff --git a/tests/sdk/test_async_scorer.py b/tests/sdk/test_async_scorer.py
index 253ae9585..23b6628c7 100644
--- a/tests/sdk/test_async_scorer.py
+++ b/tests/sdk/test_async_scorer.py
@@ -49,21 +49,3 @@ async def test_update(self, mock_async_client: AsyncMock) -> None:
assert result == update_response
mock_async_client.scenarios.scorers.update.assert_awaited_once()
-
- @pytest.mark.asyncio
- async def test_validate(self, mock_async_client: AsyncMock) -> None:
- """Test validate method."""
- validate_response = SimpleNamespace(
- name="test_scorer",
- scoring_context={},
- scoring_result=SimpleNamespace(score=0.95),
- )
- mock_async_client.scenarios.scorers.validate = AsyncMock(return_value=validate_response)
-
- scorer = AsyncScorer(mock_async_client, "sco_123")
- result = await scorer.validate(
- scoring_context={"test": "context"},
- )
-
- assert result == validate_response
- mock_async_client.scenarios.scorers.validate.assert_awaited_once()
diff --git a/tests/sdk/test_scorer.py b/tests/sdk/test_scorer.py
index 91b430db0..e3363c6dc 100644
--- a/tests/sdk/test_scorer.py
+++ b/tests/sdk/test_scorer.py
@@ -49,23 +49,3 @@ def test_update(self, mock_client: Mock) -> None:
type="updated_scorer",
bash_script="echo 'score=1.0'",
)
-
- def test_validate(self, mock_client: Mock) -> None:
- """Test validate method."""
- validate_response = SimpleNamespace(
- name="test_scorer",
- scoring_context={},
- scoring_result=SimpleNamespace(score=0.95),
- )
- mock_client.scenarios.scorers.validate.return_value = validate_response
-
- scorer = Scorer(mock_client, "sco_123")
- result = scorer.validate(
- scoring_context={"test": "context"},
- )
-
- assert result == validate_response
- mock_client.scenarios.scorers.validate.assert_called_once_with(
- "sco_123",
- scoring_context={"test": "context"},
- )
diff --git a/tests/smoketests/sdk/test_async_scorer.py b/tests/smoketests/sdk/test_async_scorer.py
index ce6603d64..a98ae677f 100644
--- a/tests/smoketests/sdk/test_async_scorer.py
+++ b/tests/smoketests/sdk/test_async_scorer.py
@@ -4,7 +4,6 @@
import pytest
-from runloop_api_client import InternalServerError
from runloop_api_client.sdk import AsyncRunloopSDK
from tests.smoketests.utils import unique_name
@@ -71,24 +70,6 @@ async def test_scorer_update(self, async_sdk_client: AsyncRunloopSDK) -> None:
assert info.type == updated_type
assert info.bash_script == "echo 'score=1.0'"
- @pytest.mark.timeout(ONE_MINUTE_TIMEOUT)
- async def test_scorer_validate(self, async_sdk_client: AsyncRunloopSDK) -> None:
- """Test validating a scorer."""
- scorer_type = unique_name("sdk-async-scorer-validate")
- scorer = await async_sdk_client.scorer.create(
- type=scorer_type,
- bash_script="echo 'score=1.0'",
- )
-
- try:
- result = await scorer.validate(
- scoring_context={},
- )
- assert result is not None
- except InternalServerError:
- # Backend may return 500 for validate endpoint - skip if this happens
- pytest.skip("Backend returned 500 for scorer validate endpoint")
-
class TestAsyncScorerListing:
"""Test async scorer listing and retrieval operations."""
diff --git a/tests/smoketests/sdk/test_scorer.py b/tests/smoketests/sdk/test_scorer.py
index 01df84df9..700cfb1f7 100644
--- a/tests/smoketests/sdk/test_scorer.py
+++ b/tests/smoketests/sdk/test_scorer.py
@@ -4,7 +4,6 @@
import pytest
-from runloop_api_client import InternalServerError
from runloop_api_client.sdk import RunloopSDK
from tests.smoketests.utils import unique_name
@@ -71,24 +70,6 @@ def test_scorer_update(self, sdk_client: RunloopSDK) -> None:
assert info.type == updated_type
assert info.bash_script == "echo 'score=1.0'"
- @pytest.mark.timeout(ONE_MINUTE_TIMEOUT)
- def test_scorer_validate(self, sdk_client: RunloopSDK) -> None:
- """Test validating a scorer."""
- scorer_type = unique_name("sdk-scorer-validate")
- scorer = sdk_client.scorer.create(
- type=scorer_type,
- bash_script="echo 'score=1.0'",
- )
-
- try:
- result = scorer.validate(
- scoring_context={},
- )
- assert result is not None
- except InternalServerError:
- # Backend may return 500 for validate endpoint - skip if this happens
- pytest.skip("Backend returned 500 for scorer validate endpoint")
-
class TestScorerListing:
"""Test scorer listing and retrieval operations."""