Skip to content

feat(mcp): add a Fusion search backfill tool and correct the search tool descriptions - #1396

Merged
Johnny Schmidt (johnny-schmidt) merged 2 commits into
release-candidate/v1from
johnny/mcp-fusion-backfill-tool
Oct 16, 2026
Merged

Johnny Schmidt (johnny-schmidt) merged 2 commits into
release-candidate/v1from
johnny/mcp-fusion-backfill-tool

Conversation

@johnny-schmidt

@johnny-schmidt Johnny Schmidt (johnny-schmidt) commented Oct 16, 2026 •

Copy link
Copy Markdown

Summary

Adds request_cloud_search_index_backfill, an MCP tool that asks Airbyte Cloud to backfill a connection's search index. It also fixes the search tool descriptions that no longer match Sonar.

Turning search on for a connection indexes only data from syncs that run afterwards. Data already in the destination is not searchable until someone requests a backfill, and until now no tool could do that.

New API

  • CloudConnection.request_search_index_backfill(streams=None, backfill_start_time=None) calls POST {config_api_root}/connections/{connectionId}/backfill, which is POST /api/v1/connections/{connectionId}/backfill in Cloud. Cloud forwards it to Sonar's /api/cloud/v1/connections/{connection_id}/backfill.
    • Request body: {"streams": [...]?, "start_time": "<RFC 3339 with offset>"?}. Cloud rejects unknown fields and an empty streams list with a 422, and returns a 409 when search indexing is not enabled for the connection.
    • Response: {"streams": [{"stream_name", "outcome": "requested"|"running"|"failed", "run_id", "backfill": {...}}]}, parsed into ExternalSearchBackfillRequestResult.
    • A naive backfill_start_time is rejected before any request, because Cloud requires an offset.
  • MCP tool request_cloud_search_index_backfill(connection_id, streams?, backfill_start_time?). It is a write tool: not read-only, not idempotent, not destructive. It is closed-world, like the sync tools, because it starts index work rather than returning external data. Like the other Fusion tools it sets external_access=True. Like run_cloud_sync it sets pipeline_change=False, because it does not change the connection's configuration.

