Repository navigation
feat(mcp): add batch stream config overrides and update_cloud_connection_unsafe - #1397
Aaron ("AJ") Steers (aaronsteers) wants to merge 17 commits into
Conversation
…loud connections Co-Authored-By: AJ Steers <aj@airbyte.io>
Co-Authored-By: AJ Steers <aj@airbyte.io>
Co-Authored-By: AJ Steers <aj@airbyte.io>
Co-Authored-By: AJ Steers <aj@airbyte.io>
…_catalog_refresh Co-Authored-By: AJ Steers <aj@airbyte.io>
…nto devin/1792179543-rc-stream-sync-mode Co-Authored-By: AJ Steers <aj@airbyte.io> # Conflicts: # airbyte/mcp/cloud.py
…/1792117913-rc-stream-sync-mode-refresh-catalog
…nto devin/1792179543-rc-stream-sync-mode
|
I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".
|
👋 Greetings, Airbyte Team Member!Here are some helpful tips and reminders for your convenience. 💡 Show Tips and TricksTesting This PyAirbyte VersionYou can test this version of PyAirbyte using the following: # Run PyAirbyte CLI from this branch:
uvx --from 'git+https://github.com/airbytehq/PyAirbyte.git@devin/1792179543-rc-stream-sync-mode' pyairbyte --help
# Install PyAirbyte from this branch for development:
pip install 'git+https://github.com/airbytehq/PyAirbyte.git@devin/1792179543-rc-stream-sync-mode'PR Slash CommandsAirbyte Maintainers can execute the following slash commands on your PR:
📚 Show Repo GuidanceHelpful ResourcesCommunity SupportQuestions? Join the #pyairbyte channel in our Slack workspace. |
Code coverage overviewLanguages: Python Python / code-coverage/pytest-no-creds
View coverage of the 10 most impacted files.
Python / code-coverage/pytest
View coverage of the 10 most impacted files.
Python / code-coverage/pytest-unit
Line coverage data for the release-candidate/v1 branch is not yet available. View coverage of the 10 most covered files.
ℹ️ Calculations represent line coverage. Results may change as calculations are updated. Updated |
Co-Authored-By: AJ Steers <aj@airbyte.io>
Co-Authored-By: AJ Steers <aj@airbyte.io>
update_cloud_connection_unsafe
There was a problem hiding this comment.
Devin Review found 4 potential issues.
1 flag not posted on this PR by your GitHub settings — view it in Devin Review. (Configure)
There was a problem hiding this comment.
🟡 Changes recommended
Safety decisions can become stale before catalog application, allowing unsafe writes through the safe tool.
3 open issues
What changed in this PR
Adds batch stream configuration updates to Cloud connections, with separate safe and unsafe MCP tools.
Changes:
- Adds override models, batch validation, and saved-state safety checks.
- Introduces unsafe updates with reset controls and reporting.
- Adds tests, tracing metadata, and catalog-cache invalidation.
| File | Description |
|---|---|
| tests/unit_tests/test_mcp_tool_errors.py | Tests unsafe-change error handling. |
| tests/unit_tests/test_mcp_connection_schedules.py | Tests tool annotations, resets, and guidance. |
| tests/unit_tests/test_cloud_connection_catalog_edits.py | Tests batch validation and catalog updates. |
| tests/unit_tests/fixtures/arg_trace_plan.json | Adds override and unsafe-tool tracing expectations. |
| airbyte/mcp/cloud.py | Exposes safe and unsafe batch updates. |
| airbyte/mcp/_error_handling.py | Registers unsafe-change errors. |
| airbyte/exceptions.py | Defines the unsafe-change exception. |
| airbyte/cloud/models.py | Defines override and result models. |
| airbyte/cloud/connections.py | Implements planning, safety checks, and application. |
| airbyte/cloud/__init__.py | Exports stream configuration models. |
| airbyte/_util/api_util.py | Adds catalog reset control. |
Review effort: Balanced
…ection update tool Co-Authored-By: AJ Steers <aj@airbyte.io>
Co-Authored-By: AJ Steers <aj@airbyte.io>
…/1792179543-rc-stream-sync-mode Co-Authored-By: AJ Steers <aj@airbyte.io> # Conflicts: # airbyte/cloud/connections.py
Co-Authored-By: AJ Steers <aj@airbyte.io>
Co-Authored-By: AJ Steers <aj@airbyte.io>
| default=None, | ||
| ), | ||
| TraceArg.FINGERPRINT, | ||
| ] = None, |
There was a problem hiding this comment.
Defaults should be defined in Field(), not as python param defaults.
There was a problem hiding this comment.
Done in 0552371. I removed the Python-level = None from all four params this PR added: override_stream_configs and workspace_id on both update_cloud_connection and update_cloud_connection_unsafe. The default now lives only in Field(default=None). One direct-call test needed override_stream_configs=None passed explicitly.
| ), | ||
| TraceArg.OMIT, | ||
| ], | ||
| ] = None, |
There was a problem hiding this comment.
Ditto.
There was a problem hiding this comment.
Done in 0552371. workspace_id on both tools is back to the Field(default=None) default only.
Co-Authored-By: AJ Steers <aj@airbyte.io>
Co-Authored-By: AJ Steers <aj@airbyte.io> # Conflicts: # airbyte/mcp/cloud.py


