Repository navigation
fix(mcp)!: decouple external access, fix read-only mode, add X-MCP-Allow-Destructive-Tools, generic unsupported-input guard - #1360
Conversation
…policy Co-Authored-By: AJ Steers <aj@airbyte.io>
Co-Authored-By: AJ Steers <aj@airbyte.io>
|
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/1791835169-mcp-external-access-orthogonal' pyairbyte --help
# Install PyAirbyte from this branch for development:
pip install 'git+https://github.com/airbytehq/PyAirbyte.git@devin/1791835169-mcp-external-access-orthogonal'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. |
There was a problem hiding this comment.
🟢 Approval recommended
The focused implementation, documentation, and tests consistently enforce the intended policy independence.
0 open findings
What changed in this PR
Decouples external-data access controls from pipeline-change and read-only policies.
Changes:
- Resolves external-access permission independently.
- Updates guidance and documentation.
- Expands policy/filter tests for independent behavior.
| File | Description |
|---|---|
airbyte/mcp/_tool_utils.py |
Removes pipeline-policy fallback. |
airbyte/mcp/__init__.py |
Documents independent controls. |
airbyte/exceptions.py |
Updates remediation guidance. |
tests/unit_tests/test_mcp_policy_middleware.py |
Tests runtime enforcement. |
tests/unit_tests/test_mcp_tool_filters.py |
Tests tool visibility. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Code Coverage OverviewLanguages: Python Python / code-coverage/pytest-no-credsThe overall line coverage in commit 8758a36 in the Show a line coverage summary of the most impacted files.
Python / code-coverage/pytest-unitThe overall line coverage in commit 8758a36 in the Show a line coverage summary of the most covered files.
Updated |
Co-Authored-By: AJ Steers <aj@airbyte.io>
Co-Authored-By: AJ Steers <aj@airbyte.io>
…ly policies Co-Authored-By: AJ Steers <aj@airbyte.io>
Co-Authored-By: AJ Steers <aj@airbyte.io>
There was a problem hiding this comment.
🔍 Devin Review: 1 flag
Not posted on this PR by your GitHub settings — view it in Devin Review. (Configure)
Co-Authored-By: AJ Steers <aj@airbyte.io>
Co-Authored-By: AJ Steers <aj@airbyte.io>
…headers Co-Authored-By: AJ Steers <aj@airbyte.io>
…e pyab CLI Co-Authored-By: AJ Steers <aj@airbyte.io>
…x read-only sync docs Co-Authored-By: AJ Steers <aj@airbyte.io>
Co-Authored-By: AJ Steers <aj@airbyte.io>
Co-Authored-By: AJ Steers <aj@airbyte.io>
8ebbf9e
into
release-candidate/v1