What the tool description says, and where each claim comes from (Sonar main)

  • Turning search on does not backfill. Only the admin route and the Cloud backfill route queue a backfill (FusionBackfillRequester in backfill.py).
  • Omitting streams queues one run that covers every stream selected in the connection. The response shows it as a single entry with an empty stream_name (ALL_STREAMS, FusionBackfillRequested).
  • backfill_start_time keeps only rows whose cursor field (a top-level timestamp or date) is at or after it. Streams without such a cursor are backfilled in full. Omitting it backfills the whole history (sonar#6954).
  • A stream whose run is still queued or running keeps it and comes back as running, so a retry is safe.
  • Supported destinations: Snowflake with username/password or key-pair auth, and BigQuery with a service-account key (FusionBackfillController._descriptor). Snowflake OAuth and PAT are in open sonar#6959, so the description does not mention them.
  • Progress shows in get_cloud_search_status. An empty table completes with 0 records (sonar#6944).

Description and model changes on existing tools

Change Motivated by
ExternalSearchHit.entity_data: null values are not stored; a keyword-only hit leaves out fields longer than 4096 characters or split across chunks sonar#6935
ExternalSearchHit.context and max_context_chars: the keyword context is centered on the first query term, with … where text was cut sonar#6935
execute_external_search_query: says where dropped fields went and points to the direct-connector and SQL tools for their full values sonar#6935
ExternalSearchBackfillStatus: adds failed_at and cancelled_at, lists every status (starting, running, finalizing, complete, failed, cancelled), and documents what backfill_start_time means sonar#6944, sonar#6954; Cloud started passing these through in airbyte-platform-internal#19936
get_cloud_search_status: explains when backfill is null and how it moves through its states, and points to the backfill tool sonar#6952, sonar#6944
The search-disabled guidance now mentions the backfill tool the new tool

Not changed: Cloud drops these fields today

Cloud's FusionSearchController keeps only the search-status fields listed in its OpenAPI spec. Several new Sonar fields therefore never reach PyAirbyte, and these descriptions do not claim them yet:

  • A failed backfill's error_code and error (nothing_to_backfill, destination_unsupported, system_error, ...), from sonar#6944. Cloud shows only status: failed and failed_at. Follow-up: add these fields to the platform's search-status schema, then name the codes in the status tool.
  • held_records on index entries, from sonar#6937. Same fix needed in the platform schema.
  • Search 4xx error_code and specific details, from sonar#6926. These reach PyAirbyte once airbyte-platform-internal#19962 merges.
  • The index entry's gaps, publication_held, stats_error and segments, and each stream's and source's gaps. Cloud's status schema does not list them either.

All of these depend on Cloud passing the fields through. Once it does, the status model and tool description can document them.

Follow-ups

  • Open sonar#6958 adds a needs_backfill flag to search status: per connection, plus a per-destination list. When it merges and Cloud forwards it, update the status tool description to tell agents to request a backfill when the flag is set.
  • Open sonar#6951 removes the placeholder all-streams run ("") from search status. Until it merges, status can list a stream with an empty name after an all-streams request.
  • Open sonar#6953 (stream-prefix matching for semantic indexes) changes nothing a tool description says.
  • The platform OpenAPI description of start_time still says "rows extracted at or after this time". It should be updated to the cursor-field meaning from sonar#6954.

Test plan

  • uv run pytest tests/unit_tests/test_cloud_direct_execute.py tests/unit_tests/test_mcp_cloud.py:
    • request shape: route, method, streams and start_time names, offset kept, {} when no arguments
    • response parsing, including a stream that comes back running
    • naive and invalid timestamps rejected
    • malformed responses rejected
    • no request sent without the Context layer API
    • a failed status's failed_at parsed
    • MCP argument forwarding and tool annotations
    • the trace-argument fixture (tests/unit_tests/fixtures/arg_trace_plan.json) includes the new tool
  • ruff format --check ., ruff check ., pyrefly check
  • The branch merges cleanly with fix(mcp): require source_id on destination search streams #1388 (git merge-tree).

@octavia-bot
octavia-bot Bot marked this pull request as draft October 16, 2026 19:09
@octavia-bot

octavia-bot Bot commented Oct 16, 2026

Copy link
Copy Markdown

Note

📝 PR Converted to Draft

More info...

Thank you for creating this PR. As a policy to protect our engineers' time, Airbyte requires all PRs to be created first in draft status. Your PR has been automatically converted to draft status in respect for this policy.

As soon as your PR is ready for formal review, you can proceed to convert the PR to "ready for review" status by clicking the "Ready for review" button at the bottom of the PR page.

To skip draft status in future PRs, please include [ready] in your PR title or add the skip-draft-status label when creating your PR.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-16T19:45:08.026751Z b8ffab5 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@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@johnny/mcp-fusion-backfill-tool' pyairbyte --help

# Install PyAirbyte from this branch for development:
pip install 'git+https://github.com/airbytehq/PyAirbyte.git@johnny/mcp-fusion-backfill-tool'

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 johnny/mcp-fusion-ba... 6a1196a +/-
65% 79% +14%
View coverage of the 10 most impacted files.
File release-candidate/v1 d9f652f johnny/mcp-fusion-ba... 6a1196a +/-
airbyte/_util/api_util.py 36% 67% +31%
airbyte/mcp/cloud.py 52% 85% +33%
airbyte/cloud/connectors.py 34% 79% +45%
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 johnny/mcp-fusion-ba... 6a1196a +/-
71% 82% +11%
View coverage of the 10 most impacted files.
File release-candidate/v1 d9f652f johnny/mcp-fusion-ba... 6a1196a +/-
airbyte/cloud/connectors.py 51% 83% +32%
airbyte/mcp/cloud.py 52% 85% +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 johnny/mcp-fusion-ba... 6a1196a +/-
— 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 johnny/mcp-fusion-ba... 6a1196a +/-
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/progress.py — 80% —
airbyte/cloud/connectors.py — 79% —
airbyte/shared/...ql_processor.py — 68% —
airbyte/_util/api_util.py — 67% —

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


Updated October 16, 2026 19:57 UTC

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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Approved with some comments. Nothing blocking.

@johnny-schmidt
Johnny Schmidt (johnny-schmidt) marked this pull request as ready for review October 16, 2026 19:41
@johnny-schmidt
Johnny Schmidt (johnny-schmidt) merged commit c041983 into release-candidate/v1 Oct 16, 2026
30 checks passed
@johnny-schmidt
Johnny Schmidt (johnny-schmidt) deleted the johnny/mcp-fusion-backfill-tool branch October 16, 2026 20:16
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.

2 participants