Skip to content

feat(mcp): deploy sources from custom YAML definitions via deploy_connector_to_cloud - #1387

Merged
Aaron ("AJ") Steers (aaronsteers) merged 7 commits into
release-candidate/v1from
devin/1792087062-deploy-custom-yaml-source
Oct 16, 2026
Merged

Aaron ("AJ") Steers (aaronsteers) merged 7 commits into
release-candidate/v1from
devin/1792087062-deploy-custom-yaml-source

Conversation

@aaronsteers

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

Copy link
Copy Markdown
Member

Overview

👉 TL;DR: Lets agents create an Airbyte Cloud source from a custom connector that was built with YAML, not only from connectors in the public catalog. Before this, a person had to create the source in the UI.

Specifically, this adds custom_definition_id to the deploy_connector_to_cloud MCP tool (with connector_name now optional), and CloudWorkspace.deploy_source now accepts a config dict plus definition_id.

Changes

  • deploy_connector_to_cloud: new custom_definition_id arg. Pass exactly one of connector_name or custom_definition_id. A custom ID is always a source (connector_type=destination is rejected). Works with and without defer_credentials.
  • Custom path, non-deferred: fetches the definition, resolves the config against the manifest's spec.connection_specification (with the same hardcoded-secret rejection as the registry path), validates it and deploys. The MCP layer does no secret hydration. Core does it, the same as on the registry path.
  • Custom path, deferred: skips the registry lookup (that lookup raised the AirbyteConnectorNotRegisteredError that external orgs hit) and passes the custom ID to the draft create.
  • CloudWorkspace.deploy_source: accepts a dict config when definition_id is given. It resolves secret_reference:: values (hydrate_secrets, the counterpart of Source._hydrated_config) without changing the caller's dict, and sends no sourceType. A dict without definition_id still raises.
  • CloudWorkspace._deploy_deferred: when the registry has no spec for a source definition, it falls back to the custom definition's manifest spec, so airbyte_secret fields are still detected. This is best effort: if the lookup fails, it uses the global secret mask.
  • CustomCloudSourceDefinition: new config_spec_jsonschema property (the manifest's spec.connection_specification, or None) and a validate_config() method that hydrates before validating, like ConnectorBase.validate_config.
  • Tests: unit tests for each path, plus a credentialed integration test that publishes a definition, deploys a source from it and cleans up.

Review Spotlight

Reviewers with limited time, please review first:

Evidence

Live probe against the CI workspace on release-candidate/v1, run before any code change. Each resource was created and then deleted.

Call Result
Public API sources.create_source with custom definitionId, no sourceType Created. The stored config was correct: Config API sources/get showed the given keys, with secrets masked. sourceDefinitionId matched the custom definition.
Config API /sources/create with createAsDraft + custom sourceDefinitionId Draft created

The new integration test test_deploy_source_from_custom_yaml_definition passes in CI (Pytest (Integration, cloud)) against the same workspace.

Risks

  • Not breaking: connector_name becomes optional, but existing callers that pass it see no change.
  • Speakeasy quirk: for custom definitions, the SDK decodes the create response's configuration as SourceAirtable(...). PyAirbyte only reads source_id from that response, so nothing here depends on it.

Follow-ups

  • The shared hydrate_secrets doesn't resolve bare secret_reference:: strings that sit directly inside lists. This affects the registry and custom paths alike and is out of scope here.

Link to Devin session: https://app.devin.ai/sessions/ec525534f583457aa0fee362e8f42d34
Open in Devin Desktop: https://app.devin.ai/desktop/session/ec525534f583457aa0fee362e8f42d34?variant=devin
Requested by: Aaron ("AJ") Steers (@aaronsteers)

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/1792087062-deploy-custom-yaml-source' pyairbyte --help

# Install PyAirbyte from this branch for development:
pip install 'git+https://github.com/airbytehq/PyAirbyte.git@devin/1792087062-deploy-custom-yaml-source'

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 15, 2026 •

Copy link
Copy Markdown
Contributor

Code coverage overview

Languages: Python

Python / code-coverage/pytest-no-creds

release-candidate/v1 d9f652f devin/1792087062-dep... eaa850c +/-
65% 79% +14%
View coverage of the 10 most impacted files.
File release-candidate/v1 d9f652f devin/1792087062-dep... eaa850c +/-
airbyte/_util/api_util.py 36% 68% +32%
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 devin/1792087062-dep... eaa850c +/-
71% 82% +11%
View coverage of the 10 most impacted files.
File release-candidate/v1 d9f652f devin/1792087062-dep... eaa850c +/-
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 devin/1792087062-dep... eaa850c +/-
— 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/1792087062-dep... eaa850c +/-
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 — 89% —
airbyte/mcp/cloud.py — 85% —
airbyte/progress.py — 80% —
airbyte/cloud/connectors.py — 79% —
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 01:43 UTC

This comment was marked as resolved.

…ffort custom spec fallback

Co-Authored-By: AJ Steers <aj@airbyte.io>
@aaronsteers
Aaron ("AJ") Steers (aaronsteers) marked this pull request as ready for review October 15, 2026 19:14

@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 2 potential issues.

1 flag not posted on this PR by your GitHub settings — view it in Devin Review. (Configure)

Devin Review

Comment thread airbyte/mcp/cloud.py Outdated
Comment thread airbyte/mcp/cloud.py Outdated
Comment thread airbyte/cloud/connectors.py Outdated
devin-ai-integration Bot and others added 3 commits October 16, 2026 00:11
…/1792087062-deploy-custom-yaml-source

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

# Conflicts:
#	airbyte/cloud/connectors.py
Co-Authored-By: AJ Steers <aj@airbyte.io>
Comment thread airbyte/mcp/cloud.py Outdated
Co-Authored-By: AJ Steers <aj@airbyte.io>

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, please confirm secret hydration shouldn't/doesn't happen in hosted MCP mode.

@devin-ai-integration

Copy link
Copy Markdown
Contributor

Aaron ("AJ") Steers (@aaronsteers) Confirmed: in hosted (HTTP) mode, no secret hydration can happen on any deploy_connector_to_cloud path, custom or registry. The core hydrate_secrets call in deploy_source / validate_config only ever sees a config with no references in it, so it does nothing. The guards that ensure this, all at eaa850ca:

  1. Hosted can't be trusted. airbyte/mcp/http_main.py:346 calls assert_http_trusted_execution_disabled(app), so the HTTP server refuses to start if AIRBYTE_MCP_TRUSTED_EXECUTION is enabled. Trusted execution defaults to off (airbyte/mcp/_guards.py).
  2. References are rejected before core sees them. resolve_connector_config (airbyte/mcp/_arg_resolvers.py) hard-fails with AirbyteTrustedExecutionRequiredError when untrusted if either of these is present:
    • any secret_reference:: string, at any depth. _contains_secret_reference recurses into dicts and lists, so the bare-list strings that hydrate_secrets misses are still rejected here.
    • config_secret_name.
  3. No server-side secret sources are registered. When untrusted, load_secrets_to_env_vars (airbyte/mcp/_config.py) doesn't register the dotenv or GSM secret managers.
  4. Deferred path: _deferred_credentials_config (airbyte/cloud/workspaces.py) rejects secret_reference:: values outright, whatever the trust setting.

Tests: test_resolve_connector_config_rejects_when_untrusted (the secret_reference case), test_assert_http_trusted_execution_disabled and test_load_secrets_registers_secret_managers_only_when_trusted in tests/unit_tests/test_mcp_trusted_execution.py.

Hydration therefore only happens over stdio with trusted execution explicitly turned on, or when someone calls the Python library directly. Neither of those is hosted.

Written by Devin

@aaronsteers
Aaron ("AJ") Steers (aaronsteers) merged commit 5a6b8c2 into release-candidate/v1 Oct 16, 2026
29 checks passed
@aaronsteers
Aaron ("AJ") Steers (aaronsteers) deleted the devin/1792087062-deploy-custom-yaml-source branch October 16, 2026 01:51
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