Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 22 additions & 7 deletions airbyte/cloud/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -917,12 +917,7 @@ def get_default_context_for_user(self) -> CloudDefaultContextInfo:
"discover workspaces."
)
if user is not None and not isinstance(user.get("defaultWorkspaceId"), str):
discovery_hints.append(
"No default workspace is set. Use "
"set_default_cloud_workspace(user_email=<your email>, "
"workspace_id=<id>) to durably set one; it applies to both MCP "
"sessions and the Airbyte Cloud web app."
)
discovery_hints.append(self._missing_default_workspace_hint())
return CloudDefaultContextInfo(
user_id=user_id,
user_name=user_name,
Expand All @@ -931,7 +926,9 @@ def get_default_context_for_user(self) -> CloudDefaultContextInfo:
default_workspace_name=(
default_workspace_info.name if default_workspace_info else None
),
default_workspace_verified=default_workspace_info is not None,
default_workspace_verified=(
default_workspace_info is not None if default_workspace_id is not None else None
),
default_organization_id=(
default_workspace_organization.organization_id
if default_workspace_organization is not None
Expand All @@ -952,6 +949,24 @@ def get_default_context_for_user(self) -> CloudDefaultContextInfo:
discovery_hints=discovery_hints,
)

def _missing_default_workspace_hint(self) -> str:
"""Hint text when the user's profile has no stored default workspace."""
if self.default_workspace_id is not None:
return (
"Your Airbyte user profile has no default workspace; this "
"session uses the configured workspace "
f"{self.default_workspace_id}. Use "
"set_default_cloud_workspace(user_email=<your email>, "
"workspace_id=<id>) to set a durable default; it applies to "
"both MCP sessions and the Airbyte Cloud web app."
)
return (
"No default workspace is set. Use "
"set_default_cloud_workspace(user_email=<your email>, "
"workspace_id=<id>) to durably set one; it applies to both MCP "
"sessions and the Airbyte Cloud web app."
)

def set_default_workspace_for_user(
self,
*,
Expand Down
8 changes: 6 additions & 2 deletions airbyte/cloud/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -283,8 +283,12 @@ class CloudDefaultContextInfo(BaseModel):
default_workspace_name: str | None
"""The resolved default workspace name, if available."""

default_workspace_verified: bool
"""Whether the resolved default workspace was verified as accessible."""
default_workspace_verified: bool | None
"""Whether the resolved default workspace was verified as accessible.

`None` when no default workspace ID was resolved, since there is nothing
to verify.
"""

unvalidated_workspace_count: int = 0
"""Number of direct workspace grants not validated due to the validation cap."""
Expand Down
8 changes: 6 additions & 2 deletions airbyte/mcp/cloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -405,8 +405,12 @@ class CloudDefaultContextResult(BaseModel):
default_workspace_name: str | None
"""The resolved default workspace name, if available."""

default_workspace_verified: bool
"""Whether the resolved default workspace was verified as accessible."""
default_workspace_verified: bool | None
"""Whether the resolved default workspace was verified as accessible.

`None` when no default workspace ID was resolved, since there is nothing
to verify.
"""

unvalidated_workspace_count: int = 0
"""Number of direct workspace grants not validated due to the validation cap."""
Expand Down
51 changes: 51 additions & 0 deletions tests/unit_tests/test_cloud_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1158,6 +1158,57 @@ def test_get_default_context_for_user_hints_setter_when_stored_default_missing()
]


def test_get_default_context_for_user_verified_is_none_without_default() -> None:
"""With no resolvable default, `verified` is None rather than False."""
patches = _api_patches(user={"userId": "user-id"})
with patches[0], patches[1], patches[2], patches[3], patches[4]:
context = CloudClient(bearer_token="token").get_default_context_for_user()

assert context.default_workspace_id is None
assert context.default_workspace_verified is None
assert context.discovery_hints == [
"No default workspace is set. Use "
"set_default_cloud_workspace(user_email=<your email>, "
"workspace_id=<id>) to durably set one; it applies to both MCP "
"sessions and the Airbyte Cloud web app."
]


def test_get_default_context_for_user_configured_default_hint() -> None:
"""A configured default without a profile default gets an accurate hint."""
patches = _api_patches(user={"userId": "user-id"})
with (
patches[0],
patches[1],
patches[2],
patches[3],
patches[4],
patch(
"airbyte._util.api_util.get_workspace",
return_value=models.WorkspaceResponse(
data_residency="auto",
name="Configured",
notifications=models.NotificationsConfig(),
workspace_id="configured-workspace",
),
),
):
context = CloudClient(
bearer_token="token",
workspace_id="configured-workspace",
).get_default_context_for_user()

assert context.default_workspace_id == "configured-workspace"
assert context.default_workspace_verified is True
assert context.discovery_hints == [
"Your Airbyte user profile has no default workspace; this session "
"uses the configured workspace configured-workspace. Use "
"set_default_cloud_workspace(user_email=<your email>, "
"workspace_id=<id>) to set a durable default; it applies to "
"both MCP sessions and the Airbyte Cloud web app."
]


def test_get_default_context_for_user_omits_setter_hint_when_default_stored() -> None:
patches = _api_patches(
user={"userId": "user-id", "defaultWorkspaceId": "workspace-1"},
Expand Down
Loading