Overview
👉 TL;DR: Agents can change how several streams on a Cloud connection sync, all in one call. The default tool refuses changes that would invalidate a stream's saved progress, and a separate unsafe tool allows them.
Specifically, this adds
override_stream_configs: list[StreamConfigOverride]toupdate_cloud_connection(nowdestructive=False), the newupdate_cloud_connection_unsafe(destructive=True), andCloudConnection.update_stream_configs.Merges before (pairs with) #1392:
discover_cloud_source_catalog) #1392Replaces (with #1392):
Changes
StreamConfigOverride:stream_name,stream_namespace(None= any,""= unnamespaced),sync_mode,destination_sync_mode,cursor_field(top-levelstr). At least one setting is required.CloudConnection.update_stream_configs(overrides, *, allow_unsafe=False): the only public entry point. In one call it reads the catalog and state, evaluates every override on a typedConfiguredAirbyteCatalog(private_evaluate_stream_config_updates; API-only fields likeselected/mappersround-trip unchanged), and writes the catalog once. Helpers live in privateairbyte/cloud/_stream_config_overrides.py. No method takes a precomputed plan. ReturnsStreamConfigUpdateResult;reset_streamsis only filled when a reset was requested.AirbyteLibInputError): missing stream (the available streams are listed once per call), ambiguous name, unsupported sync mode, incremental with no cursor, and*_dedupwith no primary key. Any invalid override rejects the whole batch._find_unsafe_stream_changesflags changes tosyncMode,destinationSyncMode,cursorField, orprimaryKeyon streams with saved state, and the safe path raisesAirbyteUnsafeStreamConfigChangeError(naming each stream and changed field) without writing anything.update_cloud_connection: gainsoverride_stream_configs, drops todestructive=False, always sendsskipReset=true, and writes stream overrides beforeenabled/schedule.update_cloud_connection_unsafe: same inputs; sendsskipReset=falsewhen unsafe streams exist and lists every stream the platform will refresh or reset. The MCP layer only adds error guidance; arg validation and schedule setting live in privateairbyte/cloud/_connection_settings.py.api_util.replace_connection_catalogandimport_raw_catalogtakeskip_reset(defaultTrue).import_raw_catalogclears the cached_connection_info(shared with feat(mcp): add async source catalog discovery and comparison (discover_cloud_source_catalog) #1392).Review Spotlight
Reviewers with limited time, please review first:
_stream_config_overrides.py:_find_unsafe_stream_changes, the saved-state rule_stream_config_overrides.py:_apply_stream_override, the per-override validationmcp/cloud.py:update_cloud_connection_unsafeand the reset reportingRisks
update_cloud_connectionchanges fromdestructive=Truetodestructive=False, so clients that filter by that flag will now see it.web_backend/connections/updateacceptsselectedFields: [](reported in feat(mcp): Add tools to modify stream sync settings and refresh catalog on existing connections #994).Test plan
AIRBYTE_MCP_TRUSTED_EXECUTION=1 uv run pytest tests/unit_tests/test_cloud_connection_catalog_edits.py tests/unit_tests/test_mcp_connection_schedules.py tests/unit_tests/test_mcp_tool_errors.py tests/unit_tests/test_mcp_arg_trace_parity.pyruff check,ruff format --checkLink to Devin session: https://app.devin.ai/sessions/4d1ece6885c44c52a07034e025db11e6
Open in Devin Desktop: https://app.devin.ai/desktop/session/4d1ece6885c44c52a07034e025db11e6?variant=devin
Requested by: Aaron ("AJ") Steers (@aaronsteers)