From 0d5ca3438ac4cbcafdfaf7584042bd78a956e3ae Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Fri, 31 Jul 2026 18:40:33 +0000
Subject: [PATCH 01/11] feat(converter): add Helm chart, container image, and
enable in dev (#10405)
---
.stats.yml | 4 ++--
src/runloop_api_client/types/blueprint_view.py | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index 63705699e..25a63951c 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 120
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai/runloop-c1b0e0d2c2bf9d7888ddef0cfdfb390458875cd2c1feb48a06fcd06e92105326.yml
-openapi_spec_hash: ec61cf6d205251b54e1a6d68d9dd9860
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai/runloop-3fbf64145b022650227fd83c53efcf2a514638e932efc2277490280fd81c083e.yml
+openapi_spec_hash: 4e673e5a85fc95f1c6dcc89d6a59d004
config_hash: 6d102da8b9fcc3d0deb42d7a707e78c6
diff --git a/src/runloop_api_client/types/blueprint_view.py b/src/runloop_api_client/types/blueprint_view.py
index 87c1eecab..b0bfefecd 100644
--- a/src/runloop_api_client/types/blueprint_view.py
+++ b/src/runloop_api_client/types/blueprint_view.py
@@ -63,7 +63,7 @@ class BlueprintView(BaseModel):
state: Literal["created", "deleted"]
"""The state of the Blueprint."""
- status: Literal["queued", "provisioning", "building", "failed", "build_complete"]
+ status: Literal["queued", "provisioning", "building", "awaiting_upload", "failed", "build_complete"]
"""The status of the Blueprint build."""
base_blueprint_id: Optional[str] = None
From 974d52b8743ceda8a7127e3a3432ce51e71b1bfd Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Fri, 31 Jul 2026 22:52:24 +0000
Subject: [PATCH 02/11] feat(broker): plumb Pi protocol selection (#10256)
---
.stats.yml | 4 ++--
src/runloop_api_client/types/shared/broker_mount.py | 8 +++++---
.../types/shared_params/broker_mount.py | 8 +++++---
3 files changed, 12 insertions(+), 8 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index 25a63951c..552a016d1 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 120
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai/runloop-3fbf64145b022650227fd83c53efcf2a514638e932efc2277490280fd81c083e.yml
-openapi_spec_hash: 4e673e5a85fc95f1c6dcc89d6a59d004
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai/runloop-655842d2f92e9a3525185c38986cfe40c08b4bab35d39384d519bc502f415904.yml
+openapi_spec_hash: fabc1067256c27f29fc1225c1ea871bf
config_hash: 6d102da8b9fcc3d0deb42d7a707e78c6
diff --git a/src/runloop_api_client/types/shared/broker_mount.py b/src/runloop_api_client/types/shared/broker_mount.py
index adf49c04a..97e6c940a 100644
--- a/src/runloop_api_client/types/shared/broker_mount.py
+++ b/src/runloop_api_client/types/shared/broker_mount.py
@@ -17,16 +17,18 @@ class BrokerMount(BaseModel):
agent_binary: Optional[str] = None
"""Binary to launch the agent (e.g., 'opencode').
- Used by protocols that launch a subprocess (acp, claude_json, codex_json).
+ Used by protocols that launch a subprocess (acp, claude_json, codex_json,
+ pi_json).
"""
launch_args: Optional[List[str]] = None
"""Arguments to pass to the agent command (e.g., ['acp']).
- Used by protocols that launch a subprocess (acp, claude_json, codex_json).
+ Used by protocols that launch a subprocess (acp, claude_json, codex_json,
+ pi_json).
"""
- protocol: Optional[Literal["acp", "claude_json", "codex_json"]] = None
+ protocol: Optional[Literal["acp", "claude_json", "codex_json", "pi_json"]] = None
"""The protocol used by the broker to deliver events to the agent."""
working_directory: Optional[str] = None
diff --git a/src/runloop_api_client/types/shared_params/broker_mount.py b/src/runloop_api_client/types/shared_params/broker_mount.py
index e62bd59e5..55e6b6661 100644
--- a/src/runloop_api_client/types/shared_params/broker_mount.py
+++ b/src/runloop_api_client/types/shared_params/broker_mount.py
@@ -19,16 +19,18 @@ class BrokerMount(TypedDict, total=False):
agent_binary: Optional[str]
"""Binary to launch the agent (e.g., 'opencode').
- Used by protocols that launch a subprocess (acp, claude_json, codex_json).
+ Used by protocols that launch a subprocess (acp, claude_json, codex_json,
+ pi_json).
"""
launch_args: Optional[SequenceNotStr[str]]
"""Arguments to pass to the agent command (e.g., ['acp']).
- Used by protocols that launch a subprocess (acp, claude_json, codex_json).
+ Used by protocols that launch a subprocess (acp, claude_json, codex_json,
+ pi_json).
"""
- protocol: Optional[Literal["acp", "claude_json", "codex_json"]]
+ protocol: Optional[Literal["acp", "claude_json", "codex_json", "pi_json"]]
"""The protocol used by the broker to deliver events to the agent."""
working_directory: Optional[str]
From 518301e444b95bc5fa73cc0f13be67cf3f750024 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Sun, 9 Aug 2026 20:08:59 +0000
Subject: [PATCH 03/11] feat(devbox): recover resume provisioning failures
(#10597)
---
.stats.yml | 4 ++--
src/runloop_api_client/types/devbox_view.py | 3 +++
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index 552a016d1..2e8657507 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 120
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai/runloop-655842d2f92e9a3525185c38986cfe40c08b4bab35d39384d519bc502f415904.yml
-openapi_spec_hash: fabc1067256c27f29fc1225c1ea871bf
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai/runloop-3dc3735bd6dde301483f325e9be5cbae2aabdf9988b7e5a1da920d41e8bb380f.yml
+openapi_spec_hash: 70e2f6ccb848ff142fbfa020329951aa
config_hash: 6d102da8b9fcc3d0deb42d7a707e78c6
diff --git a/src/runloop_api_client/types/devbox_view.py b/src/runloop_api_client/types/devbox_view.py
index 49ffc1ec8..504d1fe2e 100644
--- a/src/runloop_api_client/types/devbox_view.py
+++ b/src/runloop_api_client/types/devbox_view.py
@@ -11,6 +11,9 @@
class StateTransition(BaseModel):
+ failure_reason: Optional[str] = None
+ """The failure that caused this state transition, if applicable."""
+
status: Optional[
Literal[
"scheduled",
From c63f01108d98785b358357e4e65e5e517dd0805e Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Mon, 10 Aug 2026 21:58:49 +0000
Subject: [PATCH 04/11] feat(axon): add registry delete endpoint (#10769)
---
.stats.yml | 8 +-
api.md | 1 +
.../resources/axons/axons.py | 92 +++++++++++++++++++
tests/api_resources/test_axons.py | 76 +++++++++++++++
4 files changed, 173 insertions(+), 4 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index 2e8657507..02013546c 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
-configured_endpoints: 120
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai/runloop-3dc3735bd6dde301483f325e9be5cbae2aabdf9988b7e5a1da920d41e8bb380f.yml
-openapi_spec_hash: 70e2f6ccb848ff142fbfa020329951aa
-config_hash: 6d102da8b9fcc3d0deb42d7a707e78c6
+configured_endpoints: 121
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai/runloop-a9327279b1ba17f0983d92a3065b04a2bc1b5e5e830cf099aef4f79072fe4075.yml
+openapi_spec_hash: b63815af36cf18c8cc70be416f9c0f71
+config_hash: 2b0535779b180339d10766ba74a96700
diff --git a/api.md b/api.md
index 6becb60c5..584c2d2f8 100644
--- a/api.md
+++ b/api.md
@@ -131,6 +131,7 @@ Methods:
- client.axons.create(\*\*params) -> AxonView
- client.axons.retrieve(id) -> AxonView
- client.axons.list(\*\*params) -> SyncAxonsCursorIDPage[AxonView]
+- client.axons.delete(id) -> object
- client.axons.publish(id, \*\*params) -> PublishResultView
- client.axons.subscribe_sse(id, \*\*params) -> AxonEventView
diff --git a/src/runloop_api_client/resources/axons/axons.py b/src/runloop_api_client/resources/axons/axons.py
index fba62a339..232f6f4c2 100644
--- a/src/runloop_api_client/resources/axons/axons.py
+++ b/src/runloop_api_client/resources/axons/axons.py
@@ -210,6 +210,46 @@ def list(
model=AxonView,
)
+ 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,
+ ) -> object:
+ """
+ [Beta] Mark an axon deleted.
+
+ 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._delete(
+ path_template("/v1/axons/{id}", id=id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ idempotency_key=idempotency_key,
+ ),
+ cast_to=object,
+ )
+
def publish(
self,
id: str,
@@ -485,6 +525,46 @@ def list(
model=AxonView,
)
+ 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,
+ ) -> object:
+ """
+ [Beta] Mark an axon deleted.
+
+ 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._delete(
+ path_template("/v1/axons/{id}", id=id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ idempotency_key=idempotency_key,
+ ),
+ cast_to=object,
+ )
+
async def publish(
self,
id: str,
@@ -607,6 +687,9 @@ def __init__(self, axons: AxonsResource) -> None:
self.list = to_raw_response_wrapper(
axons.list,
)
+ self.delete = to_raw_response_wrapper(
+ axons.delete,
+ )
self.publish = to_raw_response_wrapper(
axons.publish,
)
@@ -636,6 +719,9 @@ def __init__(self, axons: AsyncAxonsResource) -> None:
self.list = async_to_raw_response_wrapper(
axons.list,
)
+ self.delete = async_to_raw_response_wrapper(
+ axons.delete,
+ )
self.publish = async_to_raw_response_wrapper(
axons.publish,
)
@@ -665,6 +751,9 @@ def __init__(self, axons: AxonsResource) -> None:
self.list = to_streamed_response_wrapper(
axons.list,
)
+ self.delete = to_streamed_response_wrapper(
+ axons.delete,
+ )
self.publish = to_streamed_response_wrapper(
axons.publish,
)
@@ -694,6 +783,9 @@ def __init__(self, axons: AsyncAxonsResource) -> None:
self.list = async_to_streamed_response_wrapper(
axons.list,
)
+ self.delete = async_to_streamed_response_wrapper(
+ axons.delete,
+ )
self.publish = async_to_streamed_response_wrapper(
axons.publish,
)
diff --git a/tests/api_resources/test_axons.py b/tests/api_resources/test_axons.py
index 8764443bb..97f4f663c 100644
--- a/tests/api_resources/test_axons.py
+++ b/tests/api_resources/test_axons.py
@@ -128,6 +128,44 @@ def test_streaming_response_list(self, client: Runloop) -> None:
assert cast(Any, response.is_closed) is True
+ @parametrize
+ def test_method_delete(self, client: Runloop) -> None:
+ axon = client.axons.delete(
+ "id",
+ )
+ assert_matches_type(object, axon, path=["response"])
+
+ @parametrize
+ def test_raw_response_delete(self, client: Runloop) -> None:
+ response = client.axons.with_raw_response.delete(
+ "id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ axon = response.parse()
+ assert_matches_type(object, axon, path=["response"])
+
+ @parametrize
+ def test_streaming_response_delete(self, client: Runloop) -> None:
+ with client.axons.with_streaming_response.delete(
+ "id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ axon = response.parse()
+ assert_matches_type(object, axon, 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.axons.with_raw_response.delete(
+ "",
+ )
+
@parametrize
def test_method_publish(self, client: Runloop) -> None:
axon = client.axons.publish(
@@ -340,6 +378,44 @@ async def test_streaming_response_list(self, async_client: AsyncRunloop) -> None
assert cast(Any, response.is_closed) is True
+ @parametrize
+ async def test_method_delete(self, async_client: AsyncRunloop) -> None:
+ axon = await async_client.axons.delete(
+ "id",
+ )
+ assert_matches_type(object, axon, path=["response"])
+
+ @parametrize
+ async def test_raw_response_delete(self, async_client: AsyncRunloop) -> None:
+ response = await async_client.axons.with_raw_response.delete(
+ "id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ axon = await response.parse()
+ assert_matches_type(object, axon, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_delete(self, async_client: AsyncRunloop) -> None:
+ async with async_client.axons.with_streaming_response.delete(
+ "id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ axon = await response.parse()
+ assert_matches_type(object, axon, 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.axons.with_raw_response.delete(
+ "",
+ )
+
@parametrize
async def test_method_publish(self, async_client: AsyncRunloop) -> None:
axon = await async_client.axons.publish(
From 0a7452145ee6d1689d8635e65f88b8f778a84820 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Mon, 17 Aug 2026 22:02:20 +0000
Subject: [PATCH 05/11] fix(devbox): make Devbox update API patch-like (#10582)
---
.stats.yml | 4 +--
.../resources/devboxes/devboxes.py | 26 ++++++++++++-------
.../types/devbox_update_params.py | 10 +++++--
3 files changed, 26 insertions(+), 14 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index 02013546c..360b9980f 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 121
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai/runloop-a9327279b1ba17f0983d92a3065b04a2bc1b5e5e830cf099aef4f79072fe4075.yml
-openapi_spec_hash: b63815af36cf18c8cc70be416f9c0f71
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai/runloop-49fd19a6c2b93ae0af51ae79c2a2a1a67adaf56611840c4d30b651729b090c63.yml
+openapi_spec_hash: 2d30d0e7c9061c5f748fcc915f7af4e9
config_hash: 2b0535779b180339d10766ba74a96700
diff --git a/src/runloop_api_client/resources/devboxes/devboxes.py b/src/runloop_api_client/resources/devboxes/devboxes.py
index 65d0dcbd3..0a0dea11a 100644
--- a/src/runloop_api_client/resources/devboxes/devboxes.py
+++ b/src/runloop_api_client/resources/devboxes/devboxes.py
@@ -338,14 +338,17 @@ def update(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
idempotency_key: str | None = None,
) -> DevboxView:
- """
- Updates a devbox by doing a complete update the existing name,metadata fields.
- It does not patch partial values.
+ """Updates the specified Devbox fields.
+
+ Omitted fields are left unchanged. An empty
+ name clears the name, and an empty metadata map clears the metadata.
Args:
- metadata: User defined metadata to attach to the devbox for organization.
+ metadata: User defined metadata to replace the Devbox metadata. Omit to leave unchanged or
+ set to an empty map to clear it.
- name: (Optional) A user specified name to give the Devbox.
+ name: A user specified name to give the Devbox. Omit to leave unchanged or set to an
+ empty string to clear it.
extra_headers: Send extra headers
@@ -2188,14 +2191,17 @@ async def update(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
idempotency_key: str | None = None,
) -> DevboxView:
- """
- Updates a devbox by doing a complete update the existing name,metadata fields.
- It does not patch partial values.
+ """Updates the specified Devbox fields.
+
+ Omitted fields are left unchanged. An empty
+ name clears the name, and an empty metadata map clears the metadata.
Args:
- metadata: User defined metadata to attach to the devbox for organization.
+ metadata: User defined metadata to replace the Devbox metadata. Omit to leave unchanged or
+ set to an empty map to clear it.
- name: (Optional) A user specified name to give the Devbox.
+ name: A user specified name to give the Devbox. Omit to leave unchanged or set to an
+ empty string to clear it.
extra_headers: Send extra headers
diff --git a/src/runloop_api_client/types/devbox_update_params.py b/src/runloop_api_client/types/devbox_update_params.py
index 2fbdae310..1e53b01a0 100644
--- a/src/runloop_api_client/types/devbox_update_params.py
+++ b/src/runloop_api_client/types/devbox_update_params.py
@@ -10,7 +10,13 @@
class DevboxUpdateParams(TypedDict, total=False):
metadata: Optional[Dict[str, str]]
- """User defined metadata to attach to the devbox for organization."""
+ """User defined metadata to replace the Devbox metadata.
+
+ Omit to leave unchanged or set to an empty map to clear it.
+ """
name: Optional[str]
- """(Optional) A user specified name to give the Devbox."""
+ """A user specified name to give the Devbox.
+
+ Omit to leave unchanged or set to an empty string to clear it.
+ """
From a931408895286f7f5a91c499bc9cbc069cba0dcf Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Tue, 18 Aug 2026 21:39:08 +0000
Subject: [PATCH 06/11] codegen metadata
---
.stats.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index 360b9980f..12a2b6c9c 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 121
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai/runloop-49fd19a6c2b93ae0af51ae79c2a2a1a67adaf56611840c4d30b651729b090c63.yml
-openapi_spec_hash: 2d30d0e7c9061c5f748fcc915f7af4e9
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai/runloop-093c44f8ed1154c727f281b89d15dcc628edb2fb705c0155ebcdfa48f4a81d48.yml
+openapi_spec_hash: 9fbaad9832de897a6e6e319483ddeb6c
config_hash: 2b0535779b180339d10766ba74a96700
From 1f50f93c26c6696d2a9f81b1b677eb9b7282601a Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Wed, 19 Aug 2026 00:35:38 +0000
Subject: [PATCH 07/11] feat(gateway): first-class BASIC auth mechanism
(#11063)
---
.stats.yml | 4 ++--
.../types/gateway_config_create_params.py | 8 ++++++--
.../types/gateway_config_update_params.py | 8 ++++++--
src/runloop_api_client/types/gateway_config_view.py | 8 ++++++--
4 files changed, 20 insertions(+), 8 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index 12a2b6c9c..f0351c489 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 121
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai/runloop-093c44f8ed1154c727f281b89d15dcc628edb2fb705c0155ebcdfa48f4a81d48.yml
-openapi_spec_hash: 9fbaad9832de897a6e6e319483ddeb6c
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai/runloop-7b9d926e46ee0dca27286795e6d90d3ba75643c897ea21c3ce05a7d999a1d33b.yml
+openapi_spec_hash: 19d9c8ceae252900eca5fbb84eb19285
config_hash: 2b0535779b180339d10766ba74a96700
diff --git a/src/runloop_api_client/types/gateway_config_create_params.py b/src/runloop_api_client/types/gateway_config_create_params.py
index 6a39055e4..dcd8711b9 100644
--- a/src/runloop_api_client/types/gateway_config_create_params.py
+++ b/src/runloop_api_client/types/gateway_config_create_params.py
@@ -35,7 +35,11 @@ class AuthMechanism(TypedDict, total=False):
"""
type: Required[str]
- """The type of authentication mechanism: 'header', 'bearer'."""
+ """The type of authentication mechanism: 'header', 'bearer', or 'basic'.
+
+ For 'basic', store the secret as plain 'user:pass'; the gateway base64-encodes
+ it.
+ """
key: Optional[str]
- """For 'header' type: the header name (e.g., 'x-api-key')."""
+ """Only valid for 'header' type: the header name (e.g., 'x-api-key')."""
diff --git a/src/runloop_api_client/types/gateway_config_update_params.py b/src/runloop_api_client/types/gateway_config_update_params.py
index d639f3a39..5379a61ae 100644
--- a/src/runloop_api_client/types/gateway_config_update_params.py
+++ b/src/runloop_api_client/types/gateway_config_update_params.py
@@ -31,7 +31,11 @@ class AuthMechanism(TypedDict, total=False):
"""
type: Required[str]
- """The type of authentication mechanism: 'header', 'bearer'."""
+ """The type of authentication mechanism: 'header', 'bearer', or 'basic'.
+
+ For 'basic', store the secret as plain 'user:pass'; the gateway base64-encodes
+ it.
+ """
key: Optional[str]
- """For 'header' type: the header name (e.g., 'x-api-key')."""
+ """Only valid for 'header' type: the header name (e.g., 'x-api-key')."""
diff --git a/src/runloop_api_client/types/gateway_config_view.py b/src/runloop_api_client/types/gateway_config_view.py
index c8fcc6e3c..16eb095b4 100644
--- a/src/runloop_api_client/types/gateway_config_view.py
+++ b/src/runloop_api_client/types/gateway_config_view.py
@@ -11,10 +11,14 @@ class AuthMechanism(BaseModel):
"""How credentials should be applied to proxied requests."""
type: str
- """The type of authentication mechanism: 'header', 'bearer'."""
+ """The type of authentication mechanism: 'header', 'bearer', or 'basic'.
+
+ For 'basic', store the secret as plain 'user:pass'; the gateway base64-encodes
+ it.
+ """
key: Optional[str] = None
- """For 'header' type: the header name (e.g., 'x-api-key')."""
+ """Only valid for 'header' type: the header name (e.g., 'x-api-key')."""
class GatewayConfigView(BaseModel):
From 9482e475d0ad6e2b1989ee25f66a759fe839065f Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Wed, 19 Aug 2026 21:49:48 +0000
Subject: [PATCH 08/11] =?UTF-8?q?feat(config):=20custom=5Fheaders=20on=20M?=
=?UTF-8?q?cpConfig/GatewayConfig=20=E2=80=94=20persistence,=20REST,=20val?=
=?UTF-8?q?idation,=20provisioner,=20OpenAPI=20(#11096)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.stats.yml | 6 ++--
api.md | 1 +
.../resources/gateway_configs.py | 23 +++++++++++++-
.../resources/mcp_configs.py | 23 +++++++++++++-
src/runloop_api_client/types/__init__.py | 1 +
.../types/gateway_config_create_params.py | 10 ++++++-
.../types/gateway_config_update_params.py | 11 ++++++-
.../types/gateway_config_view.py | 10 ++++++-
.../types/mcp_config_create_params.py | 9 +++++-
.../types/mcp_config_update_params.py | 10 ++++++-
.../types/mcp_config_view.py | 8 +++++
.../types/shared/__init__.py | 1 +
.../types/shared/custom_header.py | 29 ++++++++++++++++++
.../types/shared_params/__init__.py | 1 +
.../types/shared_params/custom_header.py | 30 +++++++++++++++++++
tests/api_resources/test_gateway_configs.py | 28 +++++++++++++++++
tests/api_resources/test_mcp_configs.py | 28 +++++++++++++++++
17 files changed, 219 insertions(+), 10 deletions(-)
create mode 100644 src/runloop_api_client/types/shared/custom_header.py
create mode 100644 src/runloop_api_client/types/shared_params/custom_header.py
diff --git a/.stats.yml b/.stats.yml
index f0351c489..3a806956b 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 121
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai/runloop-7b9d926e46ee0dca27286795e6d90d3ba75643c897ea21c3ce05a7d999a1d33b.yml
-openapi_spec_hash: 19d9c8ceae252900eca5fbb84eb19285
-config_hash: 2b0535779b180339d10766ba74a96700
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai/runloop-ae0f5383aa4cb3defa2a011375d3524b89a9fed30e2ef6c8d225192a4ea8eb1f.yml
+openapi_spec_hash: a4f906d2af71050ead8673bad44a65d7
+config_hash: 25409d286d89763213d1155725bba77a
diff --git a/api.md b/api.md
index 584c2d2f8..cc17bbabc 100644
--- a/api.md
+++ b/api.md
@@ -7,6 +7,7 @@ from runloop_api_client.types import (
AgentSource,
BrokerMount,
CodeMountParameters,
+ CustomHeader,
LaunchParameters,
LifecycleConfiguration,
LifecycleHooks,
diff --git a/src/runloop_api_client/resources/gateway_configs.py b/src/runloop_api_client/resources/gateway_configs.py
index 34f0989cf..780261a3d 100644
--- a/src/runloop_api_client/resources/gateway_configs.py
+++ b/src/runloop_api_client/resources/gateway_configs.py
@@ -2,7 +2,7 @@
from __future__ import annotations
-from typing import Optional
+from typing import Iterable, Optional
import httpx
@@ -20,6 +20,7 @@
from ..pagination import SyncGatewayConfigsCursorIDPage, AsyncGatewayConfigsCursorIDPage
from .._base_client import AsyncPaginator, make_request_options
from ..types.gateway_config_view import GatewayConfigView
+from ..types.shared_params.custom_header import CustomHeader
__all__ = ["GatewayConfigsResource", "AsyncGatewayConfigsResource"]
@@ -50,6 +51,7 @@ def create(
auth_mechanism: gateway_config_create_params.AuthMechanism,
endpoint: str,
name: str,
+ custom_headers: Optional[Iterable[CustomHeader]] | Omit = omit,
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.
@@ -73,6 +75,9 @@ def create(
name: The human-readable name for the GatewayConfig. Must be unique within your
account.
+ custom_headers: Additional headers applied to proxied requests after the auth mechanism. At most
+ 8 entries.
+
description: Optional description for this gateway configuration.
extra_headers: Send extra headers
@@ -92,6 +97,7 @@ def create(
"auth_mechanism": auth_mechanism,
"endpoint": endpoint,
"name": name,
+ "custom_headers": custom_headers,
"description": description,
},
gateway_config_create_params.GatewayConfigCreateParams,
@@ -144,6 +150,7 @@ def update(
id: str,
*,
auth_mechanism: Optional[gateway_config_update_params.AuthMechanism] | Omit = omit,
+ custom_headers: Optional[Iterable[CustomHeader]] | Omit = omit,
description: Optional[str] | Omit = omit,
endpoint: Optional[str] | Omit = omit,
name: Optional[str] | Omit = omit,
@@ -163,6 +170,9 @@ def update(
auth_mechanism: Defines how credentials are applied to HTTP requests when proxying through the
gateway.
+ custom_headers: New list of additional headers. Replaces the existing list entirely; use an
+ empty list to clear all custom headers. At most 8 entries.
+
description: New description for this gateway configuration.
endpoint: New target endpoint URL (e.g., 'https://api.anthropic.com').
@@ -186,6 +196,7 @@ def update(
body=maybe_transform(
{
"auth_mechanism": auth_mechanism,
+ "custom_headers": custom_headers,
"description": description,
"endpoint": endpoint,
"name": name,
@@ -335,6 +346,7 @@ async def create(
auth_mechanism: gateway_config_create_params.AuthMechanism,
endpoint: str,
name: str,
+ custom_headers: Optional[Iterable[CustomHeader]] | Omit = omit,
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.
@@ -358,6 +370,9 @@ async def create(
name: The human-readable name for the GatewayConfig. Must be unique within your
account.
+ custom_headers: Additional headers applied to proxied requests after the auth mechanism. At most
+ 8 entries.
+
description: Optional description for this gateway configuration.
extra_headers: Send extra headers
@@ -377,6 +392,7 @@ async def create(
"auth_mechanism": auth_mechanism,
"endpoint": endpoint,
"name": name,
+ "custom_headers": custom_headers,
"description": description,
},
gateway_config_create_params.GatewayConfigCreateParams,
@@ -429,6 +445,7 @@ async def update(
id: str,
*,
auth_mechanism: Optional[gateway_config_update_params.AuthMechanism] | Omit = omit,
+ custom_headers: Optional[Iterable[CustomHeader]] | Omit = omit,
description: Optional[str] | Omit = omit,
endpoint: Optional[str] | Omit = omit,
name: Optional[str] | Omit = omit,
@@ -448,6 +465,9 @@ async def update(
auth_mechanism: Defines how credentials are applied to HTTP requests when proxying through the
gateway.
+ custom_headers: New list of additional headers. Replaces the existing list entirely; use an
+ empty list to clear all custom headers. At most 8 entries.
+
description: New description for this gateway configuration.
endpoint: New target endpoint URL (e.g., 'https://api.anthropic.com').
@@ -471,6 +491,7 @@ async def update(
body=await async_maybe_transform(
{
"auth_mechanism": auth_mechanism,
+ "custom_headers": custom_headers,
"description": description,
"endpoint": endpoint,
"name": name,
diff --git a/src/runloop_api_client/resources/mcp_configs.py b/src/runloop_api_client/resources/mcp_configs.py
index e96647c9c..6951f401f 100644
--- a/src/runloop_api_client/resources/mcp_configs.py
+++ b/src/runloop_api_client/resources/mcp_configs.py
@@ -2,7 +2,7 @@
from __future__ import annotations
-from typing import Optional
+from typing import Iterable, Optional
import httpx
@@ -20,6 +20,7 @@
from ..pagination import SyncMcpConfigsCursorIDPage, AsyncMcpConfigsCursorIDPage
from .._base_client import AsyncPaginator, make_request_options
from ..types.mcp_config_view import McpConfigView
+from ..types.shared_params.custom_header import CustomHeader
__all__ = ["McpConfigsResource", "AsyncMcpConfigsResource"]
@@ -50,6 +51,7 @@ def create(
allowed_tools: SequenceNotStr[str],
endpoint: str,
name: str,
+ custom_headers: Optional[Iterable[CustomHeader]] | Omit = omit,
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.
@@ -75,6 +77,9 @@ def create(
The first segment before '-' is used as the service name for tool routing (e.g.,
'github-readonly' uses 'github' as the service name).
+ custom_headers: Additional headers applied to upstream requests after the credential. At most 8
+ entries.
+
description: Optional description for this MCP configuration.
extra_headers: Send extra headers
@@ -94,6 +99,7 @@ def create(
"allowed_tools": allowed_tools,
"endpoint": endpoint,
"name": name,
+ "custom_headers": custom_headers,
"description": description,
},
mcp_config_create_params.McpConfigCreateParams,
@@ -146,6 +152,7 @@ def update(
id: str,
*,
allowed_tools: Optional[SequenceNotStr[str]] | Omit = omit,
+ custom_headers: Optional[Iterable[CustomHeader]] | Omit = omit,
description: Optional[str] | Omit = omit,
endpoint: Optional[str] | Omit = omit,
name: Optional[str] | Omit = omit,
@@ -165,6 +172,9 @@ def update(
allowed_tools: New glob patterns specifying which tools are allowed. Examples: ['*'] for all
tools, ['github.search_*'] for specific patterns.
+ custom_headers: New list of additional headers. Replaces the existing list entirely; use an
+ empty list to clear all custom headers. At most 8 entries.
+
description: New description for this MCP configuration.
endpoint: New target MCP server endpoint URL.
@@ -188,6 +198,7 @@ def update(
body=maybe_transform(
{
"allowed_tools": allowed_tools,
+ "custom_headers": custom_headers,
"description": description,
"endpoint": endpoint,
"name": name,
@@ -336,6 +347,7 @@ async def create(
allowed_tools: SequenceNotStr[str],
endpoint: str,
name: str,
+ custom_headers: Optional[Iterable[CustomHeader]] | Omit = omit,
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.
@@ -361,6 +373,9 @@ async def create(
The first segment before '-' is used as the service name for tool routing (e.g.,
'github-readonly' uses 'github' as the service name).
+ custom_headers: Additional headers applied to upstream requests after the credential. At most 8
+ entries.
+
description: Optional description for this MCP configuration.
extra_headers: Send extra headers
@@ -380,6 +395,7 @@ async def create(
"allowed_tools": allowed_tools,
"endpoint": endpoint,
"name": name,
+ "custom_headers": custom_headers,
"description": description,
},
mcp_config_create_params.McpConfigCreateParams,
@@ -432,6 +448,7 @@ async def update(
id: str,
*,
allowed_tools: Optional[SequenceNotStr[str]] | Omit = omit,
+ custom_headers: Optional[Iterable[CustomHeader]] | Omit = omit,
description: Optional[str] | Omit = omit,
endpoint: Optional[str] | Omit = omit,
name: Optional[str] | Omit = omit,
@@ -451,6 +468,9 @@ async def update(
allowed_tools: New glob patterns specifying which tools are allowed. Examples: ['*'] for all
tools, ['github.search_*'] for specific patterns.
+ custom_headers: New list of additional headers. Replaces the existing list entirely; use an
+ empty list to clear all custom headers. At most 8 entries.
+
description: New description for this MCP configuration.
endpoint: New target MCP server endpoint URL.
@@ -474,6 +494,7 @@ async def update(
body=await async_maybe_transform(
{
"allowed_tools": allowed_tools,
+ "custom_headers": custom_headers,
"description": description,
"endpoint": endpoint,
"name": name,
diff --git a/src/runloop_api_client/types/__init__.py b/src/runloop_api_client/types/__init__.py
index 0bda186a3..70f0b3da8 100644
--- a/src/runloop_api_client/types/__init__.py
+++ b/src/runloop_api_client/types/__init__.py
@@ -10,6 +10,7 @@
AgentSource as AgentSource,
BrokerMount as BrokerMount,
ObjectMount as ObjectMount,
+ CustomHeader as CustomHeader,
LifecycleHooks as LifecycleHooks,
ResumeTriggers as ResumeTriggers,
LaunchParameters as LaunchParameters,
diff --git a/src/runloop_api_client/types/gateway_config_create_params.py b/src/runloop_api_client/types/gateway_config_create_params.py
index dcd8711b9..f523529ce 100644
--- a/src/runloop_api_client/types/gateway_config_create_params.py
+++ b/src/runloop_api_client/types/gateway_config_create_params.py
@@ -2,9 +2,11 @@
from __future__ import annotations
-from typing import Optional
+from typing import Iterable, Optional
from typing_extensions import Required, TypedDict
+from .shared_params.custom_header import CustomHeader
+
__all__ = ["GatewayConfigCreateParams", "AuthMechanism"]
@@ -24,6 +26,12 @@ class GatewayConfigCreateParams(TypedDict, total=False):
Must be unique within your account.
"""
+ custom_headers: Optional[Iterable[CustomHeader]]
+ """Additional headers applied to proxied requests after the auth mechanism.
+
+ At most 8 entries.
+ """
+
description: Optional[str]
"""Optional description for this gateway configuration."""
diff --git a/src/runloop_api_client/types/gateway_config_update_params.py b/src/runloop_api_client/types/gateway_config_update_params.py
index 5379a61ae..53093bb5d 100644
--- a/src/runloop_api_client/types/gateway_config_update_params.py
+++ b/src/runloop_api_client/types/gateway_config_update_params.py
@@ -2,9 +2,11 @@
from __future__ import annotations
-from typing import Optional
+from typing import Iterable, Optional
from typing_extensions import Required, TypedDict
+from .shared_params.custom_header import CustomHeader
+
__all__ = ["GatewayConfigUpdateParams", "AuthMechanism"]
@@ -15,6 +17,13 @@ class GatewayConfigUpdateParams(TypedDict, total=False):
gateway.
"""
+ custom_headers: Optional[Iterable[CustomHeader]]
+ """New list of additional headers.
+
+ Replaces the existing list entirely; use an empty list to clear all custom
+ headers. At most 8 entries.
+ """
+
description: Optional[str]
"""New description for this gateway configuration."""
diff --git a/src/runloop_api_client/types/gateway_config_view.py b/src/runloop_api_client/types/gateway_config_view.py
index 16eb095b4..154a75e1d 100644
--- a/src/runloop_api_client/types/gateway_config_view.py
+++ b/src/runloop_api_client/types/gateway_config_view.py
@@ -1,8 +1,9 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-from typing import Optional
+from typing import List, Optional
from .._models import BaseModel
+from .shared.custom_header import CustomHeader
__all__ = ["GatewayConfigView", "AuthMechanism"]
@@ -47,5 +48,12 @@ class GatewayConfigView(BaseModel):
account_id: Optional[str] = None
"""The account ID that owns this config."""
+ custom_headers: Optional[List[CustomHeader]] = None
+ """Additional headers applied to proxied requests after the auth mechanism.
+
+ Secret-backed entries reference the secret by 'sec\\__' id; values are never
+ returned.
+ """
+
description: Optional[str] = None
"""Optional description for this gateway configuration."""
diff --git a/src/runloop_api_client/types/mcp_config_create_params.py b/src/runloop_api_client/types/mcp_config_create_params.py
index 4a2dcd9e2..65789fe68 100644
--- a/src/runloop_api_client/types/mcp_config_create_params.py
+++ b/src/runloop_api_client/types/mcp_config_create_params.py
@@ -2,10 +2,11 @@
from __future__ import annotations
-from typing import Optional
+from typing import Iterable, Optional
from typing_extensions import Required, TypedDict
from .._types import SequenceNotStr
+from .shared_params.custom_header import CustomHeader
__all__ = ["McpConfigCreateParams"]
@@ -29,5 +30,11 @@ class McpConfigCreateParams(TypedDict, total=False):
service name).
"""
+ custom_headers: Optional[Iterable[CustomHeader]]
+ """Additional headers applied to upstream requests after the credential.
+
+ At most 8 entries.
+ """
+
description: Optional[str]
"""Optional description for this MCP configuration."""
diff --git a/src/runloop_api_client/types/mcp_config_update_params.py b/src/runloop_api_client/types/mcp_config_update_params.py
index 234ca939f..11aa6b495 100644
--- a/src/runloop_api_client/types/mcp_config_update_params.py
+++ b/src/runloop_api_client/types/mcp_config_update_params.py
@@ -2,10 +2,11 @@
from __future__ import annotations
-from typing import Optional
+from typing import Iterable, Optional
from typing_extensions import TypedDict
from .._types import SequenceNotStr
+from .shared_params.custom_header import CustomHeader
__all__ = ["McpConfigUpdateParams"]
@@ -17,6 +18,13 @@ class McpConfigUpdateParams(TypedDict, total=False):
Examples: ['*'] for all tools, ['github.search_*'] for specific patterns.
"""
+ custom_headers: Optional[Iterable[CustomHeader]]
+ """New list of additional headers.
+
+ Replaces the existing list entirely; use an empty list to clear all custom
+ headers. At most 8 entries.
+ """
+
description: Optional[str]
"""New description for this MCP configuration."""
diff --git a/src/runloop_api_client/types/mcp_config_view.py b/src/runloop_api_client/types/mcp_config_view.py
index 988537a59..87f67e318 100644
--- a/src/runloop_api_client/types/mcp_config_view.py
+++ b/src/runloop_api_client/types/mcp_config_view.py
@@ -3,6 +3,7 @@
from typing import List, Optional
from .._models import BaseModel
+from .shared.custom_header import CustomHeader
__all__ = ["McpConfigView"]
@@ -30,5 +31,12 @@ class McpConfigView(BaseModel):
name: str
"""The human-readable name of the McpConfig. Unique per account."""
+ custom_headers: Optional[List[CustomHeader]] = None
+ """Additional headers applied to upstream requests after the credential.
+
+ Secret-backed entries reference the secret by 'sec\\__' id; values are never
+ returned.
+ """
+
description: Optional[str] = None
"""Optional description for this MCP configuration."""
diff --git a/src/runloop_api_client/types/shared/__init__.py b/src/runloop_api_client/types/shared/__init__.py
index 5463ca25c..2ebb9e2c5 100644
--- a/src/runloop_api_client/types/shared/__init__.py
+++ b/src/runloop_api_client/types/shared/__init__.py
@@ -7,6 +7,7 @@
from .agent_source import AgentSource as AgentSource
from .broker_mount import BrokerMount as BrokerMount
from .object_mount import ObjectMount as ObjectMount
+from .custom_header import CustomHeader as CustomHeader
from .lifecycle_hooks import LifecycleHooks as LifecycleHooks
from .resume_triggers import ResumeTriggers as ResumeTriggers
from .launch_parameters import LaunchParameters as LaunchParameters
diff --git a/src/runloop_api_client/types/shared/custom_header.py b/src/runloop_api_client/types/shared/custom_header.py
new file mode 100644
index 000000000..0888c074a
--- /dev/null
+++ b/src/runloop_api_client/types/shared/custom_header.py
@@ -0,0 +1,29 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+
+from ..._models import BaseModel
+
+__all__ = ["CustomHeader"]
+
+
+class CustomHeader(BaseModel):
+ """
+ An additional header applied to upstream requests alongside the primary credential. The value comes from an account secret or a literal string; exactly one of 'secret' and 'value' must be set.
+ """
+
+ name: str
+ """The header name (e.g., 'DD-APPLICATION-KEY')."""
+
+ secret: Optional[str] = None
+ """Account secret providing the header value.
+
+ Accepts a secret name or 'sec*' id on writes; reads always return the 'sec*' id.
+ """
+
+ value: Optional[str] = None
+ """Literal header value.
+
+ Stored in plaintext and returned by reads - use 'secret' for credentials or
+ other sensitive values.
+ """
diff --git a/src/runloop_api_client/types/shared_params/__init__.py b/src/runloop_api_client/types/shared_params/__init__.py
index 5463ca25c..2ebb9e2c5 100644
--- a/src/runloop_api_client/types/shared_params/__init__.py
+++ b/src/runloop_api_client/types/shared_params/__init__.py
@@ -7,6 +7,7 @@
from .agent_source import AgentSource as AgentSource
from .broker_mount import BrokerMount as BrokerMount
from .object_mount import ObjectMount as ObjectMount
+from .custom_header import CustomHeader as CustomHeader
from .lifecycle_hooks import LifecycleHooks as LifecycleHooks
from .resume_triggers import ResumeTriggers as ResumeTriggers
from .launch_parameters import LaunchParameters as LaunchParameters
diff --git a/src/runloop_api_client/types/shared_params/custom_header.py b/src/runloop_api_client/types/shared_params/custom_header.py
new file mode 100644
index 000000000..1b249be42
--- /dev/null
+++ b/src/runloop_api_client/types/shared_params/custom_header.py
@@ -0,0 +1,30 @@
+# 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
+
+__all__ = ["CustomHeader"]
+
+
+class CustomHeader(TypedDict, total=False):
+ """
+ An additional header applied to upstream requests alongside the primary credential. The value comes from an account secret or a literal string; exactly one of 'secret' and 'value' must be set.
+ """
+
+ name: Required[str]
+ """The header name (e.g., 'DD-APPLICATION-KEY')."""
+
+ secret: Optional[str]
+ """Account secret providing the header value.
+
+ Accepts a secret name or 'sec*' id on writes; reads always return the 'sec*' id.
+ """
+
+ value: Optional[str]
+ """Literal header value.
+
+ Stored in plaintext and returned by reads - use 'secret' for credentials or
+ other sensitive values.
+ """
diff --git a/tests/api_resources/test_gateway_configs.py b/tests/api_resources/test_gateway_configs.py
index 64e35b11f..5111aa542 100644
--- a/tests/api_resources/test_gateway_configs.py
+++ b/tests/api_resources/test_gateway_configs.py
@@ -38,6 +38,13 @@ def test_method_create_with_all_params(self, client: Runloop) -> None:
},
endpoint="endpoint",
name="name",
+ custom_headers=[
+ {
+ "name": "name",
+ "secret": "secret",
+ "value": "value",
+ }
+ ],
description="description",
)
assert_matches_type(GatewayConfigView, gateway_config, path=["response"])
@@ -123,6 +130,13 @@ def test_method_update_with_all_params(self, client: Runloop) -> None:
"type": "type",
"key": "key",
},
+ custom_headers=[
+ {
+ "name": "name",
+ "secret": "secret",
+ "value": "value",
+ }
+ ],
description="description",
endpoint="endpoint",
name="name",
@@ -259,6 +273,13 @@ async def test_method_create_with_all_params(self, async_client: AsyncRunloop) -
},
endpoint="endpoint",
name="name",
+ custom_headers=[
+ {
+ "name": "name",
+ "secret": "secret",
+ "value": "value",
+ }
+ ],
description="description",
)
assert_matches_type(GatewayConfigView, gateway_config, path=["response"])
@@ -344,6 +365,13 @@ async def test_method_update_with_all_params(self, async_client: AsyncRunloop) -
"type": "type",
"key": "key",
},
+ custom_headers=[
+ {
+ "name": "name",
+ "secret": "secret",
+ "value": "value",
+ }
+ ],
description="description",
endpoint="endpoint",
name="name",
diff --git a/tests/api_resources/test_mcp_configs.py b/tests/api_resources/test_mcp_configs.py
index 2e85d3d45..9ccf2943a 100644
--- a/tests/api_resources/test_mcp_configs.py
+++ b/tests/api_resources/test_mcp_configs.py
@@ -35,6 +35,13 @@ def test_method_create_with_all_params(self, client: Runloop) -> None:
allowed_tools=["string"],
endpoint="endpoint",
name="name",
+ custom_headers=[
+ {
+ "name": "name",
+ "secret": "secret",
+ "value": "value",
+ }
+ ],
description="description",
)
assert_matches_type(McpConfigView, mcp_config, path=["response"])
@@ -117,6 +124,13 @@ def test_method_update_with_all_params(self, client: Runloop) -> None:
mcp_config = client.mcp_configs.update(
id="id",
allowed_tools=["string"],
+ custom_headers=[
+ {
+ "name": "name",
+ "secret": "secret",
+ "value": "value",
+ }
+ ],
description="description",
endpoint="endpoint",
name="name",
@@ -250,6 +264,13 @@ async def test_method_create_with_all_params(self, async_client: AsyncRunloop) -
allowed_tools=["string"],
endpoint="endpoint",
name="name",
+ custom_headers=[
+ {
+ "name": "name",
+ "secret": "secret",
+ "value": "value",
+ }
+ ],
description="description",
)
assert_matches_type(McpConfigView, mcp_config, path=["response"])
@@ -332,6 +353,13 @@ async def test_method_update_with_all_params(self, async_client: AsyncRunloop) -
mcp_config = await async_client.mcp_configs.update(
id="id",
allowed_tools=["string"],
+ custom_headers=[
+ {
+ "name": "name",
+ "secret": "secret",
+ "value": "value",
+ }
+ ],
description="description",
endpoint="endpoint",
name="name",
From 4dd9edd5abbdfab0466826db8d18cbd2efae7b31 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Wed, 19 Aug 2026 23:48:56 +0000
Subject: [PATCH 09/11] feat(mcp): configurable auth_mechanism on McpConfig,
shared shape with gateway (#11113)
---
.stats.yml | 6 ++---
api.md | 1 +
.../resources/gateway_configs.py | 17 +++++++------
.../resources/mcp_configs.py | 21 ++++++++++++++++
src/runloop_api_client/types/__init__.py | 1 +
.../types/gateway_config_create_params.py | 20 ++-------------
.../types/gateway_config_update_params.py | 25 ++++---------------
.../types/gateway_config_view.py | 17 ++-----------
.../types/mcp_config_create_params.py | 7 ++++++
.../types/mcp_config_update_params.py | 7 ++++++
.../types/mcp_config_view.py | 4 +++
.../types/shared/__init__.py | 1 +
.../types/shared/auth_mechanism.py | 22 ++++++++++++++++
.../types/shared_params/__init__.py | 1 +
.../types/shared_params/auth_mechanism.py | 23 +++++++++++++++++
tests/api_resources/test_mcp_configs.py | 16 ++++++++++++
16 files changed, 125 insertions(+), 64 deletions(-)
create mode 100644 src/runloop_api_client/types/shared/auth_mechanism.py
create mode 100644 src/runloop_api_client/types/shared_params/auth_mechanism.py
diff --git a/.stats.yml b/.stats.yml
index 3a806956b..037095a6e 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 121
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai/runloop-ae0f5383aa4cb3defa2a011375d3524b89a9fed30e2ef6c8d225192a4ea8eb1f.yml
-openapi_spec_hash: a4f906d2af71050ead8673bad44a65d7
-config_hash: 25409d286d89763213d1155725bba77a
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai/runloop-2b4c651a27238a14ac7bd03ba582183d14f9622bdb5ea292c2cea97acc4c727e.yml
+openapi_spec_hash: 8a933236116d07c8a060b6eadd205feb
+config_hash: 7afcebea2dd638ed0cf0d5367f820f73
diff --git a/api.md b/api.md
index cc17bbabc..af9a3c239 100644
--- a/api.md
+++ b/api.md
@@ -5,6 +5,7 @@ from runloop_api_client.types import (
AfterIdle,
AgentMount,
AgentSource,
+ AuthMechanism,
BrokerMount,
CodeMountParameters,
CustomHeader,
diff --git a/src/runloop_api_client/resources/gateway_configs.py b/src/runloop_api_client/resources/gateway_configs.py
index 780261a3d..36e00fd2c 100644
--- a/src/runloop_api_client/resources/gateway_configs.py
+++ b/src/runloop_api_client/resources/gateway_configs.py
@@ -21,6 +21,7 @@
from .._base_client import AsyncPaginator, make_request_options
from ..types.gateway_config_view import GatewayConfigView
from ..types.shared_params.custom_header import CustomHeader
+from ..types.shared_params.auth_mechanism import AuthMechanism
__all__ = ["GatewayConfigsResource", "AsyncGatewayConfigsResource"]
@@ -48,7 +49,7 @@ def with_streaming_response(self) -> GatewayConfigsResourceWithStreamingResponse
def create(
self,
*,
- auth_mechanism: gateway_config_create_params.AuthMechanism,
+ auth_mechanism: AuthMechanism,
endpoint: str,
name: str,
custom_headers: Optional[Iterable[CustomHeader]] | Omit = omit,
@@ -149,7 +150,7 @@ def update(
self,
id: str,
*,
- auth_mechanism: Optional[gateway_config_update_params.AuthMechanism] | Omit = omit,
+ auth_mechanism: Optional[AuthMechanism] | Omit = omit,
custom_headers: Optional[Iterable[CustomHeader]] | Omit = omit,
description: Optional[str] | Omit = omit,
endpoint: Optional[str] | Omit = omit,
@@ -167,8 +168,8 @@ def update(
All fields are optional.
Args:
- auth_mechanism: Defines how credentials are applied to HTTP requests when proxying through the
- gateway.
+ auth_mechanism: Defines how the primary credential is applied to requests proxied to the
+ upstream.
custom_headers: New list of additional headers. Replaces the existing list entirely; use an
empty list to clear all custom headers. At most 8 entries.
@@ -343,7 +344,7 @@ def with_streaming_response(self) -> AsyncGatewayConfigsResourceWithStreamingRes
async def create(
self,
*,
- auth_mechanism: gateway_config_create_params.AuthMechanism,
+ auth_mechanism: AuthMechanism,
endpoint: str,
name: str,
custom_headers: Optional[Iterable[CustomHeader]] | Omit = omit,
@@ -444,7 +445,7 @@ async def update(
self,
id: str,
*,
- auth_mechanism: Optional[gateway_config_update_params.AuthMechanism] | Omit = omit,
+ auth_mechanism: Optional[AuthMechanism] | Omit = omit,
custom_headers: Optional[Iterable[CustomHeader]] | Omit = omit,
description: Optional[str] | Omit = omit,
endpoint: Optional[str] | Omit = omit,
@@ -462,8 +463,8 @@ async def update(
All fields are optional.
Args:
- auth_mechanism: Defines how credentials are applied to HTTP requests when proxying through the
- gateway.
+ auth_mechanism: Defines how the primary credential is applied to requests proxied to the
+ upstream.
custom_headers: New list of additional headers. Replaces the existing list entirely; use an
empty list to clear all custom headers. At most 8 entries.
diff --git a/src/runloop_api_client/resources/mcp_configs.py b/src/runloop_api_client/resources/mcp_configs.py
index 6951f401f..c8b37a096 100644
--- a/src/runloop_api_client/resources/mcp_configs.py
+++ b/src/runloop_api_client/resources/mcp_configs.py
@@ -21,6 +21,7 @@
from .._base_client import AsyncPaginator, make_request_options
from ..types.mcp_config_view import McpConfigView
from ..types.shared_params.custom_header import CustomHeader
+from ..types.shared_params.auth_mechanism import AuthMechanism
__all__ = ["McpConfigsResource", "AsyncMcpConfigsResource"]
@@ -51,6 +52,7 @@ def create(
allowed_tools: SequenceNotStr[str],
endpoint: str,
name: str,
+ auth_mechanism: Optional[AuthMechanism] | Omit = omit,
custom_headers: Optional[Iterable[CustomHeader]] | Omit = omit,
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.
@@ -77,6 +79,9 @@ def create(
The first segment before '-' is used as the service name for tool routing (e.g.,
'github-readonly' uses 'github' as the service name).
+ auth_mechanism: Defines how the primary credential is applied to requests proxied to the
+ upstream.
+
custom_headers: Additional headers applied to upstream requests after the credential. At most 8
entries.
@@ -99,6 +104,7 @@ def create(
"allowed_tools": allowed_tools,
"endpoint": endpoint,
"name": name,
+ "auth_mechanism": auth_mechanism,
"custom_headers": custom_headers,
"description": description,
},
@@ -152,6 +158,7 @@ def update(
id: str,
*,
allowed_tools: Optional[SequenceNotStr[str]] | Omit = omit,
+ auth_mechanism: Optional[AuthMechanism] | Omit = omit,
custom_headers: Optional[Iterable[CustomHeader]] | Omit = omit,
description: Optional[str] | Omit = omit,
endpoint: Optional[str] | Omit = omit,
@@ -172,6 +179,9 @@ def update(
allowed_tools: New glob patterns specifying which tools are allowed. Examples: ['*'] for all
tools, ['github.search_*'] for specific patterns.
+ auth_mechanism: Defines how the primary credential is applied to requests proxied to the
+ upstream.
+
custom_headers: New list of additional headers. Replaces the existing list entirely; use an
empty list to clear all custom headers. At most 8 entries.
@@ -198,6 +208,7 @@ def update(
body=maybe_transform(
{
"allowed_tools": allowed_tools,
+ "auth_mechanism": auth_mechanism,
"custom_headers": custom_headers,
"description": description,
"endpoint": endpoint,
@@ -347,6 +358,7 @@ async def create(
allowed_tools: SequenceNotStr[str],
endpoint: str,
name: str,
+ auth_mechanism: Optional[AuthMechanism] | Omit = omit,
custom_headers: Optional[Iterable[CustomHeader]] | Omit = omit,
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.
@@ -373,6 +385,9 @@ async def create(
The first segment before '-' is used as the service name for tool routing (e.g.,
'github-readonly' uses 'github' as the service name).
+ auth_mechanism: Defines how the primary credential is applied to requests proxied to the
+ upstream.
+
custom_headers: Additional headers applied to upstream requests after the credential. At most 8
entries.
@@ -395,6 +410,7 @@ async def create(
"allowed_tools": allowed_tools,
"endpoint": endpoint,
"name": name,
+ "auth_mechanism": auth_mechanism,
"custom_headers": custom_headers,
"description": description,
},
@@ -448,6 +464,7 @@ async def update(
id: str,
*,
allowed_tools: Optional[SequenceNotStr[str]] | Omit = omit,
+ auth_mechanism: Optional[AuthMechanism] | Omit = omit,
custom_headers: Optional[Iterable[CustomHeader]] | Omit = omit,
description: Optional[str] | Omit = omit,
endpoint: Optional[str] | Omit = omit,
@@ -468,6 +485,9 @@ async def update(
allowed_tools: New glob patterns specifying which tools are allowed. Examples: ['*'] for all
tools, ['github.search_*'] for specific patterns.
+ auth_mechanism: Defines how the primary credential is applied to requests proxied to the
+ upstream.
+
custom_headers: New list of additional headers. Replaces the existing list entirely; use an
empty list to clear all custom headers. At most 8 entries.
@@ -494,6 +514,7 @@ async def update(
body=await async_maybe_transform(
{
"allowed_tools": allowed_tools,
+ "auth_mechanism": auth_mechanism,
"custom_headers": custom_headers,
"description": description,
"endpoint": endpoint,
diff --git a/src/runloop_api_client/types/__init__.py b/src/runloop_api_client/types/__init__.py
index 70f0b3da8..d7c17637c 100644
--- a/src/runloop_api_client/types/__init__.py
+++ b/src/runloop_api_client/types/__init__.py
@@ -11,6 +11,7 @@
BrokerMount as BrokerMount,
ObjectMount as ObjectMount,
CustomHeader as CustomHeader,
+ AuthMechanism as AuthMechanism,
LifecycleHooks as LifecycleHooks,
ResumeTriggers as ResumeTriggers,
LaunchParameters as LaunchParameters,
diff --git a/src/runloop_api_client/types/gateway_config_create_params.py b/src/runloop_api_client/types/gateway_config_create_params.py
index f523529ce..653a62940 100644
--- a/src/runloop_api_client/types/gateway_config_create_params.py
+++ b/src/runloop_api_client/types/gateway_config_create_params.py
@@ -6,8 +6,9 @@
from typing_extensions import Required, TypedDict
from .shared_params.custom_header import CustomHeader
+from .shared_params.auth_mechanism import AuthMechanism
-__all__ = ["GatewayConfigCreateParams", "AuthMechanism"]
+__all__ = ["GatewayConfigCreateParams"]
class GatewayConfigCreateParams(TypedDict, total=False):
@@ -34,20 +35,3 @@ class GatewayConfigCreateParams(TypedDict, total=False):
description: Optional[str]
"""Optional description for this gateway configuration."""
-
-
-class AuthMechanism(TypedDict, total=False):
- """How credentials should be applied to proxied requests.
-
- Specify the type ('header', 'bearer') and optional key field.
- """
-
- type: Required[str]
- """The type of authentication mechanism: 'header', 'bearer', or 'basic'.
-
- For 'basic', store the secret as plain 'user:pass'; the gateway base64-encodes
- it.
- """
-
- key: Optional[str]
- """Only valid for 'header' type: the header name (e.g., 'x-api-key')."""
diff --git a/src/runloop_api_client/types/gateway_config_update_params.py b/src/runloop_api_client/types/gateway_config_update_params.py
index 53093bb5d..64078933e 100644
--- a/src/runloop_api_client/types/gateway_config_update_params.py
+++ b/src/runloop_api_client/types/gateway_config_update_params.py
@@ -3,18 +3,19 @@
from __future__ import annotations
from typing import Iterable, Optional
-from typing_extensions import Required, TypedDict
+from typing_extensions import TypedDict
from .shared_params.custom_header import CustomHeader
+from .shared_params.auth_mechanism import AuthMechanism
-__all__ = ["GatewayConfigUpdateParams", "AuthMechanism"]
+__all__ = ["GatewayConfigUpdateParams"]
class GatewayConfigUpdateParams(TypedDict, total=False):
auth_mechanism: Optional[AuthMechanism]
"""
- Defines how credentials are applied to HTTP requests when proxying through the
- gateway.
+ Defines how the primary credential is applied to requests proxied to the
+ upstream.
"""
custom_headers: Optional[Iterable[CustomHeader]]
@@ -32,19 +33,3 @@ class GatewayConfigUpdateParams(TypedDict, total=False):
name: Optional[str]
"""New name for the GatewayConfig. Must be unique within your account."""
-
-
-class AuthMechanism(TypedDict, total=False):
- """
- Defines how credentials are applied to HTTP requests when proxying through the gateway.
- """
-
- type: Required[str]
- """The type of authentication mechanism: 'header', 'bearer', or 'basic'.
-
- For 'basic', store the secret as plain 'user:pass'; the gateway base64-encodes
- it.
- """
-
- key: Optional[str]
- """Only valid for 'header' type: the header name (e.g., 'x-api-key')."""
diff --git a/src/runloop_api_client/types/gateway_config_view.py b/src/runloop_api_client/types/gateway_config_view.py
index 154a75e1d..7f7cf62d7 100644
--- a/src/runloop_api_client/types/gateway_config_view.py
+++ b/src/runloop_api_client/types/gateway_config_view.py
@@ -4,22 +4,9 @@
from .._models import BaseModel
from .shared.custom_header import CustomHeader
+from .shared.auth_mechanism import AuthMechanism
-__all__ = ["GatewayConfigView", "AuthMechanism"]
-
-
-class AuthMechanism(BaseModel):
- """How credentials should be applied to proxied requests."""
-
- type: str
- """The type of authentication mechanism: 'header', 'bearer', or 'basic'.
-
- For 'basic', store the secret as plain 'user:pass'; the gateway base64-encodes
- it.
- """
-
- key: Optional[str] = None
- """Only valid for 'header' type: the header name (e.g., 'x-api-key')."""
+__all__ = ["GatewayConfigView"]
class GatewayConfigView(BaseModel):
diff --git a/src/runloop_api_client/types/mcp_config_create_params.py b/src/runloop_api_client/types/mcp_config_create_params.py
index 65789fe68..cb4164863 100644
--- a/src/runloop_api_client/types/mcp_config_create_params.py
+++ b/src/runloop_api_client/types/mcp_config_create_params.py
@@ -7,6 +7,7 @@
from .._types import SequenceNotStr
from .shared_params.custom_header import CustomHeader
+from .shared_params.auth_mechanism import AuthMechanism
__all__ = ["McpConfigCreateParams"]
@@ -30,6 +31,12 @@ class McpConfigCreateParams(TypedDict, total=False):
service name).
"""
+ auth_mechanism: Optional[AuthMechanism]
+ """
+ Defines how the primary credential is applied to requests proxied to the
+ upstream.
+ """
+
custom_headers: Optional[Iterable[CustomHeader]]
"""Additional headers applied to upstream requests after the credential.
diff --git a/src/runloop_api_client/types/mcp_config_update_params.py b/src/runloop_api_client/types/mcp_config_update_params.py
index 11aa6b495..e57849953 100644
--- a/src/runloop_api_client/types/mcp_config_update_params.py
+++ b/src/runloop_api_client/types/mcp_config_update_params.py
@@ -7,6 +7,7 @@
from .._types import SequenceNotStr
from .shared_params.custom_header import CustomHeader
+from .shared_params.auth_mechanism import AuthMechanism
__all__ = ["McpConfigUpdateParams"]
@@ -18,6 +19,12 @@ class McpConfigUpdateParams(TypedDict, total=False):
Examples: ['*'] for all tools, ['github.search_*'] for specific patterns.
"""
+ auth_mechanism: Optional[AuthMechanism]
+ """
+ Defines how the primary credential is applied to requests proxied to the
+ upstream.
+ """
+
custom_headers: Optional[Iterable[CustomHeader]]
"""New list of additional headers.
diff --git a/src/runloop_api_client/types/mcp_config_view.py b/src/runloop_api_client/types/mcp_config_view.py
index 87f67e318..4dd65503d 100644
--- a/src/runloop_api_client/types/mcp_config_view.py
+++ b/src/runloop_api_client/types/mcp_config_view.py
@@ -4,6 +4,7 @@
from .._models import BaseModel
from .shared.custom_header import CustomHeader
+from .shared.auth_mechanism import AuthMechanism
__all__ = ["McpConfigView"]
@@ -22,6 +23,9 @@ class McpConfigView(BaseModel):
['github.search_*', 'github.get_*'] or ['*'] for all tools).
"""
+ auth_mechanism: AuthMechanism
+ """How the primary credential is applied to upstream requests."""
+
create_time_ms: int
"""Creation time of the McpConfig (Unix timestamp in milliseconds)."""
diff --git a/src/runloop_api_client/types/shared/__init__.py b/src/runloop_api_client/types/shared/__init__.py
index 2ebb9e2c5..d2276afb6 100644
--- a/src/runloop_api_client/types/shared/__init__.py
+++ b/src/runloop_api_client/types/shared/__init__.py
@@ -8,6 +8,7 @@
from .broker_mount import BrokerMount as BrokerMount
from .object_mount import ObjectMount as ObjectMount
from .custom_header import CustomHeader as CustomHeader
+from .auth_mechanism import AuthMechanism as AuthMechanism
from .lifecycle_hooks import LifecycleHooks as LifecycleHooks
from .resume_triggers import ResumeTriggers as ResumeTriggers
from .launch_parameters import LaunchParameters as LaunchParameters
diff --git a/src/runloop_api_client/types/shared/auth_mechanism.py b/src/runloop_api_client/types/shared/auth_mechanism.py
new file mode 100644
index 000000000..b5f44d85f
--- /dev/null
+++ b/src/runloop_api_client/types/shared/auth_mechanism.py
@@ -0,0 +1,22 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+
+from ..._models import BaseModel
+
+__all__ = ["AuthMechanism"]
+
+
+class AuthMechanism(BaseModel):
+ """
+ Defines how the primary credential is applied to requests proxied to the upstream.
+ """
+
+ type: str
+ """The type of authentication mechanism: 'header', 'bearer', or 'basic'.
+
+ For 'basic', store the secret as plain 'user:pass'; the proxy base64-encodes it.
+ """
+
+ key: Optional[str] = None
+ """Only valid for 'header' type: the header name (e.g., 'x-api-key')."""
diff --git a/src/runloop_api_client/types/shared_params/__init__.py b/src/runloop_api_client/types/shared_params/__init__.py
index 2ebb9e2c5..d2276afb6 100644
--- a/src/runloop_api_client/types/shared_params/__init__.py
+++ b/src/runloop_api_client/types/shared_params/__init__.py
@@ -8,6 +8,7 @@
from .broker_mount import BrokerMount as BrokerMount
from .object_mount import ObjectMount as ObjectMount
from .custom_header import CustomHeader as CustomHeader
+from .auth_mechanism import AuthMechanism as AuthMechanism
from .lifecycle_hooks import LifecycleHooks as LifecycleHooks
from .resume_triggers import ResumeTriggers as ResumeTriggers
from .launch_parameters import LaunchParameters as LaunchParameters
diff --git a/src/runloop_api_client/types/shared_params/auth_mechanism.py b/src/runloop_api_client/types/shared_params/auth_mechanism.py
new file mode 100644
index 000000000..9b765223e
--- /dev/null
+++ b/src/runloop_api_client/types/shared_params/auth_mechanism.py
@@ -0,0 +1,23 @@
+# 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
+
+__all__ = ["AuthMechanism"]
+
+
+class AuthMechanism(TypedDict, total=False):
+ """
+ Defines how the primary credential is applied to requests proxied to the upstream.
+ """
+
+ type: Required[str]
+ """The type of authentication mechanism: 'header', 'bearer', or 'basic'.
+
+ For 'basic', store the secret as plain 'user:pass'; the proxy base64-encodes it.
+ """
+
+ key: Optional[str]
+ """Only valid for 'header' type: the header name (e.g., 'x-api-key')."""
diff --git a/tests/api_resources/test_mcp_configs.py b/tests/api_resources/test_mcp_configs.py
index 9ccf2943a..b67c3cf36 100644
--- a/tests/api_resources/test_mcp_configs.py
+++ b/tests/api_resources/test_mcp_configs.py
@@ -35,6 +35,10 @@ def test_method_create_with_all_params(self, client: Runloop) -> None:
allowed_tools=["string"],
endpoint="endpoint",
name="name",
+ auth_mechanism={
+ "type": "type",
+ "key": "key",
+ },
custom_headers=[
{
"name": "name",
@@ -124,6 +128,10 @@ def test_method_update_with_all_params(self, client: Runloop) -> None:
mcp_config = client.mcp_configs.update(
id="id",
allowed_tools=["string"],
+ auth_mechanism={
+ "type": "type",
+ "key": "key",
+ },
custom_headers=[
{
"name": "name",
@@ -264,6 +272,10 @@ async def test_method_create_with_all_params(self, async_client: AsyncRunloop) -
allowed_tools=["string"],
endpoint="endpoint",
name="name",
+ auth_mechanism={
+ "type": "type",
+ "key": "key",
+ },
custom_headers=[
{
"name": "name",
@@ -353,6 +365,10 @@ async def test_method_update_with_all_params(self, async_client: AsyncRunloop) -
mcp_config = await async_client.mcp_configs.update(
id="id",
allowed_tools=["string"],
+ auth_mechanism={
+ "type": "type",
+ "key": "key",
+ },
custom_headers=[
{
"name": "name",
From 194c060f4c95a85d895fc4e0d701e48b5222646c Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Thu, 20 Aug 2026 00:36:34 +0000
Subject: [PATCH 10/11] fix(gateway): reject keyless HEADER auth_mechanism at
admission and in looper (#11120)
---
.stats.yml | 4 ++--
src/runloop_api_client/types/shared/auth_mechanism.py | 5 ++++-
src/runloop_api_client/types/shared_params/auth_mechanism.py | 5 ++++-
3 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index 037095a6e..75c1b7196 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 121
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai/runloop-2b4c651a27238a14ac7bd03ba582183d14f9622bdb5ea292c2cea97acc4c727e.yml
-openapi_spec_hash: 8a933236116d07c8a060b6eadd205feb
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai/runloop-f8c19bf9329a554bedfbb8678d6d3cb8881a285d27b3d8ba078e2a573b597a17.yml
+openapi_spec_hash: 0c3d490050a9bbaa5cb7378bf55e8f37
config_hash: 7afcebea2dd638ed0cf0d5367f820f73
diff --git a/src/runloop_api_client/types/shared/auth_mechanism.py b/src/runloop_api_client/types/shared/auth_mechanism.py
index b5f44d85f..f046b0b25 100644
--- a/src/runloop_api_client/types/shared/auth_mechanism.py
+++ b/src/runloop_api_client/types/shared/auth_mechanism.py
@@ -19,4 +19,7 @@ class AuthMechanism(BaseModel):
"""
key: Optional[str] = None
- """Only valid for 'header' type: the header name (e.g., 'x-api-key')."""
+ """The header name (e.g., 'x-api-key').
+
+ Required for 'header' type; invalid for other types.
+ """
diff --git a/src/runloop_api_client/types/shared_params/auth_mechanism.py b/src/runloop_api_client/types/shared_params/auth_mechanism.py
index 9b765223e..d2579df2b 100644
--- a/src/runloop_api_client/types/shared_params/auth_mechanism.py
+++ b/src/runloop_api_client/types/shared_params/auth_mechanism.py
@@ -20,4 +20,7 @@ class AuthMechanism(TypedDict, total=False):
"""
key: Optional[str]
- """Only valid for 'header' type: the header name (e.g., 'x-api-key')."""
+ """The header name (e.g., 'x-api-key').
+
+ Required for 'header' type; invalid for other types.
+ """
From b08e71cb3c3914b4cc98a3eeb8bb1c866ee8d246 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Thu, 20 Aug 2026 00:37:02 +0000
Subject: [PATCH 11/11] release: 1.29.0
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 20 ++++++++++++++++++++
pyproject.toml | 2 +-
src/runloop_api_client/_version.py | 2 +-
4 files changed, 23 insertions(+), 3 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 31d823888..b8af36c3a 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "1.28.0"
+ ".": "1.29.0"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 492ac3a8a..aa709b138 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,25 @@
# Changelog
+## 1.29.0 (2026-08-20)
+
+Full Changelog: [v1.28.0...v1.29.0](https://github.com/runloopai/api-client-python/compare/v1.28.0...v1.29.0)
+
+### Features
+
+* **axon:** add registry delete endpoint ([#10769](https://github.com/runloopai/api-client-python/issues/10769)) ([c63f011](https://github.com/runloopai/api-client-python/commit/c63f01108d98785b358357e4e65e5e517dd0805e))
+* **broker:** plumb Pi protocol selection ([#10256](https://github.com/runloopai/api-client-python/issues/10256)) ([974d52b](https://github.com/runloopai/api-client-python/commit/974d52b8743ceda8a7127e3a3432ce51e71b1bfd))
+* **config:** custom_headers on McpConfig/GatewayConfig — persistence, REST, validation, provisioner, OpenAPI ([#11096](https://github.com/runloopai/api-client-python/issues/11096)) ([9482e47](https://github.com/runloopai/api-client-python/commit/9482e475d0ad6e2b1989ee25f66a759fe839065f))
+* **converter:** add Helm chart, container image, and enable in dev ([#10405](https://github.com/runloopai/api-client-python/issues/10405)) ([0d5ca34](https://github.com/runloopai/api-client-python/commit/0d5ca3438ac4cbcafdfaf7584042bd78a956e3ae))
+* **devbox:** recover resume provisioning failures ([#10597](https://github.com/runloopai/api-client-python/issues/10597)) ([518301e](https://github.com/runloopai/api-client-python/commit/518301e444b95bc5fa73cc0f13be67cf3f750024))
+* **gateway:** first-class BASIC auth mechanism ([#11063](https://github.com/runloopai/api-client-python/issues/11063)) ([1f50f93](https://github.com/runloopai/api-client-python/commit/1f50f93c26c6696d2a9f81b1b677eb9b7282601a))
+* **mcp:** configurable auth_mechanism on McpConfig, shared shape with gateway ([#11113](https://github.com/runloopai/api-client-python/issues/11113)) ([4dd9edd](https://github.com/runloopai/api-client-python/commit/4dd9edd5abbdfab0466826db8d18cbd2efae7b31))
+
+
+### Bug Fixes
+
+* **devbox:** make Devbox update API patch-like ([#10582](https://github.com/runloopai/api-client-python/issues/10582)) ([0a74521](https://github.com/runloopai/api-client-python/commit/0a7452145ee6d1689d8635e65f88b8f778a84820))
+* **gateway:** reject keyless HEADER auth_mechanism at admission and in looper ([#11120](https://github.com/runloopai/api-client-python/issues/11120)) ([194c060](https://github.com/runloopai/api-client-python/commit/194c060f4c95a85d895fc4e0d701e48b5222646c))
+
## 1.28.0 (2026-07-29)
Full Changelog: [v1.27.0...v1.28.0](https://github.com/runloopai/api-client-python/compare/v1.27.0...v1.28.0)
diff --git a/pyproject.toml b/pyproject.toml
index 364c6dedd..7974ff247 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "runloop_api_client"
-version = "1.28.0"
+version = "1.29.0"
description = "The official Python library for the runloop API"
dynamic = ["readme"]
license = "MIT"
diff --git a/src/runloop_api_client/_version.py b/src/runloop_api_client/_version.py
index 1a2d135f9..cb57bc803 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.28.0" # x-release-please-version
+__version__ = "1.29.0" # x-release-please-version