Skip to content

feat(mcp): wire trusted-execution gate and function-layer guards - #1082

Merged
Aaron ("AJ") Steers (aaronsteers) merged 6 commits into
mainfrom
devin/1784595371-mcp-trusted-execution
Jul 21, 2026
Merged

feat(mcp): wire trusted-execution gate and function-layer guards#1082
Aaron ("AJ") Steers (aaronsteers) merged 6 commits into
mainfrom
devin/1784595371-mcp-trusted-execution

Conversation

@aaronsteers

@aaronsteers Aaron ("AJ") Steers (aaronsteers) commented Jul 21, 2026

Copy link
Copy Markdown
Member

Summary

Wires the Airbyte MCP server into the generic secure-by-default trusted-execution gate landed in fastmcp-extensions v0.10.3 (airbytehq/fastmcp-extensions#71), and adds an independent function-layer of enforcement so trusted-machine capabilities can never be reached by an untrusted (e.g. hosted HTTP) caller — even if a future registration mistake left a tool visible.

This is PR2 of the MCP security hardening effort. It maps to remediations R1 (trusted-execution gate), R3 (server-side secret suppression / refusal), and the domain-config part of R6 for the public cloud-mcp threat model. No infrastructure (Cloud Run / Pulumi), OAuth-client, or network-egress changes are in scope here.

Two independent layers of defense

  1. Visibility layer (from PR1): registering a trusted_execution config arg lets fastmcp-extensions hide requires_client_filesystem tools when the gate is off.
  2. Function layer (new here): backend helpers call require_trusted_execution(...) themselves and hard-fail when the gate is off, independently of tool visibility.

Because the layers are independent, a mistake in either one alone cannot expose a trusted-machine capability.

What changed

  1. Generic gate mapped to the Airbyte env var. New TRUSTED_EXECUTION_CONFIG_ARG maps the generic trusted_execution config name to AIRBYTE_MCP_TRUSTED_EXECUTION (default "0"), keeping the generic library Airbyte-agnostic. Registered in server.py. It has no http_header_key — the gate widens the surface, so it must never be caller-controllable.

    TRUSTED_EXECUTION_CONFIG_ARG = MCPServerConfigArg(
        name=CONFIG_TRUSTED_EXECUTION,      # generic name from fastmcp_extensions
        env_var=MCP_TRUSTED_EXECUTION_ENV_VAR,  # "AIRBYTE_MCP_TRUSTED_EXECUTION"
        default="0",
        required=False,
    )
  2. Permanent HTTP prohibition. http_main.main() calls assert_http_trusted_execution_disabled(app) immediately before app.run(...), hard-failing startup if trusted execution was explicitly enabled on the hosted entrypoint.

  3. Reusable function-layer guard (airbyte/mcp/_guards.py):

    def is_trusted_execution_enabled() -> bool: ...          # reads env only
    def require_trusted_execution(feature: str) -> None:     # raises when off

    Raises PyAirbyteTrustedExecutionRequiredError (new, under a new PyAirbyteMCPError base) with remediation in guidance, not in the deterministic message.

  4. Guarded backend helpers. Guards were added so a direct call hard-fails when untrusted:

    • _arg_resolvers.resolve_connector_config — gates only the dangerous branches (config_file, config_secret_name, and inline secret_reference:: values). An ordinary inline config dict is still allowed untrusted, because cloud.py legitimately resolves caller-provided config that way.
    • local._get_mcp_source (connector install/execution) and the local cache / secret-inventory / destructive tools: list_cached_streams, describe_default_cache, run_sql_query, list_dotenv_secrets, list_connector_config_secrets, destination_smoke_test.
  5. Secret-manager suppression when untrusted (_config.load_secrets_to_env_vars). Dotenv and Google Secret Manager backends are no longer registered as secret sources when trusted execution is off, so config_secret_name / secret_reference:: cannot resolve server-side secrets. Ordinary environment variables are still loaded from any referenced dotenv file so safe, non-secret-manager config keeps working.

  6. Domain-config hard-fail (_tool_utils.validate_airbyte_domains, called after tool registration in server.py). Instead of silently dropping a requested domain, it hard-fails on (a) setting both AIRBYTE_MCP_DOMAINS and AIRBYTE_MCP_DOMAINS_DISABLED, and (b) naming a domain with no registered tools. Remediation names the setting to clear and says to restart.

Test plan

New tests/unit_tests/test_mcp_trusted_execution.py (31 cases), covering:

  1. env parsing / default-off for the gate;
  2. require_trusted_execution raise-when-off / pass-when-on;
  3. resolve_connector_config: inline config allowed untrusted; config_file, config_secret_name, and inline secret_reference:: rejected untrusted; config_file + secret_reference:: allowed when trusted;
  4. parametrized function-layer rejection for all guarded local helpers when untrusted;
  5. assert_http_trusted_execution_disabled raises when trusted is on, passes when off;
  6. secret-manager suppression when untrusted (env var still loaded) vs. registration when trusted;
  7. validate_airbyte_domains include+exclude conflict, unknown-domain, and valid-single-domain paths.

Local verification (uv):

uv run ruff format --check .   # 219 files already formatted
uv run ruff check .            # all checks passed
uv run pyrefly check           # 0 errors (2 preexisting warnings in cloud.py)
uv run pytest tests/unit_tests/test_mcp_trusted_execution.py   # 31 passed
uv run pytest tests/unit_tests/test_mcp_cloud.py test_mcp_tool_utils.py test_mcp_connector_registry.py  # 47 passed

Limitations / out of scope

  1. No Cloud Run / Pulumi, OAuth-client, or metadata-egress changes (separate follow-up PRs R2/R5 and infra wiring).
  2. The generic fastmcp-extensions read API is used as-is; no new raising helper was added there (per maintainer direction).
  3. The CI regression guard + CONTRIBUTING.md guidance (R8) is a separate follow-up PR.

https://app.devin.ai/sessions/1696a2c620134d61a5255f959ef4c4ef

Requested by: Aaron ("AJ") Steers (@aaronsteers)

Summary by CodeRabbit

  • New Features
    • Added a trusted-execution environment switch for MCP, covering connector config handling, secret hydration, dotenv secret processing, cache tools, and local SQL/destination checks.
    • Added startup validation for MCP domain include/exclude configuration.
  • Security
    • Enforced hard failures for untrusted contexts across local privileged MCP capabilities.
    • Blocked enabling trusted execution via HTTP-exposed startup.
  • Bug Fixes
    • Introduced MCP-specific error types for trusted-execution requirements and MCP server failures.
  • Tests
    • Added unit tests covering trusted-execution gating, connector config restrictions, HTTP enforcement, secret/env loading behavior, and domain validation.

@devin-ai-integration

Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@devin-ai-integration
devin-ai-integration Bot requested a review from Copilot July 21, 2026 01:08
@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/1784595371-mcp-trusted-execution' pyairbyte --help

# Install PyAirbyte from this branch for development:
pip install 'git+https://github.com/airbytehq/PyAirbyte.git@devin/1784595371-mcp-trusted-execution'

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.

This comment was marked as resolved.

…ation

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

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@devin-ai-integration[bot], you've reached your PR review limit, so we couldn't start this review.

Next review available in: 52 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f5657410-63e0-4595-9fb2-65ba5b00514c

📥 Commits

Reviewing files that changed from the base of the PR and between 498c85f and ffadce2.

📒 Files selected for processing (3)
  • airbyte/exceptions.py
  • airbyte/mcp/_config.py
  • tests/unit_tests/test_mcp_trusted_execution.py
📝 Walkthrough

Walkthrough

Adds an environment-controlled trusted-execution gate for MCP secret resolution and local tools, blocks trusted execution over HTTP, validates Airbyte domains at startup, and adds exceptions plus unit tests for the new behavior.

Changes

MCP trusted execution

Layer / File(s) Summary
Trusted execution contracts and configuration
airbyte/constants.py, airbyte/exceptions.py, airbyte/mcp/_guards.py, airbyte/mcp/_tool_utils.py, tests/unit_tests/test_mcp_trusted_execution.py
Defines the trusted-execution environment variable, error types, enablement parsing, enforcement function, and server configuration argument.
Protected configuration and secret resolution
airbyte/mcp/_arg_resolvers.py, airbyte/mcp/_config.py, tests/unit_tests/test_mcp_trusted_execution.py
Gates local config files, server-side secrets, inline secret references, and secret-manager registration based on trusted execution.
Local and HTTP enforcement
airbyte/mcp/local.py, airbyte/mcp/http_main.py, tests/unit_tests/test_mcp_trusted_execution.py
Protects local MCP operations and prevents explicitly enabled trusted execution during HTTP startup.
Domain validation and server wiring
airbyte/mcp/_tool_utils.py, airbyte/mcp/server.py, tests/unit_tests/test_mcp_trusted_execution.py
Validates configured Airbyte domains after registration and wires the trusted-execution configuration into the MCP server.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant MCPClient
  participant MCPServer
  participant TrustedExecutionGuard
  participant LocalMCPTool
  participant SecretResolver
  MCPClient->>MCPServer: Invoke MCP operation
  MCPServer->>TrustedExecutionGuard: Check trusted execution
  TrustedExecutionGuard-->>MCPServer: Allow or raise trusted-execution error
  MCPServer->>LocalMCPTool: Execute protected operation
  LocalMCPTool->>SecretResolver: Resolve protected configuration or secrets
  SecretResolver-->>LocalMCPTool: Return resolved values
  LocalMCPTool-->>MCPClient: Return operation result
Loading

Possibly related PRs

  • airbytehq/PyAirbyte#1072: Updates the same HTTP MCP startup flow that this change extends with a trusted-execution startup guard.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main MCP change: adding trusted-execution gating and function-level guards.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch devin/1784595371-mcp-trusted-execution

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-code-quality

github-code-quality Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Overview

Languages: Python

Python / code-coverage/pytest-fast

The overall coverage in commit ffadce2 in the devin/1784595371-mcp... branch is 68%. The coverage in commit d9f652f in the main branch is 65%.

Show a code coverage summary of the most impacted files.
File main d9f652f devin/1784595371-mcp... ffadce2 +/-
airbyte/mcp/_tool_utils.py 72% 84% +12%
airbyte/mcp/registry.py 53% 70% +17%
airbyte/mcp/_arg_resolvers.py 13% 44% +31%
airbyte/mcp/int...c_history_ui.py 0% 36% +36%
airbyte/mcp/int...hared_models.py 0% 81% +81%
airbyte/cloud/models.py 0% 91% +91%
airbyte/mcp/int..._registry_ui.py 0% 92% +92%
airbyte/mcp/int...nc_status_ui.py 0% 97% +97%
airbyte/mcp/_guards.py 0% 100% +100%
airbyte/mcp/int...ive/__init__.py 0% 100% +100%

Python / code-coverage/pytest-no-creds

The overall coverage in commit ffadce2 in the devin/1784595371-mcp... branch is 68%. The coverage in commit d9f652f in the main branch is 65%.

Show a code coverage summary of the most impacted files.
File main d9f652f devin/1784595371-mcp... ffadce2 +/-
airbyte/mcp/_tool_utils.py 72% 84% +12%
airbyte/mcp/registry.py 53% 70% +17%
airbyte/mcp/_arg_resolvers.py 13% 44% +31%
airbyte/mcp/int...c_history_ui.py 0% 36% +36%
airbyte/mcp/int...hared_models.py 0% 81% +81%
airbyte/cloud/models.py 0% 91% +91%
airbyte/mcp/int..._registry_ui.py 0% 92% +92%
airbyte/mcp/int...nc_status_ui.py 0% 97% +97%
airbyte/mcp/_guards.py 0% 100% +100%
airbyte/mcp/int...ive/__init__.py 0% 100% +100%

Python / code-coverage/pytest

The overall coverage in commit ffadce2 in the devin/1784595371-mcp... branch is 73%. The coverage in commit d9f652f in the main branch is 71%.

Show a code coverage summary of the most impacted files.
File main d9f652f devin/1784595371-mcp... ffadce2 +/-
airbyte/mcp/_tool_utils.py 72% 84% +12%
airbyte/mcp/registry.py 53% 70% +17%
airbyte/mcp/_arg_resolvers.py 13% 44% +31%
airbyte/mcp/int...c_history_ui.py 0% 36% +36%
airbyte/mcp/int...hared_models.py 0% 81% +81%
airbyte/mcp/int..._registry_ui.py 0% 92% +92%
airbyte/cloud/models.py 0% 93% +93%
airbyte/mcp/int...nc_status_ui.py 0% 97% +97%
airbyte/mcp/_guards.py 0% 100% +100%
airbyte/mcp/int...ive/__init__.py 0% 100% +100%

Updated July 21, 2026 03:55 UTC

Comment thread airbyte/mcp/_arg_resolvers.py Outdated
@aaronsteers
Aaron ("AJ") Steers (aaronsteers) marked this pull request as ready for review July 21, 2026 03:23
Comment thread airbyte/exceptions.py Outdated
Comment thread tests/unit_tests/test_mcp_trusted_execution.py

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (1)
airbyte/exceptions.py (1)

229-230: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Could we update the stale PyAirbyte wording?

AirbyteMCPError was renamed to remove the PyAirbyte prefix, but its docstring still says “PyAirbyte MCP server.” Updating this to “Airbyte MCP server” would keep the public exception documentation consistent, wdyt?

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@airbyte/exceptions.py` around lines 229 - 230, Update the docstring of
AirbyteMCPError to say “Airbyte MCP server” instead of “PyAirbyte MCP server,”
keeping the exception’s public documentation consistent with its current name.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@airbyte/exceptions.py`:
- Around line 229-230: Update the docstring of AirbyteMCPError to say “Airbyte
MCP server” instead of “PyAirbyte MCP server,” keeping the exception’s public
documentation consistent with its current name.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: c581438b-f104-469c-955f-6f7a35942df6

📥 Commits

Reviewing files that changed from the base of the PR and between 5410203 and 498c85f.

📒 Files selected for processing (3)
  • airbyte/exceptions.py
  • airbyte/mcp/_guards.py
  • tests/unit_tests/test_mcp_trusted_execution.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • airbyte/mcp/_guards.py
  • tests/unit_tests/test_mcp_trusted_execution.py

@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 1 potential issue.

View 1 additional finding in Devin Review.

Open in Devin Review

Comment thread airbyte/mcp/_config.py
@aaronsteers
Aaron ("AJ") Steers (aaronsteers) merged commit 349726d into main Jul 21, 2026
20 of 21 checks passed
@aaronsteers
Aaron ("AJ") Steers (aaronsteers) deleted the devin/1784595371-mcp-trusted-execution branch July 21, 2026 04:03
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