airbyte_ops_mcp.mcp.organization_agentic_flag
MCP tools for organization is_agentic flag management.
Provides tools to read and update the Airbyte Cloud organization is_agentic
flag.
MCP reference
MCP primitives registered by the organization_agentic_flag module of the airbyte-internal-ops server: 2 tool(s), 0 prompt(s), 0 resource(s).
Tools (2)
get_organization_agentic_flag
Hints: idempotent · open-world
Get the current is_agentic flag for one or more organizations.
Parameters:
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
organization_ids |
string | array<string> |
yes | — | One organization UUID, or a list of organization UUIDs. |
config_api_root |
string | null |
no | null |
Optional Config API root URL override. When omitted, the tool reads from the Prod DB replica. |
Show input JSON schema
{
"additionalProperties": false,
"properties": {
"organization_ids": {
"anyOf": [
{
"type": "string"
},
{
"items": {
"type": "string"
},
"type": "array"
}
],
"description": "One organization UUID, or a list of organization UUIDs."
},
"config_api_root": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Optional Config API root URL override. When omitted, the tool reads from the Prod DB replica."
}
},
"required": [
"organization_ids"
],
"type": "object"
}
Show output JSON schema
{
"description": "Current managed agentic organization status for one or more organizations.",
"properties": {
"organizations": {
"description": "Organizations that were found.",
"items": {
"description": "Current managed agentic organization status for an organization.",
"properties": {
"organization_id": {
"description": "The organization UUID",
"type": "string"
},
"organization_name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "The display name of the organization"
},
"email": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Organization contact email (may be absent)"
},
"tombstone": {
"description": "Whether the organization is tombstoned",
"type": "boolean"
},
"is_agentic": {
"description": "Whether the organization is managed by the Airbyte Agents product. `False` means a standard Cloud org; `True` means an org managed via the app.agents.ai interfaces.",
"type": "boolean"
},
"customer_tier": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Customer tier of the organization (TIER_0, TIER_1, TIER_2)"
},
"tier_warning": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Warning message if the organization is a sensitive customer tier"
}
},
"required": [
"organization_id",
"tombstone",
"is_agentic"
],
"type": "object"
},
"type": "array"
},
"missing_organization_ids": {
"description": "Requested organization IDs that were not found.",
"items": {
"type": "string"
},
"type": "array"
}
},
"required": [
"organizations"
],
"type": "object"
}
update_organization_agentic_flag
Hints: destructive · idempotent · open-world
Update the is_agentic flag for one or more organizations.
All updates require human-in-the-loop approval via escalate_to_human.
The tool validates each organization identity before writing the flag.
Parameters:
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
organization_ids |
string | array<string> |
yes | — | One organization UUID, or a list of organization UUIDs. |
is_agentic |
boolean |
yes | — | The desired is_agentic value. |
approval_comment_url |
string |
yes | — | URL to the Slack approval record. Obtain this by calling escalate_to_human with approval_requested=True; the backend delivers the approval record URL when a human clicks Approve. |
organization_name |
string | null |
no | null |
Confirmation of the target organization. Accepts the organization name, email address, or email domain. Required when updating a single organization. Ignored for multi-org updates; use organization_names instead. |
organization_names |
object | null |
no | null |
Per-organization confirmation for multi-org updates. Keys are organization UUIDs; values may be organization names, email addresses, or email domains. |
customer_tier_filter |
enum("TIER_0", "TIER_1", "TIER_2", "ALL") |
no | "TIER_2" |
Required tier filter: 'TIER_0', 'TIER_1', 'TIER_2', or 'ALL'. The operation is rejected if the actual customer tier does not match. Use 'ALL' to proceed regardless of tier after human approval. |
Show input JSON schema
{
"additionalProperties": false,
"properties": {
"organization_ids": {
"anyOf": [
{
"type": "string"
},
{
"items": {
"type": "string"
},
"type": "array"
}
],
"description": "One organization UUID, or a list of organization UUIDs."
},
"is_agentic": {
"description": "The desired `is_agentic` value.",
"type": "boolean"
},
"approval_comment_url": {
"description": "URL to the Slack approval record. Obtain this by calling `escalate_to_human` with `approval_requested=True`; the backend delivers the approval record URL when a human clicks Approve.",
"type": "string"
},
"organization_name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Confirmation of the target organization. Accepts the organization name, email address, or email domain. Required when updating a single organization. Ignored for multi-org updates; use `organization_names` instead."
},
"organization_names": {
"anyOf": [
{
"additionalProperties": {
"type": "string"
},
"type": "object"
},
{
"type": "null"
}
],
"default": null,
"description": "Per-organization confirmation for multi-org updates. Keys are organization UUIDs; values may be organization names, email addresses, or email domains."
},
"customer_tier_filter": {
"default": "TIER_2",
"description": "Required tier filter: 'TIER_0', 'TIER_1', 'TIER_2', or 'ALL'. The operation is rejected if the actual customer tier does not match. Use 'ALL' to proceed regardless of tier after human approval.",
"enum": [
"TIER_0",
"TIER_1",
"TIER_2",
"ALL"
],
"type": "string"
}
},
"required": [
"organization_ids",
"is_agentic",
"approval_comment_url"
],
"type": "object"
}
Show output JSON schema
{
"description": "Result of updating one or more managed agentic organization statuses.",
"properties": {
"success": {
"description": "Whether every requested update succeeded",
"type": "boolean"
},
"message": {
"description": "Human-readable message describing the result",
"type": "string"
},
"results": {
"description": "Per-organization update results.",
"items": {
"description": "Result of a managed agentic organization status update.",
"properties": {
"success": {
"description": "Whether the operation succeeded",
"type": "boolean"
},
"message": {
"description": "Human-readable message describing the result",
"type": "string"
},
"organization_id": {
"description": "The organization UUID",
"type": "string"
},
"organization_name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "The display name of the organization"
},
"email": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Organization contact email (may be absent)"
},
"previous_is_agentic": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
],
"default": null,
"description": "The managed agentic organization status before the update."
},
"new_is_agentic": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
],
"default": null,
"description": "The managed agentic organization status after the update."
},
"customer_tier": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Customer tier of the organization (TIER_0, TIER_1, TIER_2)"
},
"tier_warning": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Warning message if the organization is a sensitive customer tier"
}
},
"required": [
"success",
"message",
"organization_id"
],
"type": "object"
},
"type": "array"
}
},
"required": [
"success",
"message",
"results"
],
"type": "object"
}
1# Copyright (c) 2025 Airbyte, Inc., all rights reserved. 2"""MCP tools for organization `is_agentic` flag management. 3 4Provides tools to read and update the Airbyte Cloud organization `is_agentic` 5flag. 6 7## MCP reference 8 9.. include:: ../../../docs/mcp-generated/organization_agentic_flag.md 10 :start-line: 2 11""" 12 13# NOTE: We intentionally do NOT use `from __future__ import annotations` here. 14# FastMCP has issues resolving forward references when PEP 563 deferred annotations 15# are used. See: https://github.com/jlowin/fastmcp/issues/905 16 17__all__: list[str] = [] 18 19from typing import Annotated 20 21from airbyte import constants 22from fastmcp import Context, FastMCP 23from fastmcp_extensions import get_mcp_config, mcp_tool, register_mcp_tools 24from pydantic import Field 25 26from airbyte_ops_mcp.approval_resolution import ( 27 ApprovalResolutionError, 28 resolve_admin_email_from_approval, 29) 30from airbyte_ops_mcp.cloud_admin.auth import ( 31 CloudAuthError, 32 require_internal_admin_flag_only, 33) 34from airbyte_ops_mcp.cloud_admin.models import ( 35 OrganizationAgenticFlagBatchInfo, 36 OrganizationAgenticFlagBatchUpdateResult, 37 OrganizationAgenticFlagInfo, 38 OrganizationAgenticFlagUpdateResult, 39 OrganizationInfo, 40) 41from airbyte_ops_mcp.cloud_admin.organization_agentic_flag import ( 42 OrganizationAgenticFlagAPIError, 43) 44from airbyte_ops_mcp.cloud_admin.organization_agentic_flag import ( 45 get_organization_info as _get_organization_info, 46) 47from airbyte_ops_mcp.cloud_admin.organization_agentic_flag import ( 48 set_organization_agentic_status as _set_organization_agentic_status, 49) 50from airbyte_ops_mcp.cloud_admin.version_overrides import validate_tier_filter 51from airbyte_ops_mcp.constants import ServerConfigKey 52from airbyte_ops_mcp.mcp.organization_payment_config import ( 53 _build_tier_warning, 54 _validate_organization_name, 55) 56from airbyte_ops_mcp.prod_db_access.queries import ( 57 query_organization_agentic_flags, 58) 59from airbyte_ops_mcp.tier_cache import TierFilter, get_org_tier 60 61 62def _normalize_organization_ids(organization_ids: str | list[str]) -> list[str]: 63 """Normalize one organization ID or a list of organization IDs.""" 64 ids = [organization_ids] if isinstance(organization_ids, str) else organization_ids 65 66 normalized = [org_id.strip() for org_id in ids if org_id and org_id.strip()] 67 return list(dict.fromkeys(normalized)) 68 69 70def _build_update_failure_results( 71 organization_ids: list[str], 72 message: str, 73) -> list[OrganizationAgenticFlagUpdateResult]: 74 """Build per-org failure results for request-level validation failures.""" 75 return [ 76 OrganizationAgenticFlagUpdateResult( 77 success=False, 78 message=message, 79 organization_id=organization_id, 80 ) 81 for organization_id in organization_ids 82 ] 83 84 85def _resolve_cloud_auth(ctx: Context) -> tuple[str | None, str | None, str | None]: 86 """Resolve auth credentials, returning `(bearer_token, client_id, client_secret)`.""" 87 bearer_token = get_mcp_config(ctx, ServerConfigKey.BEARER_TOKEN) 88 if bearer_token: 89 return bearer_token, None, None 90 91 client_id = get_mcp_config(ctx, ServerConfigKey.CLIENT_ID) 92 client_secret = get_mcp_config(ctx, ServerConfigKey.CLIENT_SECRET) 93 return None, client_id, client_secret 94 95 96def _org_info_to_flag_info( 97 org_info: OrganizationInfo, 98 customer_tier: str | None = None, 99 tier_warning: str | None = None, 100) -> OrganizationAgenticFlagInfo: 101 """Convert Config API organization info into agentic flag info.""" 102 return OrganizationAgenticFlagInfo( 103 organization_id=org_info.organization_id, 104 organization_name=org_info.organization_name, 105 email=org_info.email, 106 tombstone=False, 107 is_agentic=org_info.is_agentic or False, 108 customer_tier=customer_tier, 109 tier_warning=tier_warning, 110 ) 111 112 113def _row_to_flag_info( 114 row: dict[str, object], 115 customer_tier: str | None = None, 116 tier_warning: str | None = None, 117) -> OrganizationAgenticFlagInfo: 118 """Convert a DB row into agentic flag info.""" 119 return OrganizationAgenticFlagInfo( 120 organization_id=str(row["organization_id"]), 121 organization_name=str(row["organization_name"]) 122 if row["organization_name"] is not None 123 else None, 124 email=str(row["email"]) if row["email"] is not None else None, 125 tombstone=bool(row["tombstone"]), 126 is_agentic=bool(row["is_agentic"]), 127 customer_tier=customer_tier, 128 tier_warning=tier_warning, 129 ) 130 131 132@mcp_tool( 133 destructive=False, 134 idempotent=True, 135 open_world=True, 136) 137def get_organization_agentic_flag( 138 organization_ids: Annotated[ 139 str | list[str], 140 Field( 141 description="One organization UUID, or a list of organization UUIDs.", 142 ), 143 ], 144 config_api_root: Annotated[ 145 str | None, 146 Field( 147 description="Optional Config API root URL override. When omitted, " 148 "the tool reads from the Prod DB replica.", 149 default=None, 150 ), 151 ] = None, 152 *, 153 ctx: Context, 154) -> OrganizationAgenticFlagBatchInfo: 155 """Get the current `is_agentic` flag for one or more organizations.""" 156 normalized_ids = _normalize_organization_ids(organization_ids) 157 if not normalized_ids: 158 return OrganizationAgenticFlagBatchInfo( 159 organizations=[], 160 missing_organization_ids=[], 161 ) 162 163 if config_api_root is None: 164 rows = query_organization_agentic_flags(normalized_ids) 165 rows_by_org_id = {str(row["organization_id"]): row for row in rows} 166 organizations: list[OrganizationAgenticFlagInfo] = [] 167 for organization_id in normalized_ids: 168 row = rows_by_org_id.get(organization_id) 169 if row is None: 170 continue 171 tier_result = get_org_tier(organization_id) 172 organizations.append( 173 _row_to_flag_info( 174 row, 175 customer_tier=tier_result.customer_tier, 176 tier_warning=_build_tier_warning(tier_result.customer_tier), 177 ) 178 ) 179 return OrganizationAgenticFlagBatchInfo( 180 organizations=organizations, 181 missing_organization_ids=[ 182 organization_id 183 for organization_id in normalized_ids 184 if organization_id not in rows_by_org_id 185 ], 186 ) 187 188 resolved_api_root = config_api_root or constants.CLOUD_CONFIG_API_ROOT 189 bearer_token, client_id, client_secret = _resolve_cloud_auth(ctx) 190 191 organizations: list[OrganizationAgenticFlagInfo] = [] 192 missing_organization_ids: list[str] = [] 193 for organization_id in normalized_ids: 194 try: 195 org_info = _get_organization_info( 196 organization_id=organization_id, 197 config_api_root=resolved_api_root, 198 client_id=client_id, 199 client_secret=client_secret, 200 bearer_token=bearer_token, 201 ) 202 except OrganizationAgenticFlagAPIError: 203 missing_organization_ids.append(organization_id) 204 continue 205 if org_info is None: 206 missing_organization_ids.append(organization_id) 207 continue 208 209 tier_result = get_org_tier(organization_id) 210 organizations.append( 211 _org_info_to_flag_info( 212 org_info, 213 customer_tier=tier_result.customer_tier, 214 tier_warning=_build_tier_warning(tier_result.customer_tier), 215 ) 216 ) 217 218 return OrganizationAgenticFlagBatchInfo( 219 organizations=organizations, 220 missing_organization_ids=missing_organization_ids, 221 ) 222 223 224@mcp_tool( 225 destructive=True, 226 idempotent=True, 227 open_world=True, 228) 229def update_organization_agentic_flag( 230 organization_ids: Annotated[ 231 str | list[str], 232 Field( 233 description="One organization UUID, or a list of organization UUIDs.", 234 ), 235 ], 236 is_agentic: Annotated[ 237 bool, 238 Field(description="The desired `is_agentic` value."), 239 ], 240 approval_comment_url: Annotated[ 241 str, 242 Field( 243 description="URL to the Slack approval record. Obtain this by calling " 244 "`escalate_to_human` with `approval_requested=True`; the backend " 245 "delivers the approval record URL when a human clicks Approve.", 246 ), 247 ], 248 organization_name: Annotated[ 249 str | None, 250 Field( 251 description="Confirmation of the target organization. Accepts the " 252 "organization name, email address, or email domain. Required when " 253 "updating a single organization. Ignored for multi-org updates; use " 254 "`organization_names` instead.", 255 default=None, 256 ), 257 ] = None, 258 organization_names: Annotated[ 259 dict[str, str] | None, 260 Field( 261 description="Per-organization confirmation for multi-org updates. " 262 "Keys are organization UUIDs; values may be organization names, " 263 "email addresses, or email domains.", 264 default=None, 265 ), 266 ] = None, 267 customer_tier_filter: Annotated[ 268 TierFilter, 269 Field( 270 description=( 271 "Required tier filter: 'TIER_0', 'TIER_1', 'TIER_2', or 'ALL'. " 272 "The operation is rejected if the actual customer tier does not match. " 273 "Use 'ALL' to proceed regardless of tier after human approval." 274 ), 275 ), 276 ] = "TIER_2", 277 *, 278 ctx: Context, 279) -> OrganizationAgenticFlagBatchUpdateResult: 280 """Update the `is_agentic` flag for one or more organizations. 281 282 All updates require human-in-the-loop approval via `escalate_to_human`. 283 The tool validates each organization identity before writing the flag. 284 """ 285 normalized_ids = _normalize_organization_ids(organization_ids) 286 if not normalized_ids: 287 return OrganizationAgenticFlagBatchUpdateResult( 288 success=False, 289 message="No organization IDs provided.", 290 results=[], 291 ) 292 293 try: 294 require_internal_admin_flag_only() 295 except CloudAuthError as e: 296 return OrganizationAgenticFlagBatchUpdateResult( 297 success=False, 298 message=f"Admin authentication failed: {e}", 299 results=_build_update_failure_results( 300 normalized_ids, f"Admin authentication failed: {e}" 301 ), 302 ) 303 304 try: 305 resolve_admin_email_from_approval( 306 approval_comment_url=approval_comment_url, 307 ) 308 except ApprovalResolutionError as e: 309 return OrganizationAgenticFlagBatchUpdateResult( 310 success=False, 311 message=str(e), 312 results=_build_update_failure_results(normalized_ids, str(e)), 313 ) 314 315 bearer_token, client_id, client_secret = _resolve_cloud_auth(ctx) 316 317 results: list[OrganizationAgenticFlagUpdateResult] = [] 318 for organization_id in normalized_ids: 319 rows = query_organization_agentic_flags([organization_id]) 320 if not rows: 321 results.append( 322 OrganizationAgenticFlagUpdateResult( 323 success=False, 324 message=f"Organization {organization_id} not found.", 325 organization_id=organization_id, 326 ) 327 ) 328 continue 329 330 current_info = _row_to_flag_info(rows[0]) 331 if current_info.tombstone: 332 results.append( 333 OrganizationAgenticFlagUpdateResult( 334 success=False, 335 message=f"Organization {organization_id} is tombstoned.", 336 organization_id=organization_id, 337 organization_name=current_info.organization_name, 338 email=current_info.email, 339 previous_is_agentic=current_info.is_agentic, 340 ) 341 ) 342 continue 343 344 org_info = OrganizationInfo.model_validate( 345 { 346 "organizationId": current_info.organization_id, 347 "organizationName": current_info.organization_name or "", 348 "email": current_info.email, 349 "isAgentic": current_info.is_agentic, 350 } 351 ) 352 expected_name = ( 353 organization_names.get(organization_id) 354 if organization_names is not None 355 else organization_name 356 ) 357 name_ok, name_error = _validate_organization_name( 358 organization_id, expected_name, org_info 359 ) 360 if not name_ok: 361 results.append( 362 OrganizationAgenticFlagUpdateResult( 363 success=False, 364 message=name_error or "Organization name validation failed.", 365 organization_id=organization_id, 366 organization_name=current_info.organization_name, 367 email=current_info.email, 368 previous_is_agentic=current_info.is_agentic, 369 ) 370 ) 371 continue 372 373 tier_result = get_org_tier(organization_id) 374 customer_tier = tier_result.customer_tier 375 tier_warning = _build_tier_warning(customer_tier) 376 tier_ok, tier_error = validate_tier_filter(customer_tier, customer_tier_filter) 377 if not tier_ok: 378 results.append( 379 OrganizationAgenticFlagUpdateResult( 380 success=False, 381 message=tier_error or "Customer tier validation failed.", 382 organization_id=organization_id, 383 organization_name=current_info.organization_name, 384 email=current_info.email, 385 previous_is_agentic=current_info.is_agentic, 386 customer_tier=customer_tier, 387 tier_warning=tier_warning, 388 ) 389 ) 390 continue 391 previous_is_agentic = current_info.is_agentic 392 393 if previous_is_agentic == is_agentic: 394 results.append( 395 OrganizationAgenticFlagUpdateResult( 396 success=True, 397 message=( 398 f"Organization {organization_id} already has managed agentic " 399 f"org status {is_agentic}." 400 ), 401 organization_id=organization_id, 402 organization_name=current_info.organization_name, 403 email=current_info.email, 404 previous_is_agentic=previous_is_agentic, 405 new_is_agentic=is_agentic, 406 customer_tier=customer_tier, 407 tier_warning=tier_warning, 408 ) 409 ) 410 continue 411 412 try: 413 updated = _set_organization_agentic_status( 414 organization_id=organization_id, 415 is_agentic=is_agentic, 416 config_api_root=constants.CLOUD_CONFIG_API_ROOT, 417 client_id=client_id, 418 client_secret=client_secret, 419 bearer_token=bearer_token, 420 ) 421 except Exception as e: 422 results.append( 423 OrganizationAgenticFlagUpdateResult( 424 success=False, 425 message=str(e), 426 organization_id=organization_id, 427 organization_name=current_info.organization_name, 428 email=current_info.email, 429 previous_is_agentic=previous_is_agentic, 430 customer_tier=customer_tier, 431 tier_warning=tier_warning, 432 ) 433 ) 434 continue 435 436 updated_info = _org_info_to_flag_info( 437 updated, 438 customer_tier=customer_tier, 439 tier_warning=tier_warning, 440 ) 441 results.append( 442 OrganizationAgenticFlagUpdateResult( 443 success=True, 444 message=( 445 f"Organization {organization_id} updated managed agentic org " 446 f"status to {updated_info.is_agentic}." 447 ), 448 organization_id=organization_id, 449 organization_name=updated_info.organization_name, 450 email=updated_info.email, 451 previous_is_agentic=previous_is_agentic, 452 new_is_agentic=updated_info.is_agentic, 453 customer_tier=customer_tier, 454 tier_warning=tier_warning, 455 ) 456 ) 457 458 success = all(result.success for result in results) 459 return OrganizationAgenticFlagBatchUpdateResult( 460 success=success, 461 message=( 462 f"Updated {sum(result.success for result in results)} of " 463 f"{len(results)} organization agentic flag request(s)." 464 ), 465 results=results, 466 ) 467 468 469def register_organization_agentic_flag_tools(app: FastMCP) -> None: 470 """Register organization agentic flag MCP tools.""" 471 register_mcp_tools(app, mcp_module=__name__)