Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/runloop_api_client/sdk/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
SnapshotOps,
BenchmarkOps,
BlueprintOps,
McpConfigOps,
GatewayConfigOps,
NetworkPolicyOps,
StorageObjectOps,
Expand All @@ -29,6 +30,7 @@
AsyncSnapshotOps,
AsyncBenchmarkOps,
AsyncBlueprintOps,
AsyncMcpConfigOps,
AsyncGatewayConfigOps,
AsyncNetworkPolicyOps,
AsyncStorageObjectOps,
Expand All @@ -47,6 +49,7 @@
from .benchmark_run import BenchmarkRun
from .async_scenario import AsyncScenario
from .async_snapshot import AsyncSnapshot
from .mcp_config import McpConfig
from .gateway_config import GatewayConfig
from .network_policy import NetworkPolicy
from .storage_object import StorageObject
Expand All @@ -57,6 +60,7 @@
from .scenario_builder import ScenarioBuilder
from .async_scenario_run import AsyncScenarioRun
from .async_benchmark_run import AsyncBenchmarkRun
from .async_mcp_config import AsyncMcpConfig
from .async_gateway_config import AsyncGatewayConfig
from .async_network_policy import AsyncNetworkPolicy
from .async_storage_object import AsyncStorageObject
Expand Down Expand Up @@ -86,6 +90,8 @@
"AsyncStorageObjectOps",
"NetworkPolicyOps",
"AsyncNetworkPolicyOps",
"McpConfigOps",
"AsyncMcpConfigOps",
"GatewayConfigOps",
"AsyncGatewayConfigOps",
# Resource classes
Expand Down Expand Up @@ -118,6 +124,8 @@
"AsyncStorageObject",
"NetworkPolicy",
"AsyncNetworkPolicy",
"McpConfig",
"AsyncMcpConfig",
"GatewayConfig",
"AsyncGatewayConfig",
"NamedShell",
Expand Down
15 changes: 15 additions & 0 deletions src/runloop_api_client/sdk/_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,16 @@
BenchmarkUpdateParams,
BlueprintCreateParams,
DevboxUploadFileParams,
McpConfigListParams,
GatewayConfigListParams,
NetworkPolicyListParams,
DevboxCreateTunnelParams,
DevboxDownloadFileParams,
DevboxEnableTunnelParams,
DevboxRemoveTunnelParams,
DevboxSnapshotDiskParams,
McpConfigCreateParams,
McpConfigUpdateParams,
GatewayConfigCreateParams,
GatewayConfigUpdateParams,
NetworkPolicyCreateParams,
Expand Down Expand Up @@ -266,6 +269,18 @@ class SDKNetworkPolicyUpdateParams(NetworkPolicyUpdateParams, LongRequestOptions
pass


class SDKMcpConfigCreateParams(McpConfigCreateParams, LongRequestOptions):
pass


class SDKMcpConfigListParams(McpConfigListParams, BaseRequestOptions):
pass


class SDKMcpConfigUpdateParams(McpConfigUpdateParams, LongRequestOptions):
pass


class SDKGatewayConfigCreateParams(GatewayConfigCreateParams, LongRequestOptions):
pass

Expand Down
81 changes: 81 additions & 0 deletions src/runloop_api_client/sdk/async_.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@
SDKBenchmarkCreateParams,
SDKBlueprintCreateParams,
SDKDiskSnapshotListParams,
SDKMcpConfigListParams,
SDKGatewayConfigListParams,
SDKNetworkPolicyListParams,
SDKMcpConfigCreateParams,
SDKGatewayConfigCreateParams,
SDKNetworkPolicyCreateParams,
SDKDevboxCreateFromImageParams,
Expand All @@ -43,6 +45,7 @@
from .async_benchmark import AsyncBenchmark
from .async_blueprint import AsyncBlueprint
from ..lib.context_loader import TarFilter, build_directory_tar
from .async_mcp_config import AsyncMcpConfig
from .async_gateway_config import AsyncGatewayConfig
from .async_network_policy import AsyncNetworkPolicy
from .async_storage_object import AsyncStorageObject
Expand Down Expand Up @@ -1011,6 +1014,80 @@ async def list(self, **params: Unpack[SDKGatewayConfigListParams]) -> list[Async
return [AsyncGatewayConfig(self._client, item.id) for item in page.gateway_configs]


class AsyncMcpConfigOps:
"""High-level async manager for creating and managing MCP configurations.

Accessed via ``runloop.mcp_config`` from :class:`AsyncRunloopSDK`, provides methods
to create, retrieve, update, delete, and list MCP configs. MCP configs define
how to connect to upstream MCP (Model Context Protocol) servers, specifying the
target endpoint and which tools are allowed.

Example:
>>> runloop = AsyncRunloopSDK()
>>> mcp_config = await runloop.mcp_config.create(
... name="my-mcp-server",
... endpoint="https://mcp.example.com",
... allowed_tools=["*"],
... )
"""

def __init__(self, client: AsyncRunloop) -> None:
"""Initialize AsyncMcpConfigOps.

:param client: AsyncRunloop client instance
:type client: AsyncRunloop
"""
self._client = client

async def create(self, **params: Unpack[SDKMcpConfigCreateParams]) -> AsyncMcpConfig:
"""Create a new MCP config.

Example:
>>> mcp_config = await runloop.mcp_config.create(
... name="my-mcp-server",
... endpoint="https://mcp.example.com",
... allowed_tools=["*"],
... description="MCP server for my tools",
... )

:param params: See :typeddict:`~runloop_api_client.sdk._types.SDKMcpConfigCreateParams` for available parameters
:return: The newly created MCP config
:rtype: AsyncMcpConfig
"""
response = await self._client.mcp_configs.create(**params)
return AsyncMcpConfig(self._client, response.id)

def from_id(self, mcp_config_id: str) -> AsyncMcpConfig:
"""Get an AsyncMcpConfig instance for an existing MCP config ID.

Example:
>>> mcp_config = runloop.mcp_config.from_id("mcp_1234567890")
>>> info = await mcp_config.get_info()
>>> print(f"MCP Config name: {info.name}")

:param mcp_config_id: ID of the MCP config
:type mcp_config_id: str
:return: AsyncMcpConfig instance for the given ID
:rtype: AsyncMcpConfig
"""
return AsyncMcpConfig(self._client, mcp_config_id)

async def list(self, **params: Unpack[SDKMcpConfigListParams]) -> list[AsyncMcpConfig]:
"""List all MCP configs, optionally filtered by parameters.

Example:
>>> configs = await runloop.mcp_config.list(limit=10)
>>> for config in configs:
... print(config.id)

:param params: See :typeddict:`~runloop_api_client.sdk._types.SDKMcpConfigListParams` for available parameters
:return: List of MCP configs
:rtype: list[AsyncMcpConfig]
"""
page = await self._client.mcp_configs.list(**params)
return [AsyncMcpConfig(self._client, item.id) for item in page.mcp_configs]


class AsyncRunloopSDK:
"""High-level asynchronous entry point for the Runloop SDK.

Expand Down Expand Up @@ -1040,6 +1117,8 @@ class AsyncRunloopSDK:
:vartype network_policy: AsyncNetworkPolicyOps
:ivar gateway_config: High-level async interface for gateway config management
:vartype gateway_config: AsyncGatewayConfigOps
:ivar mcp_config: High-level async interface for MCP config management
:vartype mcp_config: AsyncMcpConfigOps

Example:
>>> runloop = AsyncRunloopSDK() # Uses RUNLOOP_API_KEY env var
Expand All @@ -1055,6 +1134,7 @@ class AsyncRunloopSDK:
devbox: AsyncDevboxOps
blueprint: AsyncBlueprintOps
gateway_config: AsyncGatewayConfigOps
mcp_config: AsyncMcpConfigOps
network_policy: AsyncNetworkPolicyOps
scenario: AsyncScenarioOps
scorer: AsyncScorerOps
Expand Down Expand Up @@ -1104,6 +1184,7 @@ def __init__(
self.devbox = AsyncDevboxOps(self.api)
self.blueprint = AsyncBlueprintOps(self.api)
self.gateway_config = AsyncGatewayConfigOps(self.api)
self.mcp_config = AsyncMcpConfigOps(self.api)
self.network_policy = AsyncNetworkPolicyOps(self.api)
self.scenario = AsyncScenarioOps(self.api)
self.scorer = AsyncScorerOps(self.api)
Expand Down
108 changes: 108 additions & 0 deletions src/runloop_api_client/sdk/async_mcp_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
"""McpConfig resource class for asynchronous operations."""

from __future__ import annotations

from typing_extensions import Unpack, override

from ._types import BaseRequestOptions, LongRequestOptions, SDKMcpConfigUpdateParams
from .._client import AsyncRunloop
from ..types.mcp_config_view import McpConfigView


class AsyncMcpConfig:
"""Asynchronous wrapper around an MCP config resource.

MCP configs define how to connect to upstream MCP (Model Context Protocol) servers.
They specify the target endpoint and which tools are allowed. Use with devboxes to
securely connect to MCP servers.

Example:
>>> runloop = AsyncRunloopSDK()
>>> mcp_config = await runloop.mcp_config.create(
... name="my-mcp-server",
... endpoint="https://mcp.example.com",
... allowed_tools=["*"],
... )
>>> info = await mcp_config.get_info()
>>> print(f"MCP Config: {info.name}")
"""

def __init__(
self,
client: AsyncRunloop,
mcp_config_id: str,
) -> None:
"""Initialize the wrapper.

:param client: Generated AsyncRunloop client
:type client: AsyncRunloop
:param mcp_config_id: McpConfig ID returned by the API
:type mcp_config_id: str
"""
self._client = client
self._id = mcp_config_id

@override
def __repr__(self) -> str:
return f"<AsyncMcpConfig id={self._id!r}>"

@property
def id(self) -> str:
"""Return the MCP config ID.

:return: Unique MCP config ID
:rtype: str
"""
return self._id

async def get_info(
self,
**options: Unpack[BaseRequestOptions],
) -> McpConfigView:
"""Retrieve the latest MCP config details.

Example:
>>> info = await mcp_config.get_info()
>>> print(f"MCP Config: {info.name}, endpoint: {info.endpoint}")

:param options: Optional request configuration
:return: API response describing the MCP config
:rtype: McpConfigView
"""
return await self._client.mcp_configs.retrieve(
self._id,
**options,
)

async def update(self, **params: Unpack[SDKMcpConfigUpdateParams]) -> McpConfigView:
"""Update the MCP config.

Example:
>>> updated = await mcp_config.update(
... name="updated-mcp-name",
... description="Updated description",
... )

:param params: See :typeddict:`~runloop_api_client.sdk._types.SDKMcpConfigUpdateParams` for available parameters
:return: Updated MCP config view
:rtype: McpConfigView
"""
return await self._client.mcp_configs.update(self._id, **params)

async def delete(
self,
**options: Unpack[LongRequestOptions],
) -> McpConfigView:
"""Delete the MCP config. This action is irreversible.

Example:
>>> await mcp_config.delete()

:param options: Optional long-running request configuration
:return: API response acknowledging deletion
:rtype: McpConfigView
"""
return await self._client.mcp_configs.delete(
self._id,
**options,
)
Loading
Loading