Skip to content

Commit e9f6756

Browse files
authored
feat: add MCP Hub to sdk (#742)
1 parent 81c4975 commit e9f6756

7 files changed

Lines changed: 452 additions & 0 deletions

File tree

src/runloop_api_client/sdk/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
SnapshotOps,
1515
BenchmarkOps,
1616
BlueprintOps,
17+
McpConfigOps,
1718
GatewayConfigOps,
1819
NetworkPolicyOps,
1920
StorageObjectOps,
@@ -29,6 +30,7 @@
2930
AsyncSnapshotOps,
3031
AsyncBenchmarkOps,
3132
AsyncBlueprintOps,
33+
AsyncMcpConfigOps,
3234
AsyncGatewayConfigOps,
3335
AsyncNetworkPolicyOps,
3436
AsyncStorageObjectOps,
@@ -40,6 +42,7 @@
4042
from .benchmark import Benchmark
4143
from .blueprint import Blueprint
4244
from .execution import Execution
45+
from .mcp_config import McpConfig
4346
from .async_agent import AsyncAgent
4447
from .async_devbox import AsyncDevbox, AsyncNamedShell
4548
from .async_scorer import AsyncScorer
@@ -53,6 +56,7 @@
5356
from .async_benchmark import AsyncBenchmark
5457
from .async_blueprint import AsyncBlueprint
5558
from .async_execution import AsyncExecution
59+
from .async_mcp_config import AsyncMcpConfig
5660
from .execution_result import ExecutionResult
5761
from .scenario_builder import ScenarioBuilder
5862
from .async_scenario_run import AsyncScenarioRun
@@ -86,6 +90,8 @@
8690
"AsyncStorageObjectOps",
8791
"NetworkPolicyOps",
8892
"AsyncNetworkPolicyOps",
93+
"McpConfigOps",
94+
"AsyncMcpConfigOps",
8995
"GatewayConfigOps",
9096
"AsyncGatewayConfigOps",
9197
# Resource classes
@@ -118,6 +124,8 @@
118124
"AsyncStorageObject",
119125
"NetworkPolicy",
120126
"AsyncNetworkPolicy",
127+
"McpConfig",
128+
"AsyncMcpConfig",
121129
"GatewayConfig",
122130
"AsyncGatewayConfig",
123131
"NamedShell",

src/runloop_api_client/sdk/_types.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,14 @@
1313
ScenarioListParams,
1414
BenchmarkListParams,
1515
BlueprintListParams,
16+
McpConfigListParams,
1617
ObjectDownloadParams,
1718
ScenarioUpdateParams,
1819
BenchmarkCreateParams,
1920
BenchmarkUpdateParams,
2021
BlueprintCreateParams,
22+
McpConfigCreateParams,
23+
McpConfigUpdateParams,
2124
DevboxUploadFileParams,
2225
GatewayConfigListParams,
2326
NetworkPolicyListParams,
@@ -266,6 +269,18 @@ class SDKNetworkPolicyUpdateParams(NetworkPolicyUpdateParams, LongRequestOptions
266269
pass
267270

268271

272+
class SDKMcpConfigCreateParams(McpConfigCreateParams, LongRequestOptions):
273+
pass
274+
275+
276+
class SDKMcpConfigListParams(McpConfigListParams, BaseRequestOptions):
277+
pass
278+
279+
280+
class SDKMcpConfigUpdateParams(McpConfigUpdateParams, LongRequestOptions):
281+
pass
282+
283+
269284
class SDKGatewayConfigCreateParams(GatewayConfigCreateParams, LongRequestOptions):
270285
pass
271286

src/runloop_api_client/sdk/async_.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@
2323
SDKScorerCreateParams,
2424
SDKBenchmarkListParams,
2525
SDKBlueprintListParams,
26+
SDKMcpConfigListParams,
2627
SDKBenchmarkCreateParams,
2728
SDKBlueprintCreateParams,
29+
SDKMcpConfigCreateParams,
2830
SDKDiskSnapshotListParams,
2931
SDKGatewayConfigListParams,
3032
SDKNetworkPolicyListParams,
@@ -42,6 +44,7 @@
4244
from .async_snapshot import AsyncSnapshot
4345
from .async_benchmark import AsyncBenchmark
4446
from .async_blueprint import AsyncBlueprint
47+
from .async_mcp_config import AsyncMcpConfig
4548
from ..lib.context_loader import TarFilter, build_directory_tar
4649
from .async_gateway_config import AsyncGatewayConfig
4750
from .async_network_policy import AsyncNetworkPolicy
@@ -1011,6 +1014,62 @@ async def list(self, **params: Unpack[SDKGatewayConfigListParams]) -> list[Async
10111014
return [AsyncGatewayConfig(self._client, item.id) for item in page.gateway_configs]
10121015

10131016

1017+
class AsyncMcpConfigOps:
1018+
"""High-level async manager for creating and managing MCP configurations.
1019+
1020+
Accessed via ``runloop.mcp_config`` from :class:`AsyncRunloopSDK`, provides methods
1021+
to create, retrieve, update, delete, and list MCP configs. MCP configs define
1022+
how to connect to upstream MCP (Model Context Protocol) servers, specifying the
1023+
target endpoint and which tools are allowed.
1024+
1025+
Example:
1026+
>>> runloop = AsyncRunloopSDK()
1027+
>>> mcp_config = await runloop.mcp_config.create(
1028+
... name="my-mcp-server",
1029+
... endpoint="https://mcp.example.com",
1030+
... allowed_tools=["*"],
1031+
... )
1032+
"""
1033+
1034+
def __init__(self, client: AsyncRunloop) -> None:
1035+
"""Initialize AsyncMcpConfigOps.
1036+
1037+
:param client: AsyncRunloop client instance
1038+
:type client: AsyncRunloop
1039+
"""
1040+
self._client = client
1041+
1042+
async def create(self, **params: Unpack[SDKMcpConfigCreateParams]) -> AsyncMcpConfig:
1043+
"""Create a new MCP config.
1044+
1045+
:param params: See :typeddict:`~runloop_api_client.sdk._types.SDKMcpConfigCreateParams` for available parameters
1046+
:return: The newly created MCP config
1047+
:rtype: AsyncMcpConfig
1048+
"""
1049+
response = await self._client.mcp_configs.create(**params)
1050+
return AsyncMcpConfig(self._client, response.id)
1051+
1052+
def from_id(self, mcp_config_id: str) -> AsyncMcpConfig:
1053+
"""Get an AsyncMcpConfig instance for an existing MCP config ID.
1054+
1055+
:param mcp_config_id: ID of the MCP config
1056+
:type mcp_config_id: str
1057+
:return: AsyncMcpConfig instance for the given ID
1058+
:rtype: AsyncMcpConfig
1059+
"""
1060+
return AsyncMcpConfig(self._client, mcp_config_id)
1061+
1062+
async def list(self, **params: Unpack[SDKMcpConfigListParams]) -> list[AsyncMcpConfig]:
1063+
"""List all MCP configs, optionally filtered by parameters.
1064+
1065+
:param params: See :typeddict:`~runloop_api_client.sdk._types.SDKMcpConfigListParams` for available parameters
1066+
:return: List of MCP configs
1067+
:rtype: list[AsyncMcpConfig]
1068+
"""
1069+
page = await self._client.mcp_configs.list(**params)
1070+
return [AsyncMcpConfig(self._client, item.id) for item in page.mcp_configs]
1071+
1072+
10141073
class AsyncRunloopSDK:
10151074
"""High-level asynchronous entry point for the Runloop SDK.
10161075
@@ -1040,6 +1099,8 @@ class AsyncRunloopSDK:
10401099
:vartype network_policy: AsyncNetworkPolicyOps
10411100
:ivar gateway_config: High-level async interface for gateway config management
10421101
:vartype gateway_config: AsyncGatewayConfigOps
1102+
:ivar mcp_config: High-level async interface for MCP config management
1103+
:vartype mcp_config: AsyncMcpConfigOps
10431104
10441105
Example:
10451106
>>> runloop = AsyncRunloopSDK() # Uses RUNLOOP_API_KEY env var
@@ -1055,6 +1116,7 @@ class AsyncRunloopSDK:
10551116
devbox: AsyncDevboxOps
10561117
blueprint: AsyncBlueprintOps
10571118
gateway_config: AsyncGatewayConfigOps
1119+
mcp_config: AsyncMcpConfigOps
10581120
network_policy: AsyncNetworkPolicyOps
10591121
scenario: AsyncScenarioOps
10601122
scorer: AsyncScorerOps
@@ -1104,6 +1166,7 @@ def __init__(
11041166
self.devbox = AsyncDevboxOps(self.api)
11051167
self.blueprint = AsyncBlueprintOps(self.api)
11061168
self.gateway_config = AsyncGatewayConfigOps(self.api)
1169+
self.mcp_config = AsyncMcpConfigOps(self.api)
11071170
self.network_policy = AsyncNetworkPolicyOps(self.api)
11081171
self.scenario = AsyncScenarioOps(self.api)
11091172
self.scorer = AsyncScorerOps(self.api)
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
"""McpConfig resource class for asynchronous operations."""
2+
3+
from __future__ import annotations
4+
5+
from typing_extensions import Unpack, override
6+
7+
from ._types import BaseRequestOptions, LongRequestOptions, SDKMcpConfigUpdateParams
8+
from .._client import AsyncRunloop
9+
from ..types.mcp_config_view import McpConfigView
10+
11+
12+
class AsyncMcpConfig:
13+
"""Asynchronous wrapper around an MCP config resource.
14+
15+
MCP configs define how to connect to upstream MCP (Model Context Protocol) servers.
16+
They specify the target endpoint and which tools are allowed. Use with devboxes to
17+
securely connect to MCP servers.
18+
19+
Example:
20+
>>> runloop = AsyncRunloopSDK()
21+
>>> mcp_config = await runloop.mcp_config.create(
22+
... name="my-mcp-server",
23+
... endpoint="https://mcp.example.com",
24+
... allowed_tools=["*"],
25+
... )
26+
>>> info = await mcp_config.get_info()
27+
>>> print(f"MCP Config: {info.name}")
28+
"""
29+
30+
def __init__(
31+
self,
32+
client: AsyncRunloop,
33+
mcp_config_id: str,
34+
) -> None:
35+
"""Initialize the wrapper.
36+
37+
:param client: Generated AsyncRunloop client
38+
:type client: AsyncRunloop
39+
:param mcp_config_id: McpConfig ID returned by the API
40+
:type mcp_config_id: str
41+
"""
42+
self._client = client
43+
self._id = mcp_config_id
44+
45+
@override
46+
def __repr__(self) -> str:
47+
return f"<AsyncMcpConfig id={self._id!r}>"
48+
49+
@property
50+
def id(self) -> str:
51+
"""Return the MCP config ID.
52+
53+
:return: Unique MCP config ID
54+
:rtype: str
55+
"""
56+
return self._id
57+
58+
async def get_info(
59+
self,
60+
**options: Unpack[BaseRequestOptions],
61+
) -> McpConfigView:
62+
"""Retrieve the latest MCP config details.
63+
64+
:param options: Optional request configuration
65+
:return: API response describing the MCP config
66+
:rtype: McpConfigView
67+
"""
68+
return await self._client.mcp_configs.retrieve(
69+
self._id,
70+
**options,
71+
)
72+
73+
async def update(self, **params: Unpack[SDKMcpConfigUpdateParams]) -> McpConfigView:
74+
"""Update the MCP config.
75+
76+
:param params: See :typeddict:`~runloop_api_client.sdk._types.SDKMcpConfigUpdateParams` for available parameters
77+
:return: Updated MCP config view
78+
:rtype: McpConfigView
79+
"""
80+
return await self._client.mcp_configs.update(self._id, **params)
81+
82+
async def delete(
83+
self,
84+
**options: Unpack[LongRequestOptions],
85+
) -> McpConfigView:
86+
"""Delete the MCP config. This action is irreversible.
87+
88+
:param options: Optional long-running request configuration
89+
:return: API response acknowledging deletion
90+
:rtype: McpConfigView
91+
"""
92+
return await self._client.mcp_configs.delete(
93+
self._id,
94+
**options,
95+
)
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
"""McpConfig resource class for synchronous operations."""
2+
3+
from __future__ import annotations
4+
5+
from typing_extensions import Unpack, override
6+
7+
from ._types import BaseRequestOptions, LongRequestOptions, SDKMcpConfigUpdateParams
8+
from .._client import Runloop
9+
from ..types.mcp_config_view import McpConfigView
10+
11+
12+
class McpConfig:
13+
"""Synchronous wrapper around an MCP config resource.
14+
15+
MCP configs define how to connect to upstream MCP (Model Context Protocol) servers.
16+
They specify the target endpoint and which tools are allowed. Use with devboxes to
17+
securely connect to MCP servers.
18+
19+
Example:
20+
>>> runloop = RunloopSDK()
21+
>>> mcp_config = runloop.mcp_config.create(
22+
... name="my-mcp-server",
23+
... endpoint="https://mcp.example.com",
24+
... allowed_tools=["*"],
25+
... )
26+
>>> info = mcp_config.get_info()
27+
>>> print(f"MCP Config: {info.name}")
28+
"""
29+
30+
def __init__(
31+
self,
32+
client: Runloop,
33+
mcp_config_id: str,
34+
) -> None:
35+
"""Initialize the wrapper.
36+
37+
:param client: Generated Runloop client
38+
:type client: Runloop
39+
:param mcp_config_id: McpConfig ID returned by the API
40+
:type mcp_config_id: str
41+
"""
42+
self._client = client
43+
self._id = mcp_config_id
44+
45+
@override
46+
def __repr__(self) -> str:
47+
return f"<McpConfig id={self._id!r}>"
48+
49+
@property
50+
def id(self) -> str:
51+
"""Return the MCP config ID.
52+
53+
:return: Unique MCP config ID
54+
:rtype: str
55+
"""
56+
return self._id
57+
58+
def get_info(
59+
self,
60+
**options: Unpack[BaseRequestOptions],
61+
) -> McpConfigView:
62+
"""Retrieve the latest MCP config details.
63+
64+
:param options: Optional request configuration
65+
:return: API response describing the MCP config
66+
:rtype: McpConfigView
67+
"""
68+
return self._client.mcp_configs.retrieve(
69+
self._id,
70+
**options,
71+
)
72+
73+
def update(self, **params: Unpack[SDKMcpConfigUpdateParams]) -> McpConfigView:
74+
"""Update the MCP config.
75+
76+
:param params: See :typeddict:`~runloop_api_client.sdk._types.SDKMcpConfigUpdateParams` for available parameters
77+
:return: Updated MCP config view
78+
:rtype: McpConfigView
79+
"""
80+
return self._client.mcp_configs.update(self._id, **params)
81+
82+
def delete(
83+
self,
84+
**options: Unpack[LongRequestOptions],
85+
) -> McpConfigView:
86+
"""Delete the MCP config. This action is irreversible.
87+
88+
:param options: Optional long-running request configuration
89+
:return: API response acknowledging deletion
90+
:rtype: McpConfigView
91+
"""
92+
return self._client.mcp_configs.delete(
93+
self._id,
94+
**options,
95+
)

0 commit comments

Comments
 (0)