airbyte_ops_mcp.mcp.connector_versions
MCP tools for connector version lifecycle: cloud version overrides, progressive rollouts, and pre-release publishing.
MCP reference
MCP primitives registered by the connector_versions module of the airbyte-internal-ops server: 9 tool(s), 0 prompt(s), 0 resource(s).
Tools (9)
finalize_connector_rollout
Hints: destructive · open-world
Finalize a connector rollout by promoting, rolling back, or canceling.
This tool allows admins to finalize connector rollouts that are in progress. Use this after monitoring a rollout and determining it is ready for finalization.
IMPORTANT: Finalization is asynchronous. This tool sends a finalization
request to the platform API, which transitions the rollout to finalizing
state and triggers a Temporal workflow. The actual promotion (PR creation,
connector publish, registry update) or rollback (GCS cleanup, registry
recompile) happens asynchronously via the finalize_rollout.yml GitHub
Actions workflow. A successful response from this tool means the request
was accepted — NOT that the promotion/rollback is complete.
After calling this tool, you MUST verify:
- The
finalize_rollout.ymlworkflow ran successfully in GitHub Actions - For promotions: a merged PR exists (e.g.,
chore: finalize promote for <connector>) - The rollout state transitioned to its terminal state (
succeeded,failed_rolled_back, orcanceled) viaquery_prod_connector_rollouts
Admin-only operation - Requires:
- AIRBYTE_INTERNAL_ADMIN_FLAG=airbyte.io environment variable
approval_comment_url(Slack approval record URL fromescalate_to_human), ORadmin_user_email_overridewhen running inside the Ops Webapp.
Parameters:
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
docker_repository |
string |
yes | — | The docker repository (e.g., 'airbyte/source-youtube-analytics') |
docker_image_tag |
string |
yes | — | The docker image tag (e.g., '1.2.0-rc.2') |
actor_definition_id |
string |
yes | — | The actor definition ID (UUID) |
rollout_id |
string |
yes | — | The rollout ID (UUID). Can be found in the 'pin_origin' field of rollout data from query_prod_actors_by_pinned_connector_version. |
state |
enum("succeeded", "failed_rolled_back", "canceled") |
yes | — | The final state for the rollout: 'succeeded' promotes the RC to GA (default version for all users), 'failed_rolled_back' rolls back the RC, 'canceled' cancels the rollout without promotion or rollback. |
approval_comment_url |
string | null |
no | null |
URL to the Slack approval record. Obtain this by calling the escalate_to_human tool with approval_requested=True; the backend delivers the approval record URL when a human clicks Approve. Format: https:// |
admin_user_email_override |
string | null |
no | null |
Direct admin email override for webapp-initiated actions. When the Ops Webapp env var is set, this bypasses the approval URL requirement. Ignored in agent/cron environments. |
error_msg |
string | null |
no | null |
Optional error message for failed/canceled states. |
failed_reason |
string | null |
no | null |
Optional failure reason for failed/canceled states. |
retain_pins_on_cancellation |
boolean | null |
no | null |
If True, retain version pins when canceling. Only applicable when state is 'canceled'. |
Show input JSON schema
{
"additionalProperties": false,
"properties": {
"docker_repository": {
"description": "The docker repository (e.g., 'airbyte/source-youtube-analytics')",
"type": "string"
},
"docker_image_tag": {
"description": "The docker image tag (e.g., '1.2.0-rc.2')",
"type": "string"
},
"actor_definition_id": {
"description": "The actor definition ID (UUID)",
"type": "string"
},
"rollout_id": {
"description": "The rollout ID (UUID). Can be found in the 'pin_origin' field of rollout data from query_prod_actors_by_pinned_connector_version.",
"type": "string"
},
"state": {
"description": "The final state for the rollout: 'succeeded' promotes the RC to GA (default version for all users), 'failed_rolled_back' rolls back the RC, 'canceled' cancels the rollout without promotion or rollback.",
"enum": [
"succeeded",
"failed_rolled_back",
"canceled"
],
"type": "string"
},
"approval_comment_url": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "URL to the Slack approval record. Obtain this by calling the `escalate_to_human` tool with `approval_requested=True`; the backend delivers the approval record URL when a human clicks Approve. Format: https://<workspace>.slack.com/archives/... The admin email is automatically resolved from the approver's identity via the team roster."
},
"admin_user_email_override": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Direct admin email override for webapp-initiated actions. When the Ops Webapp env var is set, this bypasses the approval URL requirement. Ignored in agent/cron environments."
},
"error_msg": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Optional error message for failed/canceled states."
},
"failed_reason": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Optional failure reason for failed/canceled states."
},
"retain_pins_on_cancellation": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
],
"default": null,
"description": "If True, retain version pins when canceling. Only applicable when state is 'canceled'."
}
},
"required": [
"docker_repository",
"docker_image_tag",
"actor_definition_id",
"rollout_id",
"state"
],
"type": "object"
}
Show output JSON schema
{
"description": "Result of a connector rollout finalization operation.\n\nThis model provides detailed information about the outcome of finalizing\na connector rollout (promote, rollback, or cancel).",
"properties": {
"success": {
"description": "Whether the operation succeeded",
"type": "boolean"
},
"message": {
"description": "Human-readable message describing the result",
"type": "string"
},
"rollout_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "The rollout ID that was finalized"
},
"docker_repository": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "The docker repository (e.g., 'airbyte/source-github')"
},
"docker_image_tag": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "The docker image tag (e.g., '1.2.0-rc.2')"
},
"state": {
"anyOf": [
{
"enum": [
"succeeded",
"failed_rolled_back",
"canceled"
],
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "The final state of the rollout"
}
},
"required": [
"success",
"message"
],
"type": "object"
}
get_cloud_connector_version
Hints: read-only · idempotent · open-world
Get the current version information for a deployed connector.
Returns version details including the current version string and whether an override (pin) is applied.
Authentication credentials are resolved in priority order:
- Bearer token (Authorization header or AIRBYTE_CLOUD_BEARER_TOKEN env var)
- HTTP headers: X-Airbyte-Cloud-Client-Id, X-Airbyte-Cloud-Client-Secret
- Environment variables: AIRBYTE_CLOUD_CLIENT_ID, AIRBYTE_CLOUD_CLIENT_SECRET
Parameters:
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
workspace_id |
string | enum("266ebdfe-0d7b-4540-9817-de7e4505ba61") |
yes | — | The Airbyte Cloud workspace ID (UUID) or alias. Accepts '@devin-ai-sandbox' as an alias for the Devin AI sandbox workspace. |
actor_id |
string |
yes | — | The ID of the deployed connector (source or destination) |
actor_type |
enum("source", "destination") |
yes | — | The type of connector (source or destination) |
config_api_root |
string | null |
no | null |
Optional API root URL override for the Config API. Defaults to Airbyte Cloud (https://cloud.airbyte.com/api/v1). Use this to target local or self-hosted deployments. |
Show input JSON schema
{
"additionalProperties": false,
"properties": {
"workspace_id": {
"anyOf": [
{
"type": "string"
},
{
"description": "Workspace ID aliases that can be used in place of UUIDs.\n\nEach member's name is the alias (e.g., \"@devin-ai-sandbox\") and its value\nis the actual workspace UUID. Use `WorkspaceAliasEnum.resolve()` to\nresolve aliases to actual IDs.",
"enum": [
"266ebdfe-0d7b-4540-9817-de7e4505ba61"
],
"type": "string"
}
],
"description": "The Airbyte Cloud workspace ID (UUID) or alias. Accepts '@devin-ai-sandbox' as an alias for the Devin AI sandbox workspace."
},
"actor_id": {
"description": "The ID of the deployed connector (source or destination)",
"type": "string"
},
"actor_type": {
"description": "The type of connector (source or destination)",
"enum": [
"source",
"destination"
],
"type": "string"
},
"config_api_root": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Optional API root URL override for the Config API. Defaults to Airbyte Cloud (https://cloud.airbyte.com/api/v1). Use this to target local or self-hosted deployments."
}
},
"required": [
"workspace_id",
"actor_id",
"actor_type"
],
"type": "object"
}
Show output JSON schema
{
"description": "Information about a cloud connector's version.\n\nThis model represents the current version state of a deployed connector,\nincluding whether a version override (pin) is active.",
"properties": {
"connector_id": {
"description": "The ID of the deployed connector",
"type": "string"
},
"connector_type": {
"description": "The type of connector (source or destination)",
"enum": [
"source",
"destination"
],
"type": "string"
},
"version": {
"description": "The current version string (e.g., '0.1.0')",
"type": "string"
},
"is_version_pinned": {
"description": "Whether a version override is active for this connector",
"type": "boolean"
}
},
"required": [
"connector_id",
"connector_type",
"version",
"is_version_pinned"
],
"type": "object"
}
progress_connector_rollout
Hints: destructive · open-world
Progress a connector rollout by pinning actors to the RC version.
This tool progresses a connector rollout by either:
- Setting a target percentage of actors to pin to the RC version
- Specifying specific actor IDs to pin
Admin-only operation - Requires:
- AIRBYTE_INTERNAL_ADMIN_FLAG=airbyte.io environment variable
approval_comment_url(Slack approval record URL fromescalate_to_human), ORadmin_user_email_overridewhen running inside the Ops Webapp.
Parameters:
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
docker_repository |
string |
yes | — | The docker repository (e.g., 'airbyte/source-pokeapi') |
docker_image_tag |
string |
yes | — | The docker image tag (e.g., '0.3.48-rc.1') |
actor_definition_id |
string |
yes | — | The actor definition ID (UUID) |
rollout_id |
string |
yes | — | The rollout ID (UUID). Can be found from query_prod_connector_rollouts. |
approval_comment_url |
string | null |
no | null |
URL to the Slack approval record. Obtain this by calling the escalate_to_human tool with approval_requested=True; the backend delivers the approval record URL when a human clicks Approve. Format: https:// |
admin_user_email_override |
string | null |
no | null |
Direct admin email override for webapp-initiated actions. When the Ops Webapp env var is set, this bypasses the approval URL requirement. Ignored in agent/cron environments. |
target_percentage |
integer | null |
no | null |
Target percentage of actors to pin to the RC (1-100). Either target_percentage or actor_ids must be provided. |
actor_ids |
array<string> | null |
no | null |
Specific actor IDs to pin to the RC. Either target_percentage or actor_ids must be provided. |
Show input JSON schema
{
"additionalProperties": false,
"properties": {
"docker_repository": {
"description": "The docker repository (e.g., 'airbyte/source-pokeapi')",
"type": "string"
},
"docker_image_tag": {
"description": "The docker image tag (e.g., '0.3.48-rc.1')",
"type": "string"
},
"actor_definition_id": {
"description": "The actor definition ID (UUID)",
"type": "string"
},
"rollout_id": {
"description": "The rollout ID (UUID). Can be found from query_prod_connector_rollouts.",
"type": "string"
},
"approval_comment_url": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "URL to the Slack approval record. Obtain this by calling the `escalate_to_human` tool with `approval_requested=True`; the backend delivers the approval record URL when a human clicks Approve. Format: https://<workspace>.slack.com/archives/... The admin email is automatically resolved from the approver's identity via the team roster."
},
"admin_user_email_override": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Direct admin email override for webapp-initiated actions. When the Ops Webapp env var is set, this bypasses the approval URL requirement. Ignored in agent/cron environments."
},
"target_percentage": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"default": null,
"description": "Target percentage of actors to pin to the RC (1-100). Either target_percentage or actor_ids must be provided."
},
"actor_ids": {
"anyOf": [
{
"items": {
"type": "string"
},
"type": "array"
},
{
"type": "null"
}
],
"default": null,
"description": "Specific actor IDs to pin to the RC. Either target_percentage or actor_ids must be provided."
}
},
"required": [
"docker_repository",
"docker_image_tag",
"actor_definition_id",
"rollout_id"
],
"type": "object"
}
Show output JSON schema
{
"description": "Result of a connector rollout progress operation.\n\nThis model provides detailed information about the outcome of progressing\na connector rollout (pinning actors to the RC version).",
"properties": {
"success": {
"description": "Whether the operation succeeded",
"type": "boolean"
},
"message": {
"description": "Human-readable message describing the result",
"type": "string"
},
"rollout_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "The rollout ID that was progressed"
},
"docker_repository": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "The docker repository (e.g., 'airbyte/source-github')"
},
"docker_image_tag": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "The docker image tag (e.g., '1.2.0-rc.2')"
},
"target_percentage": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"default": null,
"description": "The target percentage of actors to pin"
}
},
"required": [
"success",
"message"
],
"type": "object"
}
publish_connector_to_airbyte_registry
Hints: open-world
Publish a connector to the Airbyte registry.
Currently only supports pre-release publishing. This tool triggers the publish-connectors-prerelease workflow in the airbytehq/airbyte repository (for OSS connectors) or the publish_enterprise_connectors workflow in airbytehq/airbyte-enterprise (for enterprise connectors), which publishes a pre-release version of the specified connector from the PR branch.
Pre-release versions are tagged with the format: {version}-preview.{7-char-git-sha} These versions are available for version pinning via the scoped_configuration API.
Requires GITHUB_CONNECTOR_PUBLISHING_PAT or GITHUB_TOKEN environment variable with 'actions:write' permission.
Parameters:
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
connector_name |
string |
yes | — | The connector name to publish (e.g., 'source-github', 'destination-postgres') |
pr_number |
integer |
yes | — | The pull request number containing the connector changes |
repo |
enum("airbyte", "airbyte-enterprise") |
no | "airbyte" |
Repository where the connector PR is located. Use 'airbyte' for OSS connectors (default) or 'airbyte-enterprise' for enterprise connectors. |
prerelease |
boolean |
no | true |
Must be True. Only prerelease publishing is supported at this time. |
Show input JSON schema
{
"additionalProperties": false,
"properties": {
"connector_name": {
"description": "The connector name to publish (e.g., 'source-github', 'destination-postgres')",
"type": "string"
},
"pr_number": {
"description": "The pull request number containing the connector changes",
"type": "integer"
},
"repo": {
"description": "Repository where the connector PR is located. Use 'airbyte' for OSS connectors (default) or 'airbyte-enterprise' for enterprise connectors.",
"enum": [
"airbyte",
"airbyte-enterprise"
],
"type": "string",
"default": "airbyte"
},
"prerelease": {
"const": true,
"default": true,
"description": "Must be True. Only prerelease publishing is supported at this time.",
"type": "boolean"
}
},
"required": [
"connector_name",
"pr_number"
],
"type": "object"
}
Show output JSON schema
{
"description": "Response model for publish_connector_to_airbyte_registry MCP tool.",
"properties": {
"success": {
"type": "boolean"
},
"message": {
"type": "string"
},
"workflow_url": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null
},
"connector_name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null
},
"pr_number": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"default": null
},
"docker_image": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null
},
"docker_image_tag": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null
}
},
"required": [
"success",
"message"
],
"type": "object"
}
query_prod_rollout_monitoring_stats
Hints: read-only · idempotent
Get monitoring stats for a connector rollout.
Returns actor selection info and per-actor sync stats for actors participating in the rollout. This uses the platform API's /get_actor_sync_info endpoint which filters sync stats to only include syncs that actually used the RC version associated with the rollout.
This is more accurate than SQL-based approaches which count all syncs regardless of which connector version was used.
Parameters:
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
rollout_id |
string |
yes | — | Rollout UUID to get monitoring stats for |
Show input JSON schema
{
"additionalProperties": false,
"properties": {
"rollout_id": {
"description": "Rollout UUID to get monitoring stats for",
"type": "string"
}
},
"required": [
"rollout_id"
],
"type": "object"
}
Show output JSON schema
{
"description": "Complete monitoring result for a rollout from the platform API.\n\nThis uses the platform API's /get_actor_sync_info endpoint which filters\nsync stats to only include syncs that actually used the RC version\nassociated with the rollout.",
"properties": {
"rollout_id": {
"description": "Rollout UUID",
"type": "string"
},
"actor_selection_info": {
"description": "Actor selection info for the rollout",
"properties": {
"num_actors": {
"description": "Total actors using this connector",
"type": "integer"
},
"num_pinned_to_connector_rollout": {
"description": "Actors specifically pinned to this rollout",
"type": "integer"
},
"num_actors_eligible_or_already_pinned": {
"description": "Actors eligible for pinning or already pinned",
"type": "integer"
}
},
"required": [
"num_actors",
"num_pinned_to_connector_rollout",
"num_actors_eligible_or_already_pinned"
],
"type": "object"
},
"actor_sync_stats": {
"description": "Per-actor sync stats for actors pinned to the rollout",
"items": {
"description": "Per-actor sync stats for a rollout (only syncs using the RC version).",
"properties": {
"actor_id": {
"description": "Actor UUID",
"type": "string"
},
"num_connections": {
"description": "Number of connections using this actor",
"type": "integer"
},
"num_succeeded": {
"description": "Number of successful syncs using the RC version",
"type": "integer"
},
"num_failed": {
"description": "Number of failed syncs using the RC version",
"type": "integer"
}
},
"required": [
"actor_id",
"num_connections",
"num_succeeded",
"num_failed"
],
"type": "object"
},
"type": "array"
}
},
"required": [
"rollout_id",
"actor_selection_info",
"actor_sync_stats"
],
"type": "object"
}
set_cloud_connector_version_override
Hints: destructive · open-world
Set or clear a version override for a deployed connector.
Admin-only operation - Requires:
- AIRBYTE_INTERNAL_ADMIN_FLAG=airbyte.io environment variable
- issue_url parameter (GitHub issue URL for context)
- approval_comment_url (Slack approval record URL from
escalate_to_human)
The admin user email is automatically derived from the Slack approval record, resolving the approver's @airbyte.io email via the team roster.
You must specify EXACTLY ONE of version OR unset=True, but not both.
When setting a version, override_reason is required.
The customer_tier_filter parameter gates the operation: the call fails if
the actual tier of the workspace's organization does not match. Use ALL
to bypass the check (a warning is still emitted for sensitive tiers).
Business rules enforced:
- Dev versions (-dev): Only creator can unpin their own dev version override
- Production versions: Require strong justification mentioning customer/support/investigation
- Release candidates (-rc): Any admin can pin/unpin RC versions
Authentication credentials are resolved in priority order:
- Bearer token (Authorization header or AIRBYTE_CLOUD_BEARER_TOKEN env var)
- HTTP headers: X-Airbyte-Cloud-Client-Id, X-Airbyte-Cloud-Client-Secret
- Environment variables: AIRBYTE_CLOUD_CLIENT_ID, AIRBYTE_CLOUD_CLIENT_SECRET
Parameters:
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
workspace_id |
string | enum("266ebdfe-0d7b-4540-9817-de7e4505ba61") |
yes | — | The Airbyte Cloud workspace ID (UUID) or alias. Accepts '@devin-ai-sandbox' as an alias for the Devin AI sandbox workspace. |
actor_id |
string |
yes | — | The ID of the deployed connector (source or destination) |
actor_type |
enum("source", "destination") |
yes | — | The type of connector (source or destination) |
approval_comment_url |
string | null |
no | null |
URL to the Slack approval record. Obtain this by calling the escalate_to_human tool with approval_requested=True; the backend delivers the approval record URL when a human clicks Approve. Format: https:// |
version |
string | null |
no | null |
The semver version string to pin to (e.g., '0.1.0'). Must be None if unset is True. |
unset |
boolean |
no | false |
If True, removes any existing version override. Cannot be True if version is provided. |
override_reason |
string | null |
no | null |
Required when setting a version. Explanation for the override (min 10 characters). |
override_reason_reference_url |
string | null |
no | null |
Optional URL with more context (e.g., issue link). |
issue_url |
string | null |
no | null |
URL to the GitHub issue providing context for this operation. Must be a valid GitHub URL (https://github.com/...). Required for authorization. |
ai_agent_session_url |
string | null |
no | null |
URL to the AI agent session driving this operation, if applicable. Provides additional auditability for AI-driven operations. |
force |
boolean |
no | false |
If True, allow overwriting an existing version pin. Existing pins may have been set by rollouts, breaking-change migrations, or other operators. Defaults to False. NOTE: force=True only bypasses the existing-pin check — major-version crossings are always blocked and cannot be overridden. |
config_api_root |
string | null |
no | null |
Optional API root URL override for the Config API. Defaults to Airbyte Cloud (https://cloud.airbyte.com/api/v1). Use this to target local or self-hosted deployments. |
customer_tier_filter |
enum("TIER_0", "TIER_1", "TIER_2", "UNKNOWN", "ALL") |
no | "TIER_2" |
Required tier filter: 'TIER_0', 'TIER_1', 'TIER_2', 'UNKNOWN', or 'ALL'. The operation will be rejected if the actual customer tier does not match. Use 'ALL' to proceed regardless of tier (a warning is shown for sensitive tiers). |
Show input JSON schema
{
"additionalProperties": false,
"properties": {
"workspace_id": {
"anyOf": [
{
"type": "string"
},
{
"description": "Workspace ID aliases that can be used in place of UUIDs.\n\nEach member's name is the alias (e.g., \"@devin-ai-sandbox\") and its value\nis the actual workspace UUID. Use `WorkspaceAliasEnum.resolve()` to\nresolve aliases to actual IDs.",
"enum": [
"266ebdfe-0d7b-4540-9817-de7e4505ba61"
],
"type": "string"
}
],
"description": "The Airbyte Cloud workspace ID (UUID) or alias. Accepts '@devin-ai-sandbox' as an alias for the Devin AI sandbox workspace."
},
"actor_id": {
"description": "The ID of the deployed connector (source or destination)",
"type": "string"
},
"actor_type": {
"description": "The type of connector (source or destination)",
"enum": [
"source",
"destination"
],
"type": "string"
},
"approval_comment_url": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "URL to the Slack approval record. Obtain this by calling the `escalate_to_human` tool with `approval_requested=True`; the backend delivers the approval record URL when a human clicks Approve. Format: https://<workspace>.slack.com/archives/... The admin email is automatically resolved from the approver's identity via the team roster."
},
"version": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "The semver version string to pin to (e.g., '0.1.0'). Must be None if unset is True."
},
"unset": {
"default": false,
"description": "If True, removes any existing version override. Cannot be True if version is provided.",
"type": "boolean"
},
"override_reason": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Required when setting a version. Explanation for the override (min 10 characters)."
},
"override_reason_reference_url": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Optional URL with more context (e.g., issue link)."
},
"issue_url": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "URL to the GitHub issue providing context for this operation. Must be a valid GitHub URL (https://github.com/...). Required for authorization."
},
"ai_agent_session_url": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "URL to the AI agent session driving this operation, if applicable. Provides additional auditability for AI-driven operations."
},
"force": {
"default": false,
"description": "If `True`, allow overwriting an existing version pin. Existing pins may have been set by rollouts, breaking-change migrations, or other operators. Defaults to `False`. NOTE: `force=True` only bypasses the existing-pin check \u2014 major-version crossings are always blocked and cannot be overridden.",
"type": "boolean"
},
"config_api_root": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Optional API root URL override for the Config API. Defaults to Airbyte Cloud (https://cloud.airbyte.com/api/v1). Use this to target local or self-hosted deployments."
},
"customer_tier_filter": {
"default": "TIER_2",
"description": "Required tier filter: 'TIER_0', 'TIER_1', 'TIER_2', 'UNKNOWN', or 'ALL'. The operation will be rejected if the actual customer tier does not match. Use 'ALL' to proceed regardless of tier (a warning is shown for sensitive tiers).",
"enum": [
"TIER_0",
"TIER_1",
"TIER_2",
"UNKNOWN",
"ALL"
],
"type": "string"
}
},
"required": [
"workspace_id",
"actor_id",
"actor_type"
],
"type": "object"
}
Show output JSON schema
{
"description": "Result of a version override operation (set or clear).\n\nThis model provides detailed information about the outcome of a version\npinning or unpinning operation.",
"properties": {
"success": {
"description": "Whether the operation succeeded",
"type": "boolean"
},
"message": {
"description": "Human-readable message describing the result",
"type": "string"
},
"connector_id": {
"description": "The ID of the connector that was modified",
"type": "string"
},
"connector_type": {
"description": "The type of connector (source or destination)",
"enum": [
"source",
"destination"
],
"type": "string"
},
"previous_version": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "The version before the operation (None if not available)"
},
"new_version": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "The version after the operation (None if cleared or failed)"
},
"was_pinned_before": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
],
"default": null,
"description": "Whether a pin was active before the operation"
},
"is_pinned_after": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
],
"default": null,
"description": "Whether a pin is active after the operation"
},
"customer_tier": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Customer tier of the affected entity (TIER_0, TIER_1, TIER_2, UNKNOWN). Included as a guardrail annotation."
},
"is_eu": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
],
"default": null,
"description": "Whether the affected entity is in the EU region."
},
"tier_warning": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Warning message if the operation targets a sensitive customer tier."
},
"warnings": {
"description": "Warnings raised by this operation.",
"items": {
"type": "string"
},
"type": "array"
}
},
"required": [
"success",
"message",
"connector_id",
"connector_type"
],
"type": "object"
}
set_organization_connector_version_override
Hints: destructive · open-world
Set or clear an organization-level version override for a connector type.
This pins ALL instances of a connector type across an entire organization to a specific version. For example, pinning 'source-github' at organization level means all GitHub sources in all workspaces within that organization will use the pinned version.
Admin-only operation - Requires:
- AIRBYTE_INTERNAL_ADMIN_FLAG=airbyte.io environment variable
- issue_url parameter (GitHub issue URL for context)
- approval_comment_url (Slack approval record URL from
escalate_to_human)
You must specify EXACTLY ONE of version OR unset=True, but not both.
When setting a version, override_reason is required.
The customer_tier_filter parameter gates the operation: the call fails if
the actual tier of the organization does not match. Use ALL to bypass
the check (a warning is still emitted for sensitive tiers).
Parameters:
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
organization_id |
string |
yes | — | The Airbyte Cloud organization ID. |
connector_name |
string |
yes | — | The connector name (e.g., 'source-github', 'destination-bigquery'). |
connector_type |
enum("source", "destination") |
yes | — | The type of connector (source or destination) |
approval_comment_url |
string | null |
no | null |
URL to the Slack approval record. Obtain this by calling the escalate_to_human tool with approval_requested=True; the backend delivers the approval record URL when a human clicks Approve. Format: https:// |
version |
string | null |
no | null |
The semver version string to pin to (e.g., '0.1.0'). Must be None if unset is True. |
unset |
boolean |
no | false |
If True, removes any existing version override. Cannot be True if version is provided. |
override_reason |
string | null |
no | null |
Required when setting a version. Explanation for the override (min 10 characters). |
override_reason_reference_url |
string | null |
no | null |
Optional URL with more context (e.g., issue link). |
issue_url |
string | null |
no | null |
URL to the GitHub issue providing context for this operation. Must be a valid GitHub URL (https://github.com/...). Required for authorization. |
ai_agent_session_url |
string | null |
no | null |
URL to the AI agent session driving this operation, if applicable. Provides additional auditability for AI-driven operations. |
force |
boolean |
no | false |
If True, allow overwriting an existing version pin. Existing pins may have been set by rollouts, breaking-change migrations, or other operators. Defaults to False. NOTE: force=True only bypasses the existing-pin check — major-version crossings are always blocked and cannot be overridden. |
config_api_root |
string | null |
no | null |
Optional API root URL override for the Config API. Defaults to Airbyte Cloud (https://cloud.airbyte.com/api/v1). Use this to target local or self-hosted deployments. |
customer_tier_filter |
enum("TIER_0", "TIER_1", "TIER_2", "UNKNOWN", "ALL") |
no | "TIER_2" |
Required tier filter: 'TIER_0', 'TIER_1', 'TIER_2', 'UNKNOWN', or 'ALL'. The operation will be rejected if the actual customer tier does not match. Use 'ALL' to proceed regardless of tier (a warning is shown for sensitive tiers). |
Show input JSON schema
{
"additionalProperties": false,
"properties": {
"organization_id": {
"description": "The Airbyte Cloud organization ID.",
"type": "string"
},
"connector_name": {
"description": "The connector name (e.g., 'source-github', 'destination-bigquery').",
"type": "string"
},
"connector_type": {
"description": "The type of connector (source or destination)",
"enum": [
"source",
"destination"
],
"type": "string"
},
"approval_comment_url": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "URL to the Slack approval record. Obtain this by calling the `escalate_to_human` tool with `approval_requested=True`; the backend delivers the approval record URL when a human clicks Approve. Format: https://<workspace>.slack.com/archives/... The admin email is automatically resolved from the approver's identity via the team roster."
},
"version": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "The semver version string to pin to (e.g., '0.1.0'). Must be None if unset is True."
},
"unset": {
"default": false,
"description": "If True, removes any existing version override. Cannot be True if version is provided.",
"type": "boolean"
},
"override_reason": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Required when setting a version. Explanation for the override (min 10 characters)."
},
"override_reason_reference_url": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Optional URL with more context (e.g., issue link)."
},
"issue_url": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "URL to the GitHub issue providing context for this operation. Must be a valid GitHub URL (https://github.com/...). Required for authorization."
},
"ai_agent_session_url": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "URL to the AI agent session driving this operation, if applicable. Provides additional auditability for AI-driven operations."
},
"force": {
"default": false,
"description": "If `True`, allow overwriting an existing version pin. Existing pins may have been set by rollouts, breaking-change migrations, or other operators. Defaults to `False`. NOTE: `force=True` only bypasses the existing-pin check \u2014 major-version crossings are always blocked and cannot be overridden.",
"type": "boolean"
},
"config_api_root": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Optional API root URL override for the Config API. Defaults to Airbyte Cloud (https://cloud.airbyte.com/api/v1). Use this to target local or self-hosted deployments."
},
"customer_tier_filter": {
"default": "TIER_2",
"description": "Required tier filter: 'TIER_0', 'TIER_1', 'TIER_2', 'UNKNOWN', or 'ALL'. The operation will be rejected if the actual customer tier does not match. Use 'ALL' to proceed regardless of tier (a warning is shown for sensitive tiers).",
"enum": [
"TIER_0",
"TIER_1",
"TIER_2",
"UNKNOWN",
"ALL"
],
"type": "string"
}
},
"required": [
"organization_id",
"connector_name",
"connector_type"
],
"type": "object"
}
Show output JSON schema
{
"description": "Result of an organization-level version override operation.\n\nThis model provides detailed information about the outcome of an organization-level\nversion pinning or unpinning operation.",
"properties": {
"success": {
"description": "Whether the operation succeeded",
"type": "boolean"
},
"message": {
"description": "Human-readable message describing the result",
"type": "string"
},
"organization_id": {
"description": "The organization ID",
"type": "string"
},
"connector_name": {
"description": "The connector name (e.g., 'source-github')",
"type": "string"
},
"connector_type": {
"description": "The type of connector (source or destination)",
"enum": [
"source",
"destination"
],
"type": "string"
},
"version": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "The version that was pinned (None if cleared or failed)"
},
"customer_tier": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Customer tier of the organization (TIER_0, TIER_1, TIER_2, UNKNOWN). Included as a guardrail annotation."
},
"tier_warning": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Warning message if the operation targets a sensitive customer tier."
},
"warnings": {
"description": "Warnings raised by this operation.",
"items": {
"type": "string"
},
"type": "array"
}
},
"required": [
"success",
"message",
"organization_id",
"connector_name",
"connector_type"
],
"type": "object"
}
set_workspace_connector_version_override
Hints: destructive · open-world
Set or clear a workspace-level version override for a connector type.
This pins ALL instances of a connector type within a workspace to a specific version. For example, pinning 'source-github' at workspace level means all GitHub sources in that workspace will use the pinned version.
Admin-only operation - Requires:
- AIRBYTE_INTERNAL_ADMIN_FLAG=airbyte.io environment variable
- issue_url parameter (GitHub issue URL for context)
- approval_comment_url (Slack approval record URL from
escalate_to_human)
You must specify EXACTLY ONE of version OR unset=True, but not both.
When setting a version, override_reason is required.
The customer_tier_filter parameter gates the operation: the call fails if
the actual tier of the workspace's organization does not match. Use ALL
to bypass the check (a warning is still emitted for sensitive tiers).
Parameters:
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
workspace_id |
string | enum("266ebdfe-0d7b-4540-9817-de7e4505ba61") |
yes | — | The Airbyte Cloud workspace ID (UUID) or alias. Accepts '@devin-ai-sandbox' as an alias for the Devin AI sandbox workspace. |
connector_name |
string |
yes | — | The connector name (e.g., 'source-github', 'destination-bigquery'). |
connector_type |
enum("source", "destination") |
yes | — | The type of connector (source or destination) |
approval_comment_url |
string | null |
no | null |
URL to the Slack approval record. Obtain this by calling the escalate_to_human tool with approval_requested=True; the backend delivers the approval record URL when a human clicks Approve. Format: https:// |
version |
string | null |
no | null |
The semver version string to pin to (e.g., '0.1.0'). Must be None if unset is True. |
unset |
boolean |
no | false |
If True, removes any existing version override. Cannot be True if version is provided. |
override_reason |
string | null |
no | null |
Required when setting a version. Explanation for the override (min 10 characters). |
override_reason_reference_url |
string | null |
no | null |
Optional URL with more context (e.g., issue link). |
issue_url |
string | null |
no | null |
URL to the GitHub issue providing context for this operation. Must be a valid GitHub URL (https://github.com/...). Required for authorization. |
ai_agent_session_url |
string | null |
no | null |
URL to the AI agent session driving this operation, if applicable. Provides additional auditability for AI-driven operations. |
force |
boolean |
no | false |
If True, allow overwriting an existing version pin. Existing pins may have been set by rollouts, breaking-change migrations, or other operators. Defaults to False. NOTE: force=True only bypasses the existing-pin check — major-version crossings are always blocked and cannot be overridden. |
config_api_root |
string | null |
no | null |
Optional API root URL override for the Config API. Defaults to Airbyte Cloud (https://cloud.airbyte.com/api/v1). Use this to target local or self-hosted deployments. |
customer_tier_filter |
enum("TIER_0", "TIER_1", "TIER_2", "UNKNOWN", "ALL") |
no | "TIER_2" |
Required tier filter: 'TIER_0', 'TIER_1', 'TIER_2', 'UNKNOWN', or 'ALL'. The operation will be rejected if the actual customer tier does not match. Use 'ALL' to proceed regardless of tier (a warning is shown for sensitive tiers). |
Show input JSON schema
{
"additionalProperties": false,
"properties": {
"workspace_id": {
"anyOf": [
{
"type": "string"
},
{
"description": "Workspace ID aliases that can be used in place of UUIDs.\n\nEach member's name is the alias (e.g., \"@devin-ai-sandbox\") and its value\nis the actual workspace UUID. Use `WorkspaceAliasEnum.resolve()` to\nresolve aliases to actual IDs.",
"enum": [
"266ebdfe-0d7b-4540-9817-de7e4505ba61"
],
"type": "string"
}
],
"description": "The Airbyte Cloud workspace ID (UUID) or alias. Accepts '@devin-ai-sandbox' as an alias for the Devin AI sandbox workspace."
},
"connector_name": {
"description": "The connector name (e.g., 'source-github', 'destination-bigquery').",
"type": "string"
},
"connector_type": {
"description": "The type of connector (source or destination)",
"enum": [
"source",
"destination"
],
"type": "string"
},
"approval_comment_url": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "URL to the Slack approval record. Obtain this by calling the `escalate_to_human` tool with `approval_requested=True`; the backend delivers the approval record URL when a human clicks Approve. Format: https://<workspace>.slack.com/archives/... The admin email is automatically resolved from the approver's identity via the team roster."
},
"version": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "The semver version string to pin to (e.g., '0.1.0'). Must be None if unset is True."
},
"unset": {
"default": false,
"description": "If True, removes any existing version override. Cannot be True if version is provided.",
"type": "boolean"
},
"override_reason": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Required when setting a version. Explanation for the override (min 10 characters)."
},
"override_reason_reference_url": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Optional URL with more context (e.g., issue link)."
},
"issue_url": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "URL to the GitHub issue providing context for this operation. Must be a valid GitHub URL (https://github.com/...). Required for authorization."
},
"ai_agent_session_url": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "URL to the AI agent session driving this operation, if applicable. Provides additional auditability for AI-driven operations."
},
"force": {
"default": false,
"description": "If `True`, allow overwriting an existing version pin. Existing pins may have been set by rollouts, breaking-change migrations, or other operators. Defaults to `False`. NOTE: `force=True` only bypasses the existing-pin check \u2014 major-version crossings are always blocked and cannot be overridden.",
"type": "boolean"
},
"config_api_root": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Optional API root URL override for the Config API. Defaults to Airbyte Cloud (https://cloud.airbyte.com/api/v1). Use this to target local or self-hosted deployments."
},
"customer_tier_filter": {
"default": "TIER_2",
"description": "Required tier filter: 'TIER_0', 'TIER_1', 'TIER_2', 'UNKNOWN', or 'ALL'. The operation will be rejected if the actual customer tier does not match. Use 'ALL' to proceed regardless of tier (a warning is shown for sensitive tiers).",
"enum": [
"TIER_0",
"TIER_1",
"TIER_2",
"UNKNOWN",
"ALL"
],
"type": "string"
}
},
"required": [
"workspace_id",
"connector_name",
"connector_type"
],
"type": "object"
}
Show output JSON schema
{
"description": "Result of a workspace-level version override operation.\n\nThis model provides detailed information about the outcome of a workspace-level\nversion pinning or unpinning operation.",
"properties": {
"success": {
"description": "Whether the operation succeeded",
"type": "boolean"
},
"message": {
"description": "Human-readable message describing the result",
"type": "string"
},
"workspace_id": {
"description": "The workspace ID",
"type": "string"
},
"connector_name": {
"description": "The connector name (e.g., 'source-github')",
"type": "string"
},
"connector_type": {
"description": "The type of connector (source or destination)",
"enum": [
"source",
"destination"
],
"type": "string"
},
"version": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "The version that was pinned (None if cleared or failed)"
},
"customer_tier": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Customer tier of the workspace's organization (TIER_0, TIER_1, TIER_2, UNKNOWN). Included as a guardrail annotation."
},
"is_eu": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
],
"default": null,
"description": "Whether the workspace is in the EU region."
},
"tier_warning": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Warning message if the operation targets a sensitive customer tier."
},
"warnings": {
"description": "Warnings raised by this operation.",
"items": {
"type": "string"
},
"type": "array"
}
},
"required": [
"success",
"message",
"workspace_id",
"connector_name",
"connector_type"
],
"type": "object"
}
start_connector_rollout
Hints: destructive · open-world
Start or configure a connector rollout workflow.
This tool configures and starts a connector rollout workflow. It can be called multiple times while the rollout is in INITIALIZED state to update the configuration (strategy, percentages). Once the Temporal workflow starts and the state transitions to WORKFLOW_STARTED, the configuration is locked and cannot be changed.
Behavior:
- If rollout is INITIALIZED: Updates configuration and starts the workflow
- If rollout is already started: Returns an error (configuration is locked)
Configuration Parameters:
- rollout_strategy: 'manual' (default), 'automated', or 'overridden'
- initial_rollout_pct: Step size for progression (default: 25%)
- final_target_rollout_pct: Maximum percentage to pin (default: 50%)
- customer_tier: Customer tier to target - 'TIER_0', 'TIER_1', 'TIER_2', or 'ALL' (default: TIER_2)
Admin-only operation - Requires:
- AIRBYTE_INTERNAL_ADMIN_FLAG=airbyte.io environment variable
approval_comment_url(Slack approval record URL fromescalate_to_human), ORadmin_user_email_overridewhen running inside the Ops Webapp.
Parameters:
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
docker_repository |
string |
yes | — | The docker repository (e.g., 'airbyte/source-pokeapi') |
docker_image_tag |
string |
yes | — | The docker image tag (e.g., '0.3.48-rc.1') |
actor_definition_id |
string |
yes | — | The actor definition ID (UUID) |
approval_comment_url |
string | null |
no | null |
URL to the Slack approval record. Obtain this by calling the escalate_to_human tool with approval_requested=True; the backend delivers the approval record URL when a human clicks Approve. Format: https:// |
admin_user_email_override |
string | null |
no | null |
Direct admin email override for webapp-initiated actions. When the Ops Webapp env var is set, this bypasses the approval URL requirement. Ignored in agent/cron environments. |
rollout_strategy |
enum("manual", "automated", "overridden") |
no | "manual" |
The rollout strategy: 'manual' for manual control of rollout progression, 'automated' for automatic progression based on metrics, 'overridden' for special cases where normal rules are bypassed. |
initial_rollout_pct |
integer | null |
no | null |
Initial/step percentage for rollout progression (0-100). For automated rollouts, this is the percentage increment per step. For example, 25 means the rollout will advance by 25% each step. Default is 25% if not specified. |
final_target_rollout_pct |
integer | null |
no | null |
Maximum percentage of actors to pin (0-100). The rollout will not exceed this percentage. For example, 50 means at most 50% of actors will be pinned to the RC. Default is 50% if not specified. |
customer_tier |
enum("TIER_0", "TIER_1", "TIER_2", "ALL") | null |
no | null |
The customer tier to target for this rollout. Each tier represents a different group of customers: 'TIER_0' for the highest-priority customers, 'TIER_1' for mid-tier customers, 'TIER_2' for the broadest customer group (default if not specified), 'ALL' to target all customer tiers. When not specified, the platform defaults to TIER_2 only. |
Show input JSON schema
{
"additionalProperties": false,
"properties": {
"docker_repository": {
"description": "The docker repository (e.g., 'airbyte/source-pokeapi')",
"type": "string"
},
"docker_image_tag": {
"description": "The docker image tag (e.g., '0.3.48-rc.1')",
"type": "string"
},
"actor_definition_id": {
"description": "The actor definition ID (UUID)",
"type": "string"
},
"approval_comment_url": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "URL to the Slack approval record. Obtain this by calling the `escalate_to_human` tool with `approval_requested=True`; the backend delivers the approval record URL when a human clicks Approve. Format: https://<workspace>.slack.com/archives/... The admin email is automatically resolved from the approver's identity via the team roster."
},
"admin_user_email_override": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Direct admin email override for webapp-initiated actions. When the Ops Webapp env var is set, this bypasses the approval URL requirement. Ignored in agent/cron environments."
},
"rollout_strategy": {
"default": "manual",
"description": "The rollout strategy: 'manual' for manual control of rollout progression, 'automated' for automatic progression based on metrics, 'overridden' for special cases where normal rules are bypassed.",
"enum": [
"manual",
"automated",
"overridden"
],
"type": "string"
},
"initial_rollout_pct": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"default": null,
"description": "Initial/step percentage for rollout progression (0-100). For automated rollouts, this is the percentage increment per step. For example, 25 means the rollout will advance by 25% each step. Default is 25% if not specified."
},
"final_target_rollout_pct": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"default": null,
"description": "Maximum percentage of actors to pin (0-100). The rollout will not exceed this percentage. For example, 50 means at most 50% of actors will be pinned to the RC. Default is 50% if not specified."
},
"customer_tier": {
"anyOf": [
{
"enum": [
"TIER_0",
"TIER_1",
"TIER_2",
"ALL"
],
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "The customer tier to target for this rollout. Each tier represents a different group of customers: 'TIER_0' for the highest-priority customers, 'TIER_1' for mid-tier customers, 'TIER_2' for the broadest customer group (default if not specified), 'ALL' to target all customer tiers. When not specified, the platform defaults to TIER_2 only."
}
},
"required": [
"docker_repository",
"docker_image_tag",
"actor_definition_id"
],
"type": "object"
}
Show output JSON schema
{
"description": "Result of a connector rollout start operation.\n\nThis model provides detailed information about the outcome of starting\na connector rollout workflow.",
"properties": {
"success": {
"description": "Whether the operation succeeded",
"type": "boolean"
},
"message": {
"description": "Human-readable message describing the result",
"type": "string"
},
"docker_repository": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "The docker repository (e.g., 'airbyte/source-github')"
},
"docker_image_tag": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "The docker image tag (e.g., '1.2.0-rc.2')"
},
"actor_definition_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "The actor definition ID (UUID)"
},
"rollout_strategy": {
"anyOf": [
{
"enum": [
"manual",
"automated",
"overridden"
],
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "The rollout strategy used"
}
},
"required": [
"success",
"message"
],
"type": "object"
}
1# Copyright (c) 2025 Airbyte, Inc., all rights reserved. 2"""MCP tools for connector version lifecycle: cloud version overrides, progressive rollouts, and pre-release publishing. 3 4## MCP reference 5 6.. include:: ../../../docs/mcp-generated/connector_versions.md 7 :start-line: 2 8""" 9 10# NOTE: We intentionally do NOT use `from __future__ import annotations` here. 11# FastMCP has issues resolving forward references when PEP 563 deferred annotations 12# are used. See: https://github.com/jlowin/fastmcp/issues/905 13# Python 3.12+ supports modern type hint syntax natively, so this is not needed. 14 15__all__: list[str] = [] 16 17import base64 18import logging 19from dataclasses import dataclass 20from enum import StrEnum 21from typing import Annotated, Literal 22 23import requests 24import yaml 25from airbyte import constants 26from airbyte.exceptions import PyAirbyteInputError 27from fastmcp import Context, FastMCP 28from fastmcp_extensions import get_mcp_config, mcp_tool, register_mcp_tools 29from pydantic import BaseModel, Field 30 31from airbyte_ops_mcp.airbyte_repo.bump_version import strip_prerelease_suffix 32from airbyte_ops_mcp.approval_resolution import ( 33 ApprovalStatus, 34 check_approval_status, 35) 36from airbyte_ops_mcp.cloud_admin import api_client 37from airbyte_ops_mcp.cloud_admin.auth import ( 38 CloudAuthError, 39 require_internal_admin_flag_only, 40) 41from airbyte_ops_mcp.cloud_admin.models import ( 42 ConnectorRolloutFinalizeResult, 43 ConnectorRolloutProgressResult, 44 ConnectorRolloutStartResult, 45 ConnectorVersionInfo, 46 OrganizationVersionOverrideResult, 47 VersionOverrideOperationResult, 48 WorkspaceVersionOverrideResult, 49) 50from airbyte_ops_mcp.cloud_admin.version_overrides import ( 51 VersionOverrideTarget, 52 get_connector_version_info, 53 set_version_override, 54) 55from airbyte_ops_mcp.connector_ops.rollouts._helpers import ( 56 count_eligible_or_pinned_actors, 57) 58from airbyte_ops_mcp.constants import ServerConfigKey, WorkspaceAliasEnum 59from airbyte_ops_mcp.github_actions import trigger_workflow_dispatch 60from airbyte_ops_mcp.github_api import ( 61 GITHUB_API_BASE, 62 get_pr_head_ref, 63 resolve_ci_trigger_github_token, 64) 65from airbyte_ops_mcp.mcp.cloud_auth import resolve_cloud_auth 66from airbyte_ops_mcp.tier_cache import TierFilter, resolve_workspace 67 68logger = logging.getLogger(__name__) 69 70 71@mcp_tool( 72 read_only=True, 73 idempotent=True, 74 open_world=True, 75) 76def get_cloud_connector_version( 77 workspace_id: Annotated[ 78 str | WorkspaceAliasEnum, 79 Field( 80 description="The Airbyte Cloud workspace ID (UUID) or alias. " 81 "Accepts '@devin-ai-sandbox' as an alias for the Devin AI sandbox workspace." 82 ), 83 ], 84 actor_id: Annotated[ 85 str, "The ID of the deployed connector (source or destination)" 86 ], 87 actor_type: Annotated[ 88 Literal["source", "destination"], 89 "The type of connector (source or destination)", 90 ], 91 config_api_root: Annotated[ 92 str | None, 93 Field( 94 description="Optional API root URL override for the Config API. " 95 "Defaults to Airbyte Cloud (https://cloud.airbyte.com/api/v1). " 96 "Use this to target local or self-hosted deployments.", 97 default=None, 98 ), 99 ] = None, 100 *, 101 ctx: Context, 102) -> ConnectorVersionInfo: 103 """Get the current version information for a deployed connector. 104 105 Returns version details including the current version string and whether 106 an override (pin) is applied. 107 108 Authentication credentials are resolved in priority order: 109 110 1. Bearer token (Authorization header or AIRBYTE_CLOUD_BEARER_TOKEN env var) 111 2. HTTP headers: X-Airbyte-Cloud-Client-Id, X-Airbyte-Cloud-Client-Secret 112 3. Environment variables: AIRBYTE_CLOUD_CLIENT_ID, AIRBYTE_CLOUD_CLIENT_SECRET 113 """ 114 resolved_workspace_id = WorkspaceAliasEnum.resolve(workspace_id) 115 assert resolved_workspace_id is not None # workspace_id is required 116 117 return get_connector_version_info( 118 auth=resolve_cloud_auth(ctx), 119 workspace_id=resolved_workspace_id, 120 actor_id=actor_id, 121 actor_type=actor_type, 122 config_api_root=config_api_root, 123 ) 124 125 126@mcp_tool( 127 destructive=True, 128 idempotent=False, 129 open_world=True, 130) 131def set_cloud_connector_version_override( 132 workspace_id: Annotated[ 133 str | WorkspaceAliasEnum, 134 Field( 135 description="The Airbyte Cloud workspace ID (UUID) or alias. " 136 "Accepts '@devin-ai-sandbox' as an alias for the Devin AI sandbox workspace." 137 ), 138 ], 139 actor_id: Annotated[ 140 str, "The ID of the deployed connector (source or destination)" 141 ], 142 actor_type: Annotated[ 143 Literal["source", "destination"], 144 "The type of connector (source or destination)", 145 ], 146 approval_comment_url: Annotated[ 147 str | None, 148 Field( 149 description="URL to the Slack approval record. Obtain this by calling the " 150 "`escalate_to_human` tool with `approval_requested=True`; the backend delivers " 151 "the approval record URL when a human clicks Approve. " 152 "Format: https://<workspace>.slack.com/archives/... " 153 "The admin email is automatically resolved from the approver's identity " 154 "via the team roster.", 155 default=None, 156 ), 157 ], 158 version: Annotated[ 159 str | None, 160 Field( 161 description="The semver version string to pin to (e.g., '0.1.0'). " 162 "Must be None if unset is True.", 163 default=None, 164 ), 165 ], 166 unset: Annotated[ 167 bool, 168 Field( 169 description="If True, removes any existing version override. " 170 "Cannot be True if version is provided.", 171 default=False, 172 ), 173 ], 174 override_reason: Annotated[ 175 str | None, 176 Field( 177 description="Required when setting a version. " 178 "Explanation for the override (min 10 characters).", 179 default=None, 180 ), 181 ], 182 override_reason_reference_url: Annotated[ 183 str | None, 184 Field( 185 description="Optional URL with more context (e.g., issue link).", 186 default=None, 187 ), 188 ], 189 issue_url: Annotated[ 190 str | None, 191 Field( 192 description="URL to the GitHub issue providing context for this operation. " 193 "Must be a valid GitHub URL (https://github.com/...). Required for authorization.", 194 default=None, 195 ), 196 ], 197 ai_agent_session_url: Annotated[ 198 str | None, 199 Field( 200 description="URL to the AI agent session driving this operation, if applicable. " 201 "Provides additional auditability for AI-driven operations.", 202 default=None, 203 ), 204 ] = None, 205 force: Annotated[ 206 bool, 207 Field( 208 description="If `True`, allow overwriting an existing version pin. " 209 "Existing pins may have been set by rollouts, breaking-change migrations, " 210 "or other operators. Defaults to `False`. NOTE: `force=True` only " 211 "bypasses the existing-pin check — major-version crossings are always " 212 "blocked and cannot be overridden.", 213 default=False, 214 ), 215 ] = False, 216 config_api_root: Annotated[ 217 str | None, 218 Field( 219 description="Optional API root URL override for the Config API. " 220 "Defaults to Airbyte Cloud (https://cloud.airbyte.com/api/v1). " 221 "Use this to target local or self-hosted deployments.", 222 default=None, 223 ), 224 ] = None, 225 customer_tier_filter: Annotated[ 226 TierFilter, 227 Field( 228 description=( 229 "Required tier filter: 'TIER_0', 'TIER_1', 'TIER_2', 'UNKNOWN', or 'ALL'. " 230 "The operation will be rejected if the actual customer tier does not match. " 231 "Use 'ALL' to proceed regardless of tier (a warning is shown for sensitive tiers)." 232 ), 233 ), 234 ] = "TIER_2", 235 *, 236 ctx: Context, 237) -> VersionOverrideOperationResult: 238 """Set or clear a version override for a deployed connector. 239 240 **Admin-only operation** - Requires: 241 242 - AIRBYTE_INTERNAL_ADMIN_FLAG=airbyte.io environment variable 243 - issue_url parameter (GitHub issue URL for context) 244 - approval_comment_url (Slack approval record URL from `escalate_to_human`) 245 246 The admin user email is automatically derived from the Slack approval record, 247 resolving the approver's @airbyte.io email via the team roster. 248 249 You must specify EXACTLY ONE of `version` OR `unset=True`, but not both. 250 When setting a version, `override_reason` is required. 251 252 The `customer_tier_filter` parameter gates the operation: the call fails if 253 the actual tier of the workspace's organization does not match. Use `ALL` 254 to bypass the check (a warning is still emitted for sensitive tiers). 255 256 Business rules enforced: 257 258 - Dev versions (-dev): Only creator can unpin their own dev version override 259 - Production versions: Require strong justification mentioning customer/support/investigation 260 - Release candidates (-rc): Any admin can pin/unpin RC versions 261 262 Authentication credentials are resolved in priority order: 263 264 1. Bearer token (Authorization header or AIRBYTE_CLOUD_BEARER_TOKEN env var) 265 2. HTTP headers: X-Airbyte-Cloud-Client-Id, X-Airbyte-Cloud-Client-Secret 266 3. Environment variables: AIRBYTE_CLOUD_CLIENT_ID, AIRBYTE_CLOUD_CLIENT_SECRET 267 """ 268 resolved_workspace_id = WorkspaceAliasEnum.resolve(workspace_id) 269 assert resolved_workspace_id is not None # workspace_id is required 270 271 ws_resolution = resolve_workspace( 272 workspace_id=resolved_workspace_id, 273 allow_degraded=True, 274 ) 275 if not ws_resolution.organization_id: 276 return VersionOverrideOperationResult( 277 success=False, 278 message="Could not resolve organization for workspace.", 279 connector_id=actor_id, 280 connector_type=actor_type, 281 ) 282 283 result = set_version_override( 284 auth=resolve_cloud_auth(ctx), 285 target=VersionOverrideTarget( 286 scope="actor", 287 organization_id=ws_resolution.organization_id, 288 workspace_id=resolved_workspace_id, 289 actor_id=actor_id, 290 connector_type=actor_type, 291 ), 292 approval_comment_url=approval_comment_url, 293 version=version, 294 unset=unset, 295 override_reason=override_reason, 296 override_reason_reference_url=override_reason_reference_url, 297 issue_url=issue_url, 298 ai_agent_session_url=ai_agent_session_url, 299 customer_tier_filter=customer_tier_filter, 300 force=force, 301 config_api_root=config_api_root, 302 ) 303 assert isinstance(result, VersionOverrideOperationResult) 304 return result 305 306 307@mcp_tool( 308 destructive=True, 309 idempotent=False, 310 open_world=True, 311) 312def set_workspace_connector_version_override( 313 workspace_id: Annotated[ 314 str | WorkspaceAliasEnum, 315 Field( 316 description="The Airbyte Cloud workspace ID (UUID) or alias. " 317 "Accepts '@devin-ai-sandbox' as an alias for the Devin AI sandbox workspace." 318 ), 319 ], 320 connector_name: Annotated[ 321 str, 322 Field( 323 description="The connector name (e.g., 'source-github', 'destination-bigquery')." 324 ), 325 ], 326 connector_type: Annotated[ 327 Literal["source", "destination"], 328 "The type of connector (source or destination)", 329 ], 330 approval_comment_url: Annotated[ 331 str | None, 332 Field( 333 description="URL to the Slack approval record. Obtain this by calling the " 334 "`escalate_to_human` tool with `approval_requested=True`; the backend delivers " 335 "the approval record URL when a human clicks Approve. " 336 "Format: https://<workspace>.slack.com/archives/... " 337 "The admin email is automatically resolved from the approver's identity " 338 "via the team roster.", 339 default=None, 340 ), 341 ], 342 version: Annotated[ 343 str | None, 344 Field( 345 description="The semver version string to pin to (e.g., '0.1.0'). " 346 "Must be None if unset is True.", 347 default=None, 348 ), 349 ], 350 unset: Annotated[ 351 bool, 352 Field( 353 description="If True, removes any existing version override. " 354 "Cannot be True if version is provided.", 355 default=False, 356 ), 357 ], 358 override_reason: Annotated[ 359 str | None, 360 Field( 361 description="Required when setting a version. " 362 "Explanation for the override (min 10 characters).", 363 default=None, 364 ), 365 ], 366 override_reason_reference_url: Annotated[ 367 str | None, 368 Field( 369 description="Optional URL with more context (e.g., issue link).", 370 default=None, 371 ), 372 ], 373 issue_url: Annotated[ 374 str | None, 375 Field( 376 description="URL to the GitHub issue providing context for this operation. " 377 "Must be a valid GitHub URL (https://github.com/...). Required for authorization.", 378 default=None, 379 ), 380 ], 381 ai_agent_session_url: Annotated[ 382 str | None, 383 Field( 384 description="URL to the AI agent session driving this operation, if applicable. " 385 "Provides additional auditability for AI-driven operations.", 386 default=None, 387 ), 388 ] = None, 389 force: Annotated[ 390 bool, 391 Field( 392 description="If `True`, allow overwriting an existing version pin. " 393 "Existing pins may have been set by rollouts, breaking-change migrations, " 394 "or other operators. Defaults to `False`. NOTE: `force=True` only " 395 "bypasses the existing-pin check — major-version crossings are always " 396 "blocked and cannot be overridden.", 397 default=False, 398 ), 399 ] = False, 400 config_api_root: Annotated[ 401 str | None, 402 Field( 403 description="Optional API root URL override for the Config API. " 404 "Defaults to Airbyte Cloud (https://cloud.airbyte.com/api/v1). " 405 "Use this to target local or self-hosted deployments.", 406 default=None, 407 ), 408 ] = None, 409 customer_tier_filter: Annotated[ 410 TierFilter, 411 Field( 412 description=( 413 "Required tier filter: 'TIER_0', 'TIER_1', 'TIER_2', 'UNKNOWN', or 'ALL'. " 414 "The operation will be rejected if the actual customer tier does not match. " 415 "Use 'ALL' to proceed regardless of tier (a warning is shown for sensitive tiers)." 416 ), 417 ), 418 ] = "TIER_2", 419 *, 420 ctx: Context, 421) -> WorkspaceVersionOverrideResult: 422 """Set or clear a workspace-level version override for a connector type. 423 424 This pins ALL instances of a connector type within a workspace to a specific version. 425 For example, pinning 'source-github' at workspace level means all GitHub sources 426 in that workspace will use the pinned version. 427 428 **Admin-only operation** - Requires: 429 430 - AIRBYTE_INTERNAL_ADMIN_FLAG=airbyte.io environment variable 431 - issue_url parameter (GitHub issue URL for context) 432 - approval_comment_url (Slack approval record URL from `escalate_to_human`) 433 434 You must specify EXACTLY ONE of `version` OR `unset=True`, but not both. 435 When setting a version, `override_reason` is required. 436 437 The `customer_tier_filter` parameter gates the operation: the call fails if 438 the actual tier of the workspace's organization does not match. Use `ALL` 439 to bypass the check (a warning is still emitted for sensitive tiers). 440 """ 441 resolved_workspace_id = WorkspaceAliasEnum.resolve(workspace_id) 442 assert resolved_workspace_id is not None # workspace_id is required 443 444 ws_resolution = resolve_workspace( 445 workspace_id=resolved_workspace_id, 446 allow_degraded=True, 447 ) 448 if not ws_resolution.organization_id: 449 return WorkspaceVersionOverrideResult( 450 success=False, 451 message="Could not resolve organization for workspace.", 452 workspace_id=resolved_workspace_id, 453 connector_name=connector_name, 454 connector_type=connector_type, 455 ) 456 457 result = set_version_override( 458 auth=resolve_cloud_auth(ctx), 459 target=VersionOverrideTarget( 460 scope="workspace", 461 organization_id=ws_resolution.organization_id, 462 workspace_id=resolved_workspace_id, 463 connector_name=connector_name, 464 connector_type=connector_type, 465 ), 466 approval_comment_url=approval_comment_url, 467 version=version, 468 unset=unset, 469 override_reason=override_reason, 470 override_reason_reference_url=override_reason_reference_url, 471 issue_url=issue_url, 472 ai_agent_session_url=ai_agent_session_url, 473 customer_tier_filter=customer_tier_filter, 474 force=force, 475 config_api_root=config_api_root, 476 ) 477 assert isinstance(result, WorkspaceVersionOverrideResult) 478 return result 479 480 481@mcp_tool( 482 destructive=True, 483 idempotent=False, 484 open_world=True, 485) 486def set_organization_connector_version_override( 487 organization_id: Annotated[ 488 str, 489 Field(description="The Airbyte Cloud organization ID."), 490 ], 491 connector_name: Annotated[ 492 str, 493 Field( 494 description="The connector name (e.g., 'source-github', 'destination-bigquery')." 495 ), 496 ], 497 connector_type: Annotated[ 498 Literal["source", "destination"], 499 "The type of connector (source or destination)", 500 ], 501 approval_comment_url: Annotated[ 502 str | None, 503 Field( 504 description="URL to the Slack approval record. Obtain this by calling the " 505 "`escalate_to_human` tool with `approval_requested=True`; the backend delivers " 506 "the approval record URL when a human clicks Approve. " 507 "Format: https://<workspace>.slack.com/archives/... " 508 "The admin email is automatically resolved from the approver's identity " 509 "via the team roster.", 510 default=None, 511 ), 512 ], 513 version: Annotated[ 514 str | None, 515 Field( 516 description="The semver version string to pin to (e.g., '0.1.0'). " 517 "Must be None if unset is True.", 518 default=None, 519 ), 520 ], 521 unset: Annotated[ 522 bool, 523 Field( 524 description="If True, removes any existing version override. " 525 "Cannot be True if version is provided.", 526 default=False, 527 ), 528 ], 529 override_reason: Annotated[ 530 str | None, 531 Field( 532 description="Required when setting a version. " 533 "Explanation for the override (min 10 characters).", 534 default=None, 535 ), 536 ], 537 override_reason_reference_url: Annotated[ 538 str | None, 539 Field( 540 description="Optional URL with more context (e.g., issue link).", 541 default=None, 542 ), 543 ], 544 issue_url: Annotated[ 545 str | None, 546 Field( 547 description="URL to the GitHub issue providing context for this operation. " 548 "Must be a valid GitHub URL (https://github.com/...). Required for authorization.", 549 default=None, 550 ), 551 ], 552 ai_agent_session_url: Annotated[ 553 str | None, 554 Field( 555 description="URL to the AI agent session driving this operation, if applicable. " 556 "Provides additional auditability for AI-driven operations.", 557 default=None, 558 ), 559 ] = None, 560 force: Annotated[ 561 bool, 562 Field( 563 description="If `True`, allow overwriting an existing version pin. " 564 "Existing pins may have been set by rollouts, breaking-change migrations, " 565 "or other operators. Defaults to `False`. NOTE: `force=True` only " 566 "bypasses the existing-pin check — major-version crossings are always " 567 "blocked and cannot be overridden.", 568 default=False, 569 ), 570 ] = False, 571 config_api_root: Annotated[ 572 str | None, 573 Field( 574 description="Optional API root URL override for the Config API. " 575 "Defaults to Airbyte Cloud (https://cloud.airbyte.com/api/v1). " 576 "Use this to target local or self-hosted deployments.", 577 default=None, 578 ), 579 ] = None, 580 customer_tier_filter: Annotated[ 581 TierFilter, 582 Field( 583 description=( 584 "Required tier filter: 'TIER_0', 'TIER_1', 'TIER_2', 'UNKNOWN', or 'ALL'. " 585 "The operation will be rejected if the actual customer tier does not match. " 586 "Use 'ALL' to proceed regardless of tier (a warning is shown for sensitive tiers)." 587 ), 588 ), 589 ] = "TIER_2", 590 *, 591 ctx: Context, 592) -> OrganizationVersionOverrideResult: 593 """Set or clear an organization-level version override for a connector type. 594 595 This pins ALL instances of a connector type across an entire organization to a 596 specific version. For example, pinning 'source-github' at organization level means 597 all GitHub sources in all workspaces within that organization will use the pinned version. 598 599 **Admin-only operation** - Requires: 600 601 - AIRBYTE_INTERNAL_ADMIN_FLAG=airbyte.io environment variable 602 - issue_url parameter (GitHub issue URL for context) 603 - approval_comment_url (Slack approval record URL from `escalate_to_human`) 604 605 You must specify EXACTLY ONE of `version` OR `unset=True`, but not both. 606 When setting a version, `override_reason` is required. 607 608 The `customer_tier_filter` parameter gates the operation: the call fails if 609 the actual tier of the organization does not match. Use `ALL` to bypass 610 the check (a warning is still emitted for sensitive tiers). 611 """ 612 result = set_version_override( 613 auth=resolve_cloud_auth(ctx), 614 target=VersionOverrideTarget( 615 scope="organization", 616 organization_id=organization_id, 617 connector_name=connector_name, 618 connector_type=connector_type, 619 ), 620 approval_comment_url=approval_comment_url, 621 version=version, 622 unset=unset, 623 override_reason=override_reason, 624 override_reason_reference_url=override_reason_reference_url, 625 issue_url=issue_url, 626 ai_agent_session_url=ai_agent_session_url, 627 customer_tier_filter=customer_tier_filter, 628 force=force, 629 config_api_root=config_api_root, 630 ) 631 assert isinstance(result, OrganizationVersionOverrideResult) 632 return result 633 634 635@dataclass(frozen=True) 636class _ResolvedCloudAuth: 637 """Resolved authentication for Airbyte Cloud API calls. 638 639 Either bearer_token OR (client_id AND client_secret) will be set, not both. 640 """ 641 642 bearer_token: str | None = None 643 client_id: str | None = None 644 client_secret: str | None = None 645 646 647def _resolve_cloud_auth(ctx: Context) -> _ResolvedCloudAuth: 648 """Resolve authentication credentials for Airbyte Cloud API. 649 650 Credentials are resolved in priority order: 651 1. Bearer token (Authorization header or AIRBYTE_CLOUD_BEARER_TOKEN env var) 652 2. Client credentials (X-Airbyte-Cloud-Client-Id/Secret headers or env vars) 653 654 Args: 655 ctx: FastMCP Context object from the current tool invocation. 656 657 Returns: 658 _ResolvedCloudAuth with either bearer_token or client credentials set. 659 660 Raises: 661 CloudAuthError: If credentials cannot be resolved from headers or env vars. 662 """ 663 # Try bearer token first (preferred, but not required) 664 bearer_token = get_mcp_config(ctx, ServerConfigKey.BEARER_TOKEN) 665 if bearer_token: 666 return _ResolvedCloudAuth(bearer_token=bearer_token) 667 668 # Fall back to client credentials 669 try: 670 client_id = get_mcp_config(ctx, ServerConfigKey.CLIENT_ID) 671 client_secret = get_mcp_config(ctx, ServerConfigKey.CLIENT_SECRET) 672 return _ResolvedCloudAuth( 673 client_id=client_id, 674 client_secret=client_secret, 675 ) 676 except ValueError as e: 677 raise CloudAuthError( 678 f"Failed to resolve credentials. Ensure credentials are provided " 679 f"via Authorization header (Bearer token), " 680 f"HTTP headers (X-Airbyte-Cloud-Client-Id, X-Airbyte-Cloud-Client-Secret), " 681 f"or environment variables. Error: {e}" 682 ) from e 683 684 685@mcp_tool( 686 destructive=True, 687 idempotent=False, 688 open_world=True, 689) 690def start_connector_rollout( 691 docker_repository: Annotated[ 692 str, 693 Field(description="The docker repository (e.g., 'airbyte/source-pokeapi')"), 694 ], 695 docker_image_tag: Annotated[ 696 str, 697 Field(description="The docker image tag (e.g., '0.3.48-rc.1')"), 698 ], 699 actor_definition_id: Annotated[ 700 str, 701 Field(description="The actor definition ID (UUID)"), 702 ], 703 approval_comment_url: Annotated[ 704 str | None, 705 Field( 706 description="URL to the Slack approval record. Obtain this by calling the " 707 "`escalate_to_human` tool with `approval_requested=True`; the backend delivers " 708 "the approval record URL when a human clicks Approve. " 709 "Format: https://<workspace>.slack.com/archives/... " 710 "The admin email is automatically resolved from the approver's identity " 711 "via the team roster.", 712 default=None, 713 ), 714 ], 715 admin_user_email_override: Annotated[ 716 str | None, 717 Field( 718 description="Direct admin email override for webapp-initiated actions. " 719 "When the Ops Webapp env var is set, this bypasses the approval URL " 720 "requirement. Ignored in agent/cron environments.", 721 default=None, 722 ), 723 ], 724 rollout_strategy: Annotated[ 725 Literal["manual", "automated", "overridden"], 726 Field( 727 description="The rollout strategy: " 728 "'manual' for manual control of rollout progression, " 729 "'automated' for automatic progression based on metrics, " 730 "'overridden' for special cases where normal rules are bypassed.", 731 default="manual", 732 ), 733 ], 734 initial_rollout_pct: Annotated[ 735 int | None, 736 Field( 737 description="Initial/step percentage for rollout progression (0-100). " 738 "For automated rollouts, this is the percentage increment per step. " 739 "For example, 25 means the rollout will advance by 25% each step. " 740 "Default is 25% if not specified.", 741 default=None, 742 ), 743 ], 744 final_target_rollout_pct: Annotated[ 745 int | None, 746 Field( 747 description="Maximum percentage of actors to pin (0-100). " 748 "The rollout will not exceed this percentage. " 749 "For example, 50 means at most 50% of actors will be pinned to the RC. " 750 "Default is 50% if not specified.", 751 default=None, 752 ), 753 ], 754 customer_tier: Annotated[ 755 Literal["TIER_0", "TIER_1", "TIER_2", "ALL"] | None, 756 Field( 757 description="The customer tier to target for this rollout. " 758 "Each tier represents a different group of customers: " 759 "'TIER_0' for the highest-priority customers, " 760 "'TIER_1' for mid-tier customers, " 761 "'TIER_2' for the broadest customer group (default if not specified), " 762 "'ALL' to target all customer tiers. " 763 "When not specified, the platform defaults to TIER_2 only.", 764 default=None, 765 ), 766 ], 767 *, 768 ctx: Context, 769) -> ConnectorRolloutStartResult: 770 """Start or configure a connector rollout workflow. 771 772 This tool configures and starts a connector rollout workflow. It can be called 773 multiple times while the rollout is in INITIALIZED state to update the configuration 774 (strategy, percentages). Once the Temporal workflow starts and the state transitions 775 to WORKFLOW_STARTED, the configuration is locked and cannot be changed. 776 777 **Behavior:** 778 - If rollout is INITIALIZED: Updates configuration and starts the workflow 779 - If rollout is already started: Returns an error (configuration is locked) 780 781 **Configuration Parameters:** 782 - rollout_strategy: 'manual' (default), 'automated', or 'overridden' 783 - initial_rollout_pct: Step size for progression (default: 25%) 784 - final_target_rollout_pct: Maximum percentage to pin (default: 50%) 785 - customer_tier: Customer tier to target - 'TIER_0', 'TIER_1', 'TIER_2', or 'ALL' (default: TIER_2) 786 787 **Admin-only operation** - Requires: 788 - AIRBYTE_INTERNAL_ADMIN_FLAG=airbyte.io environment variable 789 - `approval_comment_url` (Slack approval record URL from `escalate_to_human`), 790 OR `admin_user_email_override` when running inside the Ops Webapp. 791 """ 792 # Validate admin access (check env var flag) 793 try: 794 require_internal_admin_flag_only() 795 except CloudAuthError as e: 796 return ConnectorRolloutStartResult( 797 success=False, 798 message=f"Admin authentication failed: {e}", 799 docker_repository=docker_repository, 800 docker_image_tag=docker_image_tag, 801 actor_definition_id=actor_definition_id, 802 ) 803 804 # Resolve admin email: webapp bypass or external approval URL 805 approval = check_approval_status( 806 approval_comment_url=approval_comment_url, 807 user_email=admin_user_email_override, 808 ) 809 if approval.status != ApprovalStatus.APPROVED: 810 return ConnectorRolloutStartResult( 811 success=False, 812 message=approval.reason or "Approval check failed", 813 docker_repository=docker_repository, 814 docker_image_tag=docker_image_tag, 815 actor_definition_id=actor_definition_id, 816 ) 817 admin_user_email = approval.admin_email 818 819 # Resolve auth credentials 820 try: 821 auth = _resolve_cloud_auth(ctx) 822 except CloudAuthError as e: 823 return ConnectorRolloutStartResult( 824 success=False, 825 message=f"Failed to resolve credentials: {e}", 826 docker_repository=docker_repository, 827 docker_image_tag=docker_image_tag, 828 actor_definition_id=actor_definition_id, 829 ) 830 831 # Get user ID from admin email 832 try: 833 user_id = api_client.get_user_id_by_email( 834 email=admin_user_email, 835 config_api_root=constants.CLOUD_CONFIG_API_ROOT, 836 client_id=auth.client_id, 837 client_secret=auth.client_secret, 838 bearer_token=auth.bearer_token, 839 ) 840 except PyAirbyteInputError as e: 841 return ConnectorRolloutStartResult( 842 success=False, 843 message=f"Failed to get user ID for admin email '{admin_user_email}': {e}", 844 docker_repository=docker_repository, 845 docker_image_tag=docker_image_tag, 846 actor_definition_id=actor_definition_id, 847 ) 848 849 # Call the API to start the rollout 850 try: 851 api_client.start_connector_rollout( 852 docker_repository=docker_repository, 853 docker_image_tag=docker_image_tag, 854 actor_definition_id=actor_definition_id, 855 updated_by=user_id, 856 rollout_strategy=rollout_strategy, 857 config_api_root=constants.CLOUD_CONFIG_API_ROOT, 858 initial_rollout_pct=initial_rollout_pct, 859 final_target_rollout_pct=final_target_rollout_pct, 860 customer_tier=customer_tier, 861 client_id=auth.client_id, 862 client_secret=auth.client_secret, 863 bearer_token=auth.bearer_token, 864 ) 865 866 # Build message with configuration details 867 config_details = [] 868 if initial_rollout_pct is not None: 869 config_details.append(f"initial_rollout_pct={initial_rollout_pct}%") 870 if final_target_rollout_pct is not None: 871 config_details.append( 872 f"final_target_rollout_pct={final_target_rollout_pct}%" 873 ) 874 if customer_tier is not None: 875 config_details.append(f"customer_tier={customer_tier}") 876 config_str = ( 877 f" Configuration: {', '.join(config_details)}." if config_details else "" 878 ) 879 880 return ConnectorRolloutStartResult( 881 success=True, 882 message=f"Successfully started rollout workflow for " 883 f"{docker_repository}:{docker_image_tag}. " 884 f"The rollout state has transitioned from INITIALIZED to WORKFLOW_STARTED." 885 f"{config_str}", 886 docker_repository=docker_repository, 887 docker_image_tag=docker_image_tag, 888 actor_definition_id=actor_definition_id, 889 rollout_strategy=rollout_strategy, 890 ) 891 892 except PyAirbyteInputError as e: 893 return ConnectorRolloutStartResult( 894 success=False, 895 message=str(e), 896 docker_repository=docker_repository, 897 docker_image_tag=docker_image_tag, 898 actor_definition_id=actor_definition_id, 899 ) 900 901 902@mcp_tool( 903 destructive=True, 904 idempotent=False, 905 open_world=True, 906) 907def progress_connector_rollout( 908 docker_repository: Annotated[ 909 str, 910 Field(description="The docker repository (e.g., 'airbyte/source-pokeapi')"), 911 ], 912 docker_image_tag: Annotated[ 913 str, 914 Field(description="The docker image tag (e.g., '0.3.48-rc.1')"), 915 ], 916 actor_definition_id: Annotated[ 917 str, 918 Field(description="The actor definition ID (UUID)"), 919 ], 920 rollout_id: Annotated[ 921 str, 922 Field( 923 description="The rollout ID (UUID). Can be found from query_prod_connector_rollouts." 924 ), 925 ], 926 approval_comment_url: Annotated[ 927 str | None, 928 Field( 929 description="URL to the Slack approval record. Obtain this by calling the " 930 "`escalate_to_human` tool with `approval_requested=True`; the backend delivers " 931 "the approval record URL when a human clicks Approve. " 932 "Format: https://<workspace>.slack.com/archives/... " 933 "The admin email is automatically resolved from the approver's identity " 934 "via the team roster.", 935 default=None, 936 ), 937 ], 938 admin_user_email_override: Annotated[ 939 str | None, 940 Field( 941 description="Direct admin email override for webapp-initiated actions. " 942 "When the Ops Webapp env var is set, this bypasses the approval URL " 943 "requirement. Ignored in agent/cron environments.", 944 default=None, 945 ), 946 ], 947 target_percentage: Annotated[ 948 int | None, 949 Field( 950 description="Target percentage of actors to pin to the RC (1-100). " 951 "Either target_percentage or actor_ids must be provided.", 952 default=None, 953 ), 954 ] = None, 955 actor_ids: Annotated[ 956 list[str] | None, 957 Field( 958 description="Specific actor IDs to pin to the RC. " 959 "Either target_percentage or actor_ids must be provided.", 960 default=None, 961 ), 962 ] = None, 963 *, 964 ctx: Context, 965) -> ConnectorRolloutProgressResult: 966 """Progress a connector rollout by pinning actors to the RC version. 967 968 This tool progresses a connector rollout by either: 969 - Setting a target percentage of actors to pin to the RC version 970 - Specifying specific actor IDs to pin 971 972 **Admin-only operation** - Requires: 973 - AIRBYTE_INTERNAL_ADMIN_FLAG=airbyte.io environment variable 974 - `approval_comment_url` (Slack approval record URL from `escalate_to_human`), 975 OR `admin_user_email_override` when running inside the Ops Webapp. 976 """ 977 # Validate admin access (check env var flag) 978 try: 979 require_internal_admin_flag_only() 980 except CloudAuthError as e: 981 return ConnectorRolloutProgressResult( 982 success=False, 983 message=f"Admin authentication failed: {e}", 984 rollout_id=rollout_id, 985 docker_repository=docker_repository, 986 docker_image_tag=docker_image_tag, 987 ) 988 989 # Validate that at least one of target_percentage or actor_ids is provided 990 if target_percentage is None and actor_ids is None: 991 return ConnectorRolloutProgressResult( 992 success=False, 993 message="Either target_percentage or actor_ids must be provided", 994 rollout_id=rollout_id, 995 docker_repository=docker_repository, 996 docker_image_tag=docker_image_tag, 997 ) 998 999 # Resolve admin email: webapp bypass or external approval URL 1000 approval = check_approval_status( 1001 approval_comment_url=approval_comment_url, 1002 user_email=admin_user_email_override, 1003 ) 1004 if approval.status != ApprovalStatus.APPROVED: 1005 return ConnectorRolloutProgressResult( 1006 success=False, 1007 message=approval.reason or "Approval check failed", 1008 rollout_id=rollout_id, 1009 docker_repository=docker_repository, 1010 docker_image_tag=docker_image_tag, 1011 ) 1012 admin_user_email = approval.admin_email 1013 1014 # Resolve auth credentials 1015 try: 1016 auth = _resolve_cloud_auth(ctx) 1017 except CloudAuthError as e: 1018 return ConnectorRolloutProgressResult( 1019 success=False, 1020 message=f"Failed to resolve credentials: {e}", 1021 rollout_id=rollout_id, 1022 docker_repository=docker_repository, 1023 docker_image_tag=docker_image_tag, 1024 ) 1025 1026 # Get user ID from admin email 1027 try: 1028 user_id = api_client.get_user_id_by_email( 1029 email=admin_user_email, 1030 config_api_root=constants.CLOUD_CONFIG_API_ROOT, 1031 client_id=auth.client_id, 1032 client_secret=auth.client_secret, 1033 bearer_token=auth.bearer_token, 1034 ) 1035 except PyAirbyteInputError as e: 1036 return ConnectorRolloutProgressResult( 1037 success=False, 1038 message=f"Failed to get user ID for admin email '{admin_user_email}': {e}", 1039 rollout_id=rollout_id, 1040 docker_repository=docker_repository, 1041 docker_image_tag=docker_image_tag, 1042 ) 1043 1044 # Guard: a percentage-based progression toward a tier with zero eligible 1045 # actors will throw `ConnectorRolloutNotEnoughActorsProblem` server-side 1046 # and silently wedge the rollout at `workflow_started` (the throw happens 1047 # before the `IN_PROGRESS` write and the `@Transactional` rolls back). 1048 # Detect this up front and return an actionable error instead. Skipped when 1049 # pinning specific `actor_ids` (the caller chose the actors explicitly). 1050 if target_percentage is not None and target_percentage > 0 and not actor_ids: 1051 try: 1052 sync_info = api_client.get_actor_sync_info( 1053 rollout_id=rollout_id, 1054 config_api_root=constants.CLOUD_CONFIG_API_ROOT, 1055 client_id=auth.client_id, 1056 client_secret=auth.client_secret, 1057 bearer_token=auth.bearer_token, 1058 ) 1059 except (PyAirbyteInputError, requests.exceptions.RequestException): 1060 # If eligibility can't be fetched (bad input or a network-level 1061 # failure), fall through and let the progress call surface the 1062 # underlying error rather than crashing the pre-check. 1063 sync_info = None 1064 if sync_info is not None and count_eligible_or_pinned_actors(sync_info) == 0: 1065 return ConnectorRolloutProgressResult( 1066 success=False, 1067 message=( 1068 "Zero eligible actors: this rollout's tier has no actors to " 1069 "pin, so progressing to " 1070 f"target_percentage={target_percentage}% would throw " 1071 "ConnectorRolloutNotEnoughActorsProblem server-side and " 1072 "silently wedge the rollout at 'workflow_started'. " 1073 "TIER_1/TIER_0 are named strategic accounts; a connector " 1074 "with no customers in this tier can never pin anyone here. " 1075 "Do not progress this tier. If the last non-empty tier is " 1076 "healthy at 100%, cancel this empty rollout " 1077 "(retain_pins_on_cancellation=true) and finalize the healthy " 1078 "rollout as 'succeeded' to promote to GA. See the " 1079 "'Rollout stuck at workflow_started with zero eligible " 1080 "actors' troubleshooting guide in docs/progressive-rollouts.md." 1081 ), 1082 rollout_id=rollout_id, 1083 docker_repository=docker_repository, 1084 docker_image_tag=docker_image_tag, 1085 ) 1086 1087 # Call the API to progress the rollout 1088 try: 1089 api_client.progress_connector_rollout( 1090 docker_repository=docker_repository, 1091 docker_image_tag=docker_image_tag, 1092 actor_definition_id=actor_definition_id, 1093 rollout_id=rollout_id, 1094 updated_by=user_id, 1095 config_api_root=constants.CLOUD_CONFIG_API_ROOT, 1096 target_percentage=target_percentage, 1097 actor_ids=actor_ids, 1098 client_id=auth.client_id, 1099 client_secret=auth.client_secret, 1100 bearer_token=auth.bearer_token, 1101 ) 1102 1103 progress_msg = ( 1104 f"target_percentage={target_percentage}%" 1105 if target_percentage 1106 else f"{len(actor_ids) if actor_ids else 0} specific actors" 1107 ) 1108 return ConnectorRolloutProgressResult( 1109 success=True, 1110 message=f"Successfully progressed rollout for " 1111 f"{docker_repository}:{docker_image_tag} to {progress_msg}.", 1112 rollout_id=rollout_id, 1113 docker_repository=docker_repository, 1114 docker_image_tag=docker_image_tag, 1115 target_percentage=target_percentage, 1116 ) 1117 1118 except PyAirbyteInputError as e: 1119 return ConnectorRolloutProgressResult( 1120 success=False, 1121 message=str(e), 1122 rollout_id=rollout_id, 1123 docker_repository=docker_repository, 1124 docker_image_tag=docker_image_tag, 1125 ) 1126 1127 1128@mcp_tool( 1129 destructive=True, 1130 idempotent=False, 1131 open_world=True, 1132) 1133def finalize_connector_rollout( 1134 docker_repository: Annotated[ 1135 str, 1136 Field( 1137 description="The docker repository (e.g., 'airbyte/source-youtube-analytics')" 1138 ), 1139 ], 1140 docker_image_tag: Annotated[ 1141 str, 1142 Field(description="The docker image tag (e.g., '1.2.0-rc.2')"), 1143 ], 1144 actor_definition_id: Annotated[ 1145 str, 1146 Field(description="The actor definition ID (UUID)"), 1147 ], 1148 rollout_id: Annotated[ 1149 str, 1150 Field( 1151 description="The rollout ID (UUID). Can be found in the 'pin_origin' field " 1152 "of rollout data from query_prod_actors_by_pinned_connector_version." 1153 ), 1154 ], 1155 state: Annotated[ 1156 Literal["succeeded", "failed_rolled_back", "canceled"], 1157 Field( 1158 description="The final state for the rollout: " 1159 "'succeeded' promotes the RC to GA (default version for all users), " 1160 "'failed_rolled_back' rolls back the RC, " 1161 "'canceled' cancels the rollout without promotion or rollback." 1162 ), 1163 ], 1164 approval_comment_url: Annotated[ 1165 str | None, 1166 Field( 1167 description="URL to the Slack approval record. Obtain this by calling the " 1168 "`escalate_to_human` tool with `approval_requested=True`; the backend delivers " 1169 "the approval record URL when a human clicks Approve. " 1170 "Format: https://<workspace>.slack.com/archives/... " 1171 "The admin email is automatically resolved from the approver's identity " 1172 "via the team roster.", 1173 default=None, 1174 ), 1175 ], 1176 admin_user_email_override: Annotated[ 1177 str | None, 1178 Field( 1179 description="Direct admin email override for webapp-initiated actions. " 1180 "When the Ops Webapp env var is set, this bypasses the approval URL " 1181 "requirement. Ignored in agent/cron environments.", 1182 default=None, 1183 ), 1184 ], 1185 error_msg: Annotated[ 1186 str | None, 1187 Field( 1188 description="Optional error message for failed/canceled states.", 1189 default=None, 1190 ), 1191 ] = None, 1192 failed_reason: Annotated[ 1193 str | None, 1194 Field( 1195 description="Optional failure reason for failed/canceled states.", 1196 default=None, 1197 ), 1198 ] = None, 1199 retain_pins_on_cancellation: Annotated[ 1200 bool | None, 1201 Field( 1202 description="If True, retain version pins when canceling. " 1203 "Only applicable when state is 'canceled'.", 1204 default=None, 1205 ), 1206 ] = None, 1207 *, 1208 ctx: Context, 1209) -> ConnectorRolloutFinalizeResult: 1210 """Finalize a connector rollout by promoting, rolling back, or canceling. 1211 1212 This tool allows admins to finalize connector rollouts that are in progress. 1213 Use this after monitoring a rollout and determining it is ready for finalization. 1214 1215 **IMPORTANT: Finalization is asynchronous.** This tool sends a finalization 1216 request to the platform API, which transitions the rollout to `finalizing` 1217 state and triggers a Temporal workflow. The actual promotion (PR creation, 1218 connector publish, registry update) or rollback (GCS cleanup, registry 1219 recompile) happens asynchronously via the `finalize_rollout.yml` GitHub 1220 Actions workflow. A successful response from this tool means the request 1221 was accepted — NOT that the promotion/rollback is complete. 1222 1223 After calling this tool, you MUST verify: 1224 1. The `finalize_rollout.yml` workflow ran successfully in GitHub Actions 1225 2. For promotions: a merged PR exists (e.g., `chore: finalize promote for <connector>`) 1226 3. The rollout state transitioned to its terminal state (`succeeded`, 1227 `failed_rolled_back`, or `canceled`) via `query_prod_connector_rollouts` 1228 1229 **Admin-only operation** - Requires: 1230 - AIRBYTE_INTERNAL_ADMIN_FLAG=airbyte.io environment variable 1231 - `approval_comment_url` (Slack approval record URL from `escalate_to_human`), 1232 OR `admin_user_email_override` when running inside the Ops Webapp. 1233 """ 1234 # Validate admin access (check env var flag) 1235 try: 1236 require_internal_admin_flag_only() 1237 except CloudAuthError as e: 1238 return ConnectorRolloutFinalizeResult( 1239 success=False, 1240 message=f"Admin authentication failed: {e}", 1241 rollout_id=rollout_id, 1242 docker_repository=docker_repository, 1243 docker_image_tag=docker_image_tag, 1244 ) 1245 1246 # Resolve admin email: webapp bypass or external approval URL 1247 approval = check_approval_status( 1248 approval_comment_url=approval_comment_url, 1249 user_email=admin_user_email_override, 1250 ) 1251 if approval.status != ApprovalStatus.APPROVED: 1252 return ConnectorRolloutFinalizeResult( 1253 success=False, 1254 message=approval.reason or "Approval check failed", 1255 rollout_id=rollout_id, 1256 docker_repository=docker_repository, 1257 docker_image_tag=docker_image_tag, 1258 ) 1259 admin_user_email = approval.admin_email 1260 1261 # Resolve auth credentials 1262 try: 1263 auth = _resolve_cloud_auth(ctx) 1264 except CloudAuthError as e: 1265 return ConnectorRolloutFinalizeResult( 1266 success=False, 1267 message=f"Failed to resolve credentials: {e}", 1268 rollout_id=rollout_id, 1269 docker_repository=docker_repository, 1270 docker_image_tag=docker_image_tag, 1271 ) 1272 1273 # Get user ID from admin email 1274 try: 1275 user_id = api_client.get_user_id_by_email( 1276 email=admin_user_email, 1277 config_api_root=constants.CLOUD_CONFIG_API_ROOT, 1278 client_id=auth.client_id, 1279 client_secret=auth.client_secret, 1280 bearer_token=auth.bearer_token, 1281 ) 1282 except PyAirbyteInputError as e: 1283 return ConnectorRolloutFinalizeResult( 1284 success=False, 1285 message=f"Failed to get user ID for admin email '{admin_user_email}': {e}", 1286 rollout_id=rollout_id, 1287 docker_repository=docker_repository, 1288 docker_image_tag=docker_image_tag, 1289 ) 1290 1291 # Call the API to finalize the rollout 1292 try: 1293 api_client.finalize_connector_rollout( 1294 docker_repository=docker_repository, 1295 docker_image_tag=docker_image_tag, 1296 actor_definition_id=actor_definition_id, 1297 rollout_id=rollout_id, 1298 updated_by=user_id, 1299 state=state, 1300 config_api_root=constants.CLOUD_CONFIG_API_ROOT, 1301 client_id=auth.client_id, 1302 client_secret=auth.client_secret, 1303 bearer_token=auth.bearer_token, 1304 error_msg=error_msg, 1305 failed_reason=failed_reason, 1306 retain_pins_on_cancellation=retain_pins_on_cancellation, 1307 ) 1308 1309 state_descriptions = { 1310 "succeeded": ( 1311 "GA promotion has been initiated (state: finalizing). " 1312 "The actual promotion (PR creation, publish, registry update) " 1313 "happens asynchronously via the finalize_rollout.yml GitHub Actions workflow. " 1314 "You MUST verify the workflow completes successfully and the rollout " 1315 "transitions to 'succeeded' state before reporting completion. " 1316 "Check: (1) GitHub Actions for a 'Finalize Progressive Rollout' workflow run, " 1317 "(2) a merged promotion PR, and " 1318 "(3) query_prod_connector_rollouts to confirm state is 'succeeded'." 1319 ), 1320 "failed_rolled_back": ( 1321 "rollback has been initiated (state: finalizing). " 1322 "The rollback happens asynchronously via the finalize_rollout.yml workflow. " 1323 "Verify the workflow completes and the rollout transitions to " 1324 "'failed_rolled_back' state." 1325 ), 1326 "canceled": "canceled", 1327 } 1328 state_desc = state_descriptions.get(state, state) 1329 1330 return ConnectorRolloutFinalizeResult( 1331 success=True, 1332 message=f"Finalization request accepted for {docker_repository}:{docker_image_tag}: " 1333 f"{state_desc}", 1334 rollout_id=rollout_id, 1335 docker_repository=docker_repository, 1336 docker_image_tag=docker_image_tag, 1337 state=state, 1338 ) 1339 1340 except PyAirbyteInputError as e: 1341 return ConnectorRolloutFinalizeResult( 1342 success=False, 1343 message=str(e), 1344 rollout_id=rollout_id, 1345 docker_repository=docker_repository, 1346 docker_image_tag=docker_image_tag, 1347 ) 1348 1349 1350class RolloutActorSelectionInfo(BaseModel): 1351 """Actor selection info for a connector rollout.""" 1352 1353 num_actors: int = Field(description="Total actors using this connector") 1354 num_pinned_to_connector_rollout: int = Field( 1355 description="Actors specifically pinned to this rollout" 1356 ) 1357 num_actors_eligible_or_already_pinned: int = Field( 1358 description="Actors eligible for pinning or already pinned" 1359 ) 1360 1361 1362class RolloutActorSyncStats(BaseModel): 1363 """Per-actor sync stats for a rollout (only syncs using the RC version).""" 1364 1365 actor_id: str = Field(description="Actor UUID") 1366 num_connections: int = Field(description="Number of connections using this actor") 1367 num_succeeded: int = Field( 1368 description="Number of successful syncs using the RC version" 1369 ) 1370 num_failed: int = Field(description="Number of failed syncs using the RC version") 1371 1372 1373class RolloutMonitoringResult(BaseModel): 1374 """Complete monitoring result for a rollout from the platform API. 1375 1376 This uses the platform API's /get_actor_sync_info endpoint which filters 1377 sync stats to only include syncs that actually used the RC version 1378 associated with the rollout. 1379 """ 1380 1381 rollout_id: str = Field(description="Rollout UUID") 1382 actor_selection_info: RolloutActorSelectionInfo = Field( 1383 description="Actor selection info for the rollout" 1384 ) 1385 actor_sync_stats: list[RolloutActorSyncStats] = Field( 1386 description="Per-actor sync stats for actors pinned to the rollout" 1387 ) 1388 1389 1390@mcp_tool( 1391 read_only=True, 1392 idempotent=True, 1393) 1394def query_prod_rollout_monitoring_stats( 1395 rollout_id: Annotated[ 1396 str, 1397 Field(description="Rollout UUID to get monitoring stats for"), 1398 ], 1399 *, 1400 ctx: Context, 1401) -> RolloutMonitoringResult: 1402 """Get monitoring stats for a connector rollout. 1403 1404 Returns actor selection info and per-actor sync stats for actors 1405 participating in the rollout. This uses the platform API's 1406 /get_actor_sync_info endpoint which filters sync stats to only include 1407 syncs that actually used the RC version associated with the rollout. 1408 1409 This is more accurate than SQL-based approaches which count all syncs 1410 regardless of which connector version was used. 1411 """ 1412 auth = _resolve_cloud_auth(ctx) 1413 1414 response = api_client.get_actor_sync_info( 1415 rollout_id=rollout_id, 1416 config_api_root=constants.CLOUD_CONFIG_API_ROOT, 1417 client_id=auth.client_id, 1418 client_secret=auth.client_secret, 1419 bearer_token=auth.bearer_token, 1420 ) 1421 1422 data = response.get("data", {}) 1423 actor_selection_info_data = data.get("actor_selection_info", {}) 1424 syncs_data = data.get("syncs", {}) 1425 1426 actor_selection_info = RolloutActorSelectionInfo( 1427 num_actors=actor_selection_info_data.get("num_actors", 0), 1428 num_pinned_to_connector_rollout=actor_selection_info_data.get( 1429 "num_pinned_to_connector_rollout", 0 1430 ), 1431 num_actors_eligible_or_already_pinned=actor_selection_info_data.get( 1432 "num_actors_eligible_or_already_pinned", 0 1433 ), 1434 ) 1435 1436 actor_sync_stats = [ 1437 RolloutActorSyncStats( 1438 actor_id=actor_id, 1439 num_connections=sync_info.get("num_connections", 0), 1440 num_succeeded=sync_info.get("num_succeeded", 0), 1441 num_failed=sync_info.get("num_failed", 0), 1442 ) 1443 for actor_id, sync_info in syncs_data.items() 1444 ] 1445 1446 return RolloutMonitoringResult( 1447 rollout_id=rollout_id, 1448 actor_selection_info=actor_selection_info, 1449 actor_sync_stats=actor_sync_stats, 1450 ) 1451 1452 1453class ConnectorRepo(StrEnum): 1454 """Repository where connector code is located.""" 1455 1456 AIRBYTE = "airbyte" 1457 AIRBYTE_ENTERPRISE = "airbyte-enterprise" 1458 1459 1460DEFAULT_REPO_OWNER = "airbytehq" 1461 1462DEFAULT_REPO_NAME = ConnectorRepo.AIRBYTE 1463 1464DEFAULT_BRANCH = "master" 1465 1466PRERELEASE_WORKFLOW_FILE = "publish-connectors-prerelease-command.yml" 1467 1468CONNECTOR_PATH_PREFIX = "airbyte-integrations/connectors" 1469 1470ENTERPRISE_REPO_NAME = ConnectorRepo.AIRBYTE_ENTERPRISE 1471 1472ENTERPRISE_DEFAULT_BRANCH = "main" 1473 1474ENTERPRISE_PRERELEASE_WORKFLOW_FILE = "publish_enterprise_connectors.yml" 1475 1476PRERELEASE_TOKEN_ENV_VARS = [ 1477 "GITHUB_CONNECTOR_PUBLISHING_PAT", 1478 "GITHUB_CI_WORKFLOW_TRIGGER_PAT", 1479 "GITHUB_TOKEN", 1480] 1481 1482PRERELEASE_TAG_PREFIX = "preview" 1483 1484PRERELEASE_SHA_LENGTH = 7 1485 1486 1487def compute_prerelease_docker_image_tag(base_version: str, sha: str) -> str: 1488 """Compute the pre-release docker image tag. 1489 1490 This is the SINGLE SOURCE OF TRUTH for pre-release version format. 1491 All other code should receive this value as a parameter, not recompute it. 1492 1493 The format is: {base_version}-preview.{short_sha} 1494 1495 Where: 1496 - base_version: The base version from metadata.yaml (e.g., "1.2.3"), 1497 which may already contain a pre-release suffix (e.g., "2.23.16-rc.1"). 1498 Any existing pre-release suffix is stripped before applying the preview tag. 1499 - short_sha: The first 7 characters of the git commit SHA 1500 1501 Examples: 1502 >>> compute_prerelease_docker_image_tag("1.2.3", "abcdef1234567890") 1503 '1.2.3-preview.abcdef1' 1504 >>> compute_prerelease_docker_image_tag("0.1.0", "1234567") 1505 '0.1.0-preview.1234567' 1506 >>> compute_prerelease_docker_image_tag("2.23.16-rc.1", "abcdef1234567890") 1507 '2.23.16-preview.abcdef1' 1508 1509 Args: 1510 base_version: The base version from metadata.yaml (e.g., "1.2.3" or "2.23.16-rc.1") 1511 sha: The full git commit SHA (or at least 7 characters) 1512 1513 Returns: 1514 Pre-release version tag (e.g., "1.2.3-preview.abcde12") 1515 """ 1516 short_sha = sha[:PRERELEASE_SHA_LENGTH] 1517 clean_version = strip_prerelease_suffix(base_version) 1518 return f"{clean_version}-{PRERELEASE_TAG_PREFIX}.{short_sha}" 1519 1520 1521class PrereleaseWorkflowResult(BaseModel): 1522 """Response model for publish_connector_to_airbyte_registry MCP tool.""" 1523 1524 success: bool 1525 message: str 1526 workflow_url: str | None = None 1527 connector_name: str | None = None 1528 pr_number: int | None = None 1529 docker_image: str | None = None 1530 docker_image_tag: str | None = None 1531 1532 1533def _get_connector_metadata( 1534 owner: str, 1535 repo: str, 1536 connector_name: str, 1537 ref: str, 1538 token: str, 1539) -> dict | None: 1540 """Fetch and parse connector metadata.yaml from the repository. 1541 1542 Args: 1543 owner: Repository owner (e.g., "airbytehq") 1544 repo: Repository name (e.g., "airbyte") 1545 connector_name: Connector name (e.g., "source-github") 1546 ref: Git ref to fetch from (branch name or SHA) 1547 token: GitHub API token 1548 1549 Returns: 1550 Parsed metadata dictionary, or None if not found. 1551 """ 1552 metadata_path = f"{CONNECTOR_PATH_PREFIX}/{connector_name}/metadata.yaml" 1553 url = f"{GITHUB_API_BASE}/repos/{owner}/{repo}/contents/{metadata_path}" 1554 headers = { 1555 "Authorization": f"Bearer {token}", 1556 "Accept": "application/vnd.github+json", 1557 "X-GitHub-Api-Version": "2022-11-28", 1558 } 1559 params = {"ref": ref} 1560 1561 response = requests.get(url, headers=headers, params=params, timeout=30) 1562 1563 # Guard: Return None if metadata file not found 1564 if response.status_code == 404: 1565 return None 1566 1567 response.raise_for_status() 1568 1569 content_data = response.json() 1570 1571 # Guard: Return None if content is not base64 encoded 1572 if content_data.get("encoding") != "base64": 1573 return None 1574 1575 content = base64.b64decode(content_data["content"]).decode("utf-8") 1576 return yaml.safe_load(content) 1577 1578 1579@mcp_tool( 1580 read_only=False, 1581 destructive=False, 1582 idempotent=False, 1583 open_world=True, 1584) 1585def publish_connector_to_airbyte_registry( 1586 connector_name: Annotated[ 1587 str, 1588 Field( 1589 description="The connector name to publish (e.g., 'source-github', 'destination-postgres')" 1590 ), 1591 ], 1592 pr_number: Annotated[ 1593 int, 1594 Field(description="The pull request number containing the connector changes"), 1595 ], 1596 repo: Annotated[ 1597 ConnectorRepo, 1598 Field( 1599 default=ConnectorRepo.AIRBYTE, 1600 description="Repository where the connector PR is located. " 1601 "Use 'airbyte' for OSS connectors (default) or 'airbyte-enterprise' for enterprise connectors.", 1602 ), 1603 ], 1604 prerelease: Annotated[ 1605 Literal[True], 1606 Field( 1607 default=True, 1608 description="Must be True. Only prerelease publishing is supported at this time.", 1609 ), 1610 ], 1611) -> PrereleaseWorkflowResult: 1612 """Publish a connector to the Airbyte registry. 1613 1614 Currently only supports pre-release publishing. This tool triggers the 1615 publish-connectors-prerelease workflow in the airbytehq/airbyte repository 1616 (for OSS connectors) or the publish_enterprise_connectors workflow in 1617 airbytehq/airbyte-enterprise (for enterprise connectors), which publishes 1618 a pre-release version of the specified connector from the PR branch. 1619 1620 Pre-release versions are tagged with the format: {version}-preview.{7-char-git-sha} 1621 These versions are available for version pinning via the scoped_configuration API. 1622 1623 Requires GITHUB_CONNECTOR_PUBLISHING_PAT or GITHUB_TOKEN environment variable 1624 with 'actions:write' permission. 1625 """ 1626 # Guard: Only prerelease publishing is supported 1627 if prerelease is not True: 1628 raise NotImplementedError( 1629 "Non-prerelease publishing is not implemented yet. Set prerelease=True." 1630 ) 1631 1632 # Guard: Check for required token 1633 token = resolve_ci_trigger_github_token(PRERELEASE_TOKEN_ENV_VARS) 1634 1635 # Determine repo-specific settings 1636 is_enterprise = repo == ConnectorRepo.AIRBYTE_ENTERPRISE 1637 target_repo_name = ENTERPRISE_REPO_NAME if is_enterprise else DEFAULT_REPO_NAME 1638 target_branch = ENTERPRISE_DEFAULT_BRANCH if is_enterprise else DEFAULT_BRANCH 1639 target_workflow = ( 1640 ENTERPRISE_PRERELEASE_WORKFLOW_FILE 1641 if is_enterprise 1642 else PRERELEASE_WORKFLOW_FILE 1643 ) 1644 1645 # Get the PR's head SHA for computing the docker image tag 1646 # Note: We no longer pass gitref to the workflow - it derives the ref from PR number 1647 head_info = get_pr_head_ref(DEFAULT_REPO_OWNER, target_repo_name, pr_number, token) 1648 1649 # Prepare workflow inputs 1650 workflow_inputs = { 1651 "repo": f"{DEFAULT_REPO_OWNER}/{target_repo_name}", 1652 "pr": str(pr_number), 1653 "connector": connector_name, 1654 } 1655 1656 # Trigger the workflow on the configured default branch. 1657 # The workflow will checkout the PR branch via inputs.gitref 1658 dispatch_result = trigger_workflow_dispatch( 1659 owner=DEFAULT_REPO_OWNER, 1660 repo=target_repo_name, 1661 workflow_file=target_workflow, 1662 ref=target_branch, 1663 inputs=workflow_inputs, 1664 token=token, 1665 find_run=True, 1666 ) 1667 # Use the specific run URL if found, otherwise fall back to the workflow URL 1668 workflow_url = dispatch_result.run_url or dispatch_result.workflow_url 1669 1670 # Try to compute docker_image and docker_image_tag from connector metadata 1671 docker_image: str | None = None 1672 docker_image_tag: str | None = None 1673 metadata = _get_connector_metadata( 1674 DEFAULT_REPO_OWNER, 1675 target_repo_name, 1676 connector_name, 1677 head_info.sha, 1678 token, 1679 ) 1680 if metadata and "data" in metadata: 1681 data = metadata["data"] 1682 docker_image = data.get("dockerRepository") 1683 base_version = data.get("dockerImageTag") 1684 if base_version: 1685 docker_image_tag = compute_prerelease_docker_image_tag( 1686 base_version, head_info.sha 1687 ) 1688 1689 repo_info = f" from {repo}" if is_enterprise else "" 1690 return PrereleaseWorkflowResult( 1691 success=True, 1692 message=f"Successfully triggered pre-release workflow for {connector_name}{repo_info} from PR #{pr_number}", 1693 workflow_url=workflow_url, 1694 connector_name=connector_name, 1695 pr_number=pr_number, 1696 docker_image=docker_image, 1697 docker_image_tag=docker_image_tag, 1698 ) 1699 1700 1701def register_connector_version_tools(app: FastMCP) -> None: 1702 """Register connector_versions tools with the FastMCP app.""" 1703 register_mcp_tools(app, mcp_module=__name__)