Skip to content

feat(mcp): add async source catalog discovery and comparison (discover_cloud_source_catalog) - #1392

Open
Aaron ("AJ") Steers (aaronsteers) wants to merge 8 commits into
release-candidate/v1from
devin/1792117913-rc-stream-sync-mode-refresh-catalog
Open

Aaron ("AJ") Steers (aaronsteers) wants to merge 8 commits into
release-candidate/v1from
devin/1792117913-rc-stream-sync-mode-refresh-catalog

Conversation

@aaronsteers

@aaronsteers Aaron ("AJ") Steers (aaronsteers) commented Oct 16, 2026 •

Copy link
Copy Markdown
Member

Overview

👉 TL;DR: Agents can start a source schema discovery in Cloud without waiting for it, check on it later, and compare the resulting catalog with another catalog or with a connection's current one. Nothing is applied to the connection yet.

Specifically, this adds the MCP tool discover_cloud_source_catalog, which is backed by CloudSource.start_catalog_discover, CloudSource.get_catalog_discover_result, CloudSource.get_latest_saved_catalog, and CloudWorkspace.compare_source_catalogs.

Depends on #1397:

Replaces (with #1397):

Changes

  • New MCP tool discover_cloud_source_catalog(source_id, discover_request_id, no_refresh, source_catalog_id, compare_to_source_catalog_id, compare_to_connection_id, workspace_id), marked destructive=False, pipeline_change=False. It supports five modes:
    1. source_id: starts an async discover via /v1/commands/run/discover and returns a generated discover_request_id.
    2. source_id + no_refresh=True: returns the latest saved catalog's timestamp and stream count, read from /v1/sources/most_recent_source_actor_catalog.
    3. discover_request_id: returns the status from /v1/commands/status. Once the discover completes, it also returns the outcome, source_catalog_id, stream count, and failure reason from /v1/commands/output/discover.
    4. source_catalog_id + compare_to_source_catalog_id: returns a bounded diff summary and schema_change from /v1/catalogs/diff (persist_changes=false).
    5. source_catalog_id + compare_to_connection_id: the same diff, against the connection's current sourceCatalogId.
  • Input rules: source_id and discover_request_id are mutually exclusive. no_refresh requires source_id. source_catalog_id requires exactly one compare_to_* argument.
  • api_util wrappers: run_source_discover_command, get_command_status, get_discover_command_output, get_most_recent_source_catalog, and get_catalog_diff.
  • Result models CloudSourceCatalogDiscoverResult and CloudSourceCatalogDiffStreamSummary.
  • _otel.py: the new endpoints are added to the safe-URL list.
  • CloudConnection.import_raw_catalog() now clears the cached _connection_info, so stream_names and the other cached properties no longer go stale after a catalog import. This bug predates the PR.

Review Spotlight

Reviewers with limited time, please review first:

Risks

  • Not verified against live Cloud. The commands API and /v1/catalogs/diff behavior come from reading the platform code.
  • Mode 2 returns no source_catalog_id, because most_recent_source_actor_catalog doesn't return one.

Follow-ups

Test plan

  • AIRBYTE_MCP_TRUSTED_EXECUTION=1 uv run pytest tests/unit_tests/test_cloud_source_catalog_discovery.py tests/unit_tests/test_cloud_api_util.py tests/unit_tests/test_cloud_connection_catalog_edits.py tests/unit_tests/test_mcp_otel.py tests/unit_tests/test_mcp_arg_trace_parity.py
  • ruff check, ruff format --check, pyrefly check

Link 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)

…loud connections

Co-Authored-By: AJ Steers <aj@airbyte.io>
@devin-ai-integration

Copy link
Copy Markdown
Contributor

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

@github-actions

Copy link
Copy Markdown

👋 Greetings, Airbyte Team Member!

Here are some helpful tips and reminders for your convenience.

💡 Show Tips and Tricks

Testing This PyAirbyte Version

You 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/1792117913-rc-stream-sync-mode-refresh-catalog' pyairbyte --help

# Install PyAirbyte from this branch for development:
pip install 'git+https://github.com/airbytehq/PyAirbyte.git@devin/1792117913-rc-stream-sync-mode-refresh-catalog'

PR Slash Commands