Overview
👉 TL;DR: Cloud MCP tool policies now match the hosted docs:
AIRBYTE_CLOUD_MCP_READONLY_MODE=1now hides and blocks every tool that isn't read-only, includingrun_cloud_syncandcancel_cloud_sync.X-MCP-Allow-Destructive-Tools: 0/AIRBYTE_CLOUD_MCP_ALLOW_DESTRUCTIVE_TOOLS=0policy that hides and blocks destructive tools.Pairs with:
Changes
external_access_allowed()drops the fallback topipeline_changes_allowed(), and theAirbyteExternalAccessDisabledErrorguidance drops the coupling sentence.airbyte_readonly_mode_enabled().airbyte_readonly_mode_filterhides tools that lackreadOnlyHint, andPolicyGuardMiddlewareraises the newAirbyteReadOnlyModeErrorfor them. Pipeline-change denial still leaves run/cancel sync available.ALLOW_DESTRUCTIVE_TOOLS_CONFIG_ARG,destructive_tools_allowed()(same_resolve_policyenv/header precedence) andairbyte_destructive_tools_filter. The middleware blocks with the newAirbyteDestructiveToolsDisabledError.UNSUPPORTED_ENV_VARS/UNSUPPORTED_MCP_HEADERSmaps (removed name → guidance) replacecheck_if_demo_mode_guards()/check_legacy_safe_mode_header().UNSUPPORTED_ENV_VARS/check_unsupported_env_vars()live inairbyte/_util/unsupported_inputs.pyand run before everypyabCLI command (declaratively, viacli = App(..., config=[_check_unsupported_env_vars_hook]);[project.scripts]still points atairbyte.cli.pyab:cli) and at startup of both MCP servers, andcheck_unsupported_mcp_headers()runs ontools/listandtools/call. The only entries so far areAIRBYTE_CLOUD_MCP_SAFE_MODEandX-MCP-Safe-Mode, and their guidance now points toAIRBYTE_CLOUD_MCP_ALLOW_DESTRUCTIVE_TOOLS=0/X-MCP-Allow-Destructive-Tools: 0.docs/CONTRIBUTING.md: new## Removing Environment Variables and MCP Headerssection.cloudandinteractive, via_is_cloud_tool), which matches the documented "local operations are not affected" contract. Local tools such asdestination_smoke_teststay available.airbyte/mcp/__init__.pydocs cover all three settings, plus the read-only vs. pipeline-change behavior for sync run/cancel.test_mcp_policy_middleware.pyandtest_mcp_tool_filters.pycover each policy through both filters and middleware.Review Spotlight
Reviewers with limited time, please review first:
airbyte/mcp/_policy_middleware.pyon_call_tool: read-only, pipeline, external-access and destructive checks, in that orderairbyte/mcp/_tool_utils.pyairbyte_readonly_mode_filterandairbyte_destructive_tools_filterairbyte/_util/unsupported_inputs.pyandairbyte/mcp/_tool_utils.pyUNSUPPORTED_MCP_HEADERSairbyte/mcp/_tool_utils.py_is_cloud_tool/_CLOUD_MCP_MODULESRisks
AIRBYTE_CLOUD_MCP_ALLOW_PIPELINE_CHANGES=0orAIRBYTE_CLOUD_MCP_READONLY_MODE=1, or clients that send onlyX-MCP-Allow-Pipeline-Changes: 0, now expose SQL/API passthrough tools. SetX-MCP-Allow-External-Access: 0explicitly to keep blocking them.AIRBYTE_CLOUD_MCP_READONLY_MODE=1loserun_cloud_sync/cancel_cloud_sync.AIRBYTE_CLOUD_MCP_ALLOW_PIPELINE_CHANGES=0andAIRBYTE_CLOUD_MCP_READONLY_MODE=1used to also hide non-read-only local tools. They no longer do.X-MCP-No-Destructive-Tools/MCP_NO_DESTRUCTIVE_TOOLSfilter still works but isn't documented anymore.Test plan
uv run ruff check .,uv run ruff format --check .,uv run pyrefly check airbyte/mcp airbyte/exceptions.py airbyte/constants.pyuv run pytest tests/unit_tests/test_mcp_tool_utils.py tests/unit_tests/test_mcp_policy_middleware.py tests/unit_tests/test_mcp_tool_filters.py tests/unit_tests/test_mcp_http_main.py -qplustests/unit_tests/test_unsupported_inputs.py(106 passed at d84f042)uv run pytest tests/unit_tests/test_unsupported_inputs.py tests/unit_tests/test_cli.py -q(17 passed at 8758a36);AIRBYTE_CLOUD_MCP_SAFE_MODE=1 pyab benchmarkfails with the guidance,pyab --helpstill worksLink to Devin session: https://app.devin.ai/sessions/5dd86f59bf0e4c0ea97d21c1111a8f8c
Open in Devin Desktop: https://app.devin.ai/desktop/session/5dd86f59bf0e4c0ea97d21c1111a8f8c?variant=devin
Requested by: Aaron ("AJ") Steers (@aaronsteers)
Important
Auto-merge enabled.
This PR is set to merge automatically when all requirements are met.