Skip to content

Commit fed8d94

Browse files
feat: Implement session-based safe mode for Cloud ops (#844)
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
1 parent eda647b commit fed8d94

File tree

2 files changed

+43
-1
lines changed

2 files changed

+43
-1
lines changed

airbyte/mcp/_tool_utils.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
AIRBYTE_CLOUD_MCP_SAFE_MODE = os.environ.get("AIRBYTE_CLOUD_MCP_SAFE_MODE", "").strip() == "1"
2828

2929
_REGISTERED_TOOLS: list[tuple[Callable[..., Any], dict[str, Any]]] = []
30+
_GUIDS_CREATED_IN_SESSION: set[str] = set()
3031

3132

3233
class SafeModeError(Exception):
@@ -35,6 +36,32 @@ class SafeModeError(Exception):
3536
pass
3637

3738

39+
def register_guid_created_in_session(guid: str) -> None:
40+
"""Register a GUID as created in this session.
41+
42+
Args:
43+
guid: The GUID to register
44+
"""
45+
_GUIDS_CREATED_IN_SESSION.add(guid)
46+
47+
48+
def check_guid_created_in_session(guid: str) -> None:
49+
"""Check if a GUID was created in this session.
50+
51+
Raises SafeModeError if the GUID was not created in this session and
52+
AIRBYTE_CLOUD_MCP_SAFE_MODE is set to 1.
53+
54+
Args:
55+
guid: The GUID to check
56+
"""
57+
if AIRBYTE_CLOUD_MCP_SAFE_MODE and guid not in _GUIDS_CREATED_IN_SESSION:
58+
raise SafeModeError(
59+
f"Cannot perform destructive operation on '{guid}': "
60+
f"Object was not created in this session. "
61+
f"AIRBYTE_CLOUD_MCP_SAFE_MODE is set to '1'."
62+
)
63+
64+
3865
def should_register_tool(annotations: dict[str, Any]) -> bool:
3966
"""Check if a tool should be registered based on safe mode settings.
4067

airbyte/mcp/cloud_ops.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,12 @@
1818
from airbyte.cloud.connectors import CloudDestination, CloudSource, CustomCloudSourceDefinition
1919
from airbyte.cloud.workspaces import CloudWorkspace
2020
from airbyte.destinations.util import get_noop_destination
21-
from airbyte.mcp._tool_utils import mcp_tool, register_tools
21+
from airbyte.mcp._tool_utils import (
22+
check_guid_created_in_session,
23+
mcp_tool,
24+
register_guid_created_in_session,
25+
register_tools,
26+
)
2227
from airbyte.mcp._util import resolve_config, resolve_list_of_strings
2328

2429

@@ -96,6 +101,7 @@ def deploy_source_to_cloud(
96101
except Exception as ex:
97102
return f"Failed to deploy source '{source_name}': {ex}"
98103
else:
104+
register_guid_created_in_session(deployed_source.connector_id)
99105
return (
100106
f"Successfully deployed source '{source_name}' with ID '{deployed_source.connector_id}'"
101107
f" and URL: {deployed_source.connector_url}"
@@ -166,6 +172,7 @@ def deploy_destination_to_cloud(
166172
except Exception as ex:
167173
return f"Failed to deploy destination '{destination_name}': {ex}"
168174
else:
175+
register_guid_created_in_session(deployed_destination.connector_id)
169176
return (
170177
f"Successfully deployed destination '{destination_name}' "
171178
f"with ID: {deployed_destination.connector_id}"
@@ -227,6 +234,7 @@ def create_connection_on_cloud(
227234
except Exception as ex:
228235
return f"Failed to create connection '{connection_name}': {ex}"
229236
else:
237+
register_guid_created_in_session(deployed_connection.connection_id)
230238
return (
231239
f"Successfully created connection '{connection_name}' "
232240
f"with ID '{deployed_connection.connection_id}' and "
@@ -342,6 +350,7 @@ def deploy_noop_destination_to_cloud(
342350
except Exception as ex:
343351
return f"Failed to deploy No-op Destination: {ex}"
344352
else:
353+
register_guid_created_in_session(deployed_destination.connector_id)
345354
return (
346355
f"Successfully deployed No-op Destination "
347356
f"with ID '{deployed_destination.connector_id}' and "
@@ -617,6 +626,7 @@ def publish_custom_source_definition(
617626
except Exception as ex:
618627
return f"Failed to publish custom source definition '{name}': {ex}"
619628
else:
629+
register_guid_created_in_session(custom_source.definition_id)
620630
return (
621631
"Successfully published custom YAML source definition:\n"
622632
+ _get_custom_source_definition_description(
@@ -684,6 +694,7 @@ def update_custom_source_definition(
684694
Note: Only YAML (declarative) connectors are currently supported.
685695
Docker-based custom sources are not yet available.
686696
"""
697+
check_guid_created_in_session(definition_id)
687698
try:
688699
processed_manifest = manifest_yaml
689700
if isinstance(manifest_yaml, str) and "\n" not in manifest_yaml:
@@ -733,6 +744,7 @@ def permanently_delete_custom_source_definition(
733744
Note: Only YAML (declarative) connectors are currently supported.
734745
Docker-based custom sources are not yet available.
735746
"""
747+
check_guid_created_in_session(definition_id)
736748
workspace: CloudWorkspace = _get_cloud_workspace()
737749
definition = workspace.get_custom_source_definition(
738750
definition_id=definition_id,
@@ -806,6 +818,7 @@ def update_cloud_source_config(
806818
and `AIRBYTE_API_ROOT` environment variables will be used to authenticate with the
807819
Airbyte Cloud API.
808820
"""
821+
check_guid_created_in_session(source_id)
809822
workspace: CloudWorkspace = _get_cloud_workspace()
810823
source = workspace.get_source(source_id=source_id)
811824

@@ -881,6 +894,7 @@ def update_cloud_destination_config(
881894
and `AIRBYTE_API_ROOT` environment variables will be used to authenticate with the
882895
Airbyte Cloud API.
883896
"""
897+
check_guid_created_in_session(destination_id)
884898
workspace: CloudWorkspace = _get_cloud_workspace()
885899
destination = workspace.get_destination(destination_id=destination_id)
886900

@@ -983,6 +997,7 @@ def set_cloud_connection_selected_streams(
983997
and `AIRBYTE_API_ROOT` environment variables will be used to authenticate with the
984998
Airbyte Cloud API.
985999
"""
1000+
check_guid_created_in_session(connection_id)
9861001
workspace: CloudWorkspace = _get_cloud_workspace()
9871002
connection = workspace.get_connection(connection_id=connection_id)
9881003

0 commit comments

Comments
 (0)