Airbyte Maintainers can execute the following slash commands on your PR:

  • /fix-pr - Fixes most formatting and linting issues
  • /uv-lock - Updates uv.lock file
  • /test-pr - Runs tests with the updated PyAirbyte
  • /prerelease - Builds and publishes a prerelease version to PyPI
📚 Show Repo Guidance

Helpful Resources

Community Support

Questions? Join the #pyairbyte channel in our Slack workspace.

📝 Edit this welcome message.

@github-code-quality

github-code-quality Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Code coverage overview

Languages: Python

Python / code-coverage/pytest-no-creds

release-candidate/v1 d9f652f devin/1792117913-rc-... 1202ea4 +/-
65% 79% +14%
View coverage of the 10 most impacted files.
File release-candidate/v1 d9f652f devin/1792117913-rc-... 1202ea4 +/-
airbyte/_util/api_util.py 36% 68% +32%
airbyte/mcp/cloud.py 52% 85% +33%
airbyte/cloud/connectors.py 34% 81% +47%
airbyte/mcp/_datadog.py 0% 50% +50%
airbyte/mcp/_user_identity.py 0% 93% +93%
airbyte/mcp/_telemetry.py 0% 94% +94%
airbyte/_direct...ctors/models.py 0% 95% +95%
airbyte/mcp/_sso_auth.py 0% 96% +96%
airbyte/cloud/models.py 0% 97% +97%
airbyte/mcp/int...nc_status_ui.py 0% 97% +97%

Python / code-coverage/pytest

release-candidate/v1 d9f652f devin/1792117913-rc-... 1202ea4 +/-
71% 82% +11%
View coverage of the 10 most impacted files.
File release-candidate/v1 d9f652f devin/1792117913-rc-... 1202ea4 +/-
airbyte/mcp/cloud.py 52% 85% +33%
airbyte/cloud/connectors.py 51% 84% +33%
airbyte/mcp/_datadog.py 0% 50% +50%
airbyte/mcp/int..._registry_ui.py 0% 92% +92%
airbyte/mcp/_user_identity.py 0% 93% +93%
airbyte/mcp/_telemetry.py 0% 94% +94%
airbyte/_direct...ctors/models.py 0% 95% +95%
airbyte/mcp/_sso_auth.py 0% 96% +96%
airbyte/cloud/models.py 0% 97% +97%
airbyte/mcp/int...nc_status_ui.py 0% 97% +97%

Python / code-coverage/pytest-unit

release-candidate/v1 devin/1792117913-rc-... 1202ea4 +/-
— 76% —

Line coverage data for the release-candidate/v1 branch is not yet available.

View coverage of the 10 most covered files.
File release-candidate/v1 devin/1792117913-rc-... 1202ea4 +/-
airbyte/cloud/models.py — 97% —
airbyte/mcp/_sso_auth.py — 96% —
airbyte/_direct...ctors/models.py — 95% —
airbyte/cloud/client.py — 90% —
airbyte/exceptions.py — 90% —
airbyte/mcp/cloud.py — 85% —
airbyte/cloud/connectors.py — 81% —
airbyte/progress.py — 80% —
airbyte/_util/api_util.py — 68% —
airbyte/shared/...ql_processor.py — 68% —

ℹ️ Calculations represent line coverage. Results may change as calculations are updated.


Updated October 16, 2026 23:10 UTC

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Default cursor persistence, null-namespace selection, and the trace-inventory fixture need correction.

3 open findings
What changed in this PR

Adds Cloud catalog refresh and per-stream sync configuration to the Python API and MCP, porting #994 to the RC trunk.

Changes:

  • Adds catalog rediscovery and targeted stream configuration methods.
  • Exposes guarded, destructive MCP tools.
  • Adds mocked tests for validation and catalog preservation.
File Description
tests/​unit_tests/​test_cloud_connection_catalog_edits.py Tests catalog refresh, validation, and stream preservation.
airbyte/​mcp/​cloud.py Adds two guarded catalog-editing tools.
airbyte/​cloud/​connections.py Implements refresh and per-stream sync configuration.
airbyte/​_util/​api_util.py Adds the refreshed-catalog API request.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread airbyte/mcp/cloud.py Outdated
Comment thread airbyte/cloud/connections.py Outdated
Comment thread airbyte/cloud/connections.py Outdated
Co-Authored-By: AJ Steers <aj@airbyte.io>
@aaronsteers
Aaron ("AJ") Steers (aaronsteers) marked this pull request as ready for review October 16, 2026 04:45

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 3 potential issues.

