Skip to content

feat(mcp): accept separate Client-Id/Client-Secret credential headers - #1086

Open
Aaron ("AJ") Steers (aaronsteers) wants to merge 2 commits into
devin/1784611568-cloud-mcp-airbyte-branded-authfrom
devin/1784872732-pyairbyte-fastmcp-ext-0.15
Open

feat(mcp): accept separate Client-Id/Client-Secret credential headers#1086
Aaron ("AJ") Steers (aaronsteers) wants to merge 2 commits into
devin/1784611568-cloud-mcp-airbyte-branded-authfrom
devin/1784872732-pyairbyte-fastmcp-ext-0.15

Conversation

@aaronsteers

Copy link
Copy Markdown
Member

This PR targets the following PR:


Summary

Bumps fastmcp-extensions 0.14.0 -> 0.15.0 so the opt-in client-credentials transport auth accepts a second credential form. Previously a headless agent could only present static client_id/client_secret as HTTP Basic (Authorization: Basic base64(id:secret)); 0.15.0's wrap_client_credentials middleware now also accepts them as two flat headers, for clients that can't Base64-encode the pair:

Client-Id: <client_id>
Client-Secret: <client_secret>

The exchange (post to the Airbyte token endpoint -> short-lived Bearer -> verify via existing JWTVerifier) is unchanged, and the new headers are redacted from logs + stripped before forwarding downstream — all inside the library. This consumer inherits the behavior purely by the version bump; airbyte/mcp/_client_credentials.py still owns only the Airbyte-specific policy (opt-in AIRBYTE_MCP_AUTH_ALLOW_CLIENT_CREDENTIALS flag + token endpoint) and passes plain values to wrap_client_credentials.

No behavior change when AIRBYTE_MCP_AUTH_ALLOW_CLIENT_CREDENTIALS is unset (the default): the wrapper returns the app unchanged.

Changes

  • pyproject.toml / uv.lock: floor fastmcp-extensions>=0.15.0,<1.0.0, relock (0.14.0 -> 0.15.0).
  • Docs/docstrings (airbyte/mcp/__init__.py, airbyte/mcp/_client_credentials.py): document the Client-Id/Client-Secret header form alongside Basic; drop "HTTP Basic"-only phrasing.

Test

  • uv run pytest tests/unit_tests/test_mcp_client_credentials.py tests/unit_tests/test_mcp_server_auth.py -q — 31 passed.
  • uv run ruff check airbyte/mcp/ + ruff format --check — clean.

Link to Devin session: https://app.devin.ai/sessions/0b0d7690d5354d5c95e12ccf1b2e620e
Requested by: Aaron ("AJ") Steers (@aaronsteers)

…p-extensions 0.15.0

Co-Authored-By: AJ Steers <aj@airbyte.io>
@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 marked this pull request as ready for review July 24, 2026 06:01
@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/1784872732-pyairbyte-fastmcp-ext-0.15' pyairbyte --help

# Install PyAirbyte from this branch for development:
pip install 'git+https://github.com/airbytehq/PyAirbyte.git@devin/1784872732-pyairbyte-fastmcp-ext-0.15'

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.

@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: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Open in Devin Review

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.

Pull request overview

Updates the MCP server’s optional client-credentials transport auth by bumping fastmcp-extensions to a version that accepts client_id/client_secret via either HTTP Basic or separate Client-Id / Client-Secret headers, and aligns local documentation accordingly.

Changes:

  • Bump fastmcp-extensions dependency floor to >=0.15.0,<1.0.0 and refresh the lockfile.
  • Update MCP module docs/docstrings to document the new Client-Id / Client-Secret header form alongside HTTP Basic.

Reviewed changes

Copilot reviewed 3 out of 4 changed files in this pull request and generated 1 comment.

File Description
uv.lock Locks fastmcp-extensions to 0.15.0 and updates artifact metadata.
pyproject.toml Raises the minimum supported fastmcp-extensions version and documents the new header-based credential option.
airbyte/mcp/_client_credentials.py Updates module and function docstrings to reflect that credentials can be provided via Basic or separate headers.
airbyte/mcp/init.py Expands public documentation to include the Client-Id / Client-Secret header option for opt-in static client credentials.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread airbyte/mcp/__init__.py
Comment on lines +201 to +208
### Opt-in: static client credentials

The headless bearer path assumes the client can re-mint a token every ~15 min.
An agent that can only set a **static** `Authorization` value (no refresh hook)
can't do that. For this case the server can optionally accept the long-lived
`client_id` / `client_secret` directly on the inbound request, via standard HTTP
Basic (the same credential encoding OAuth's `client_secret_basic` uses):
`client_id` / `client_secret` directly on the inbound request, in either form —
standard HTTP Basic (the same credential encoding OAuth's `client_secret_basic`
uses), or separate flat headers for clients that can't Base64-encode the pair:

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.

☑️ Resolved in e91e7d9. Updated the AIRBYTE_MCP_AUTH_ALLOW_CLIENT_CREDENTIALS bullet to say it "enables the inbound static client-credentials path described above (Basic or the Client-Id/Client-Secret headers)" so it matches the expanded behavior.


Devin session

@github-code-quality

github-code-quality Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Overview

Languages: Python

Python / code-coverage/pytest-fast

The overall coverage in commit e91e7d9 in the devin/1784872732-pya... branch is 68%. The coverage in commit d9f652f in the devin/1784611568-clo... branch is 65%.

Show a code coverage summary of the most impacted files.
File devin/1784611568-clo... d9f652f devin/1784872732-pya... e91e7d9 +/-
airbyte/mcp/_tool_utils.py 72% 84% +12%
airbyte/mcp/server.py 69% 86% +17%
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/_cl..._credentials.py 0% 100% +100%

Python / code-coverage/pytest-no-creds

The overall coverage in commit e91e7d9 in the devin/1784872732-pya... branch is 68%. The coverage in commit d9f652f in the devin/1784611568-clo... branch is 65%.

Show a code coverage summary of the most impacted files.
File devin/1784611568-clo... d9f652f devin/1784872732-pya... e91e7d9 +/-
airbyte/mcp/_tool_utils.py 72% 84% +12%
airbyte/mcp/server.py 69% 86% +17%
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/_cl..._credentials.py 0% 100% +100%

Python / code-coverage/pytest

The overall coverage in commit e91e7d9 in the devin/1784872732-pya... branch is 73%. The coverage in commit d9f652f in the devin/1784611568-clo... branch is 71%.

Show a code coverage summary of the most impacted files.
File devin/1784611568-clo... d9f652f devin/1784872732-pya... e91e7d9 +/-
airbyte/mcp/_tool_utils.py 72% 84% +12%
airbyte/mcp/server.py 69% 86% +17%
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/_cl..._credentials.py 0% 100% +100%

Updated July 24, 2026 06:33 UTC

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