2 flags not posted on this PR by your GitHub settings — view them in Devin Review. (Configure)

Devin Review

Comment thread airbyte/cloud/connections.py Outdated
Comment thread airbyte/cloud/connections.py Outdated
Comment thread airbyte/cloud/connections.py Outdated
Comment thread airbyte/mcp/cloud.py Outdated

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin, let's split this into two PRs. One for catalog refresh and one for set stream sync mode. This one can stay the refresh catalog feature, and the new one can tackle set stream sync mode. If helpful stack the new one on top of this one.

devin-ai-integration Bot and others added 3 commits October 16, 2026 19:38
Co-Authored-By: AJ Steers <aj@airbyte.io>
…_catalog_refresh

Co-Authored-By: AJ Steers <aj@airbyte.io>
…/1792117913-rc-stream-sync-mode-refresh-catalog
@devin-ai-integration devin-ai-integration Bot changed the title feat(mcp): add tools to refresh catalog and set stream sync mode on Cloud connections feat(mcp): add catalog refresh to Cloud connections (force_catalog_refresh) Oct 16, 2026
@devin-ai-integration

Copy link
Copy Markdown
Contributor

Split done. This PR now covers only catalog refresh, exposed as update_cloud_connection(force_catalog_refresh=True) (no standalone tool), plus the import_raw_catalog cache-clear fix. Set stream sync mode moved to stacked draft #1397.

Written by Devin

@aaronsteers
Aaron ("AJ") Steers (aaronsteers) added this pull request to stack #1398 October 16, 2026 20:01
Comment thread airbyte/mcp/cloud.py Outdated
@devin-ai-integration
devin-ai-integration Bot removed this pull request from stack #1398 October 16, 2026 20:38
devin-ai-integration Bot and others added 2 commits October 16, 2026 20:59
Co-Authored-By: AJ Steers <aj@airbyte.io>
Co-Authored-By: AJ Steers <aj@airbyte.io>
@devin-ai-integration devin-ai-integration Bot changed the title feat(mcp): add catalog refresh to Cloud connections (force_catalog_refresh) feat(mcp): add async source catalog discovery and comparison (discover_cloud_source_catalog) Oct 16, 2026
Co-Authored-By: AJ Steers <aj@airbyte.io>
@calebevans-ab

Caleb Evans (calebevans-ab) commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

/evals full=true

Cloud MCP evals queued for 1202ea4 (full suite). Check job output.
Evals running: https://github.com/airbytehq/cloud-mcp-evals/actions/runs/38980034242
Cloud MCP evals on 1202ea4 (full suite): 49 / 57 passed (7 failed, 1 uncertain) · summary

@calebevans-ab

Copy link
Copy Markdown
Contributor

Aaron ("AJ") Steers (@aaronsteers) sorry for the comment, just testing

Comment thread airbyte/mcp/cloud.py

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this file and how is it updated?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a snapshot of how every MCP tool argument is recorded in OpenTelemetry spans. Each entry is one of the TraceArg classes: omit, hash, fingerprint, presence, or value allowed=[...].

How it's checked: test_inventory_matches_fixture in tests/unit_tests/test_mcp_arg_trace_parity.py builds the live plan with fastmcp_extensions.otel.trace_plan(server_app) and asserts it equals this file. Adding, removing or reclassifying a tool arg fails the test until the fixture is updated, so every telemetry classification change shows up in review.

How it's updated: regenerate it, then review and commit the diff:

UPDATE_ARG_TRACE_FIXTURE=1 AIRBYTE_MCP_TRUSTED_EXECUTION=1 \
  uv run pytest tests/unit_tests/test_mcp_arg_trace_parity.py::test_inventory_matches_fixture

AIRBYTE_MCP_TRUSTED_EXECUTION=1 is required because the test refuses to write the file unless the trusted-only manifest_yaml_file args are visible.

In this PR, the only change is the 9-line discover_cloud_source_catalog entry. That entry has an inconsistency I'd fix: all five ID args (source_id, discover_request_id, source_catalog_id, compare_to_*) are fingerprint, but every existing ID arg in the fixture is hash. FINGERPRINT adds a similarity bitset, which is meant for short text and is meaningless for UUIDs. Switching them to TraceArg.HASH is a small change: the markers plus the regenerated fixture.

Written by Devin

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm concerned this is a wasteful process with no guidance and no safeguards, and only adds friction and confusion to new PRs against this repo. It has the signs of both an auto-generated file that should be ignored via gitproperties - while also showing signs of needing careful review by each reviwerer, which apparently no guidance about how to handle - and apparently not enough AGENTS.md guidance that your suggestions here were found by you earlier not by any of the bot reviewers.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Here's what I found:

  1. It came in with feat(mcp): adopt fastmcp-extensions tool-call tracing with built-in Otel handling (fastmcp-extensions#143) #1339 (adopting fastmcp-extensions tool-call tracing) and has changed in 6 PRs since. No AGENTS.md, CONTRIBUTING or docs file mentions it, and .gitattributes doesn't mark it as generated.
  2. The only safeguard is the exact-match snapshot. Nothing checks that a classification is right. With no marker, a plain str arg defaults to hash, so the fingerprint IDs in this PR come from the explicit TraceArg.FINGERPRINT markers I copied from sibling text args. The test passed because I regenerated the fixture, which is exactly the gap you're describing.

Proposal, as a separate small PR on release-candidate/v1 (it's repo-wide, not specific to this PR):

  1. Rule test instead of eyeball review: assert that every *_id arg classifies as hash or omit, and that fingerprint appears only on free-text or name args. That catches this PR's mistake automatically.
  2. Collapse the snapshot: mark tests/unit_tests/fixtures/arg_trace_plan.json as linguist-generated=true. Once the rule test guards the meaning, reviewers don't need to read it.
  3. Two lines in AGENTS.md: the regen command, plus "IDs: no marker (defaults to hash); free text: FINGERPRINT; workspace: OMIT".

Alternative: delete the snapshot entirely and keep only the rule test plus the existing record tests. That's less friction, but we'd lose the full inventory diff when an arg gets reclassified.

Separately, in this PR I'd drop the FINGERPRINT markers from the five ID args so they default to hash. No changes until you choose.

Written by Devin

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we probably need a DRY artifact per input arg name or with arg name pattern-based rules. Not in this PR but if the managed artifact is a data classification rules file that only updates when a new arg name shows up which doesn't match rules.

Then the json file is purely a generated build artifact which can be gitignored. And then the CI failure is to modify the rules file not the json.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense. That's a better shape than my proposal. Here's how I'd sketch it for the follow-up:

  1. Managed artifact: a small rules file (for example airbyte/mcp/arg_trace_rules.yaml) of arg-name patterns mapped to a class, such as *_id: hash, workspace_id: omit, *_name/prompt/name_contains: fingerprint, with exact names overriding patterns.
  2. Generated artifact: arg_trace_plan.json gets gitignored and built from the live plan on demand.
  3. CI failure: an arg whose name matches no rule, or whose live class contradicts its rule, fails with "add or adjust a rule in arg_trace_rules.yaml". It no longer says "regenerate the JSON".

One design choice to settle: today the class comes from the TraceArg markers in the tool signatures. fastmcp-extensions only exposes a single global arg_default, with no name-based rules hook. So either:

  1. Rules validate the markers: this lives entirely in PyAirbyte. It's still two places to touch when a new arg pattern appears, but CI points you to both.
  2. Rules drive classification: fully DRY, because the markers go away except for real exceptions. This needs a small fastmcp-extensions change: an arg_rules hook next to arg_default.

I'd lean toward 2 for DRY, landing 1 first if the upstream change is slow. Not touching this in #1392.

Written by Devin

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Option 2. New PR in parallel.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On it: option 2, in a separate parallel PR on release-candidate/v1, plus the upstream arg_rules hook in fastmcp-extensions. I'll link both here once they're open.

Written by Devin

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both are open as drafts:

  1. feat(otel): add name-pattern arg_rules for argument tracing fastmcp-extensions#151 adds arg_rules and trace_plan(include_sources=True).
  2. refactor(mcp): classify traced tool args from a rules file #1412 adds airbyte/mcp/arg_trace_rules.yaml and removes all TraceArg markers. It gitignores the JSON, and CI fails with "add a rule" for unclassified args.

After #1412 lands, this PR drops its markers and its IDs pick up *_id: hash.

Written by Devin